@skhema/embed 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"embed.min.js","sources":["../src/styles/design-tokens.ts","../src/utils/color.ts","../../skhema-types-library/dist/events/organization.js","../../skhema-types-library/dist/events/pod.js","../../skhema-types-library/dist/events/product.js","../../skhema-types-library/dist/events/index.js","../../skhema-types-library/dist/schema/components.js","../../skhema-types-library/dist/schema/elements.js","../../skhema-types-library/dist/schema/mapping.js","../src/utils/component-validation.ts","../src/utils/sanitization.ts","../src/utils/validation.ts","../src/render/index.ts","../../skhema-types-library/dist/schema/guidance.js","../src/utils/analytics.ts","../src/utils/hash.ts","../src/utils/seo.ts","../src/components/SkhemaComponent.ts","../src/components/SkhemaElement.ts","../src/cdn.ts"],"sourcesContent":["/**\n * Hardcoded color tokens for component-type badge colors.\n * Embeds can't rely on host page CSS vars, so we use inline values.\n * Source: skhema-web/src/app/globals.css oklch tokens converted to HSL-ish for browser compat.\n */\n\nexport const COMPONENT_COLORS = {\n diagnosis: {\n bg: 'oklch(0.65 0.06 25 / 0.15)',\n text: 'oklch(0.65 0.06 25)',\n border: 'oklch(0.65 0.06 25 / 0.3)',\n },\n method: {\n bg: 'oklch(0.6 0.06 250 / 0.15)',\n text: 'oklch(0.6 0.06 250)',\n border: 'oklch(0.6 0.06 250 / 0.3)',\n },\n initiatives: {\n bg: 'oklch(0.6 0.06 155 / 0.15)',\n text: 'oklch(0.6 0.06 155)',\n border: 'oklch(0.6 0.06 155 / 0.3)',\n },\n measures: {\n bg: 'oklch(0.6 0.06 300 / 0.15)',\n text: 'oklch(0.6 0.06 300)',\n border: 'oklch(0.6 0.06 300 / 0.3)',\n },\n support: {\n bg: 'oklch(0.65 0.06 65 / 0.15)',\n text: 'oklch(0.65 0.06 65)',\n border: 'oklch(0.65 0.06 65 / 0.3)',\n },\n} as const\n\nexport type ComponentColorKey = keyof typeof COMPONENT_COLORS\n\n/**\n * Email-safe flat hex palette for the card surface, per theme. These mirror the\n * Tailwind slate values behind the `CARD_VARS` HSL tokens, pre-resolved to hex\n * because email clients ignore `<style>` / CSS vars. The card renderer\n * (`@skhema/embed/render`) inlines these so the live browser embed and email\n * share one colour system (see the \"converge on hex\" decision).\n */\nexport const CARD_PALETTE = {\n light: {\n cardBg: '#ffffff', // hsl(0 0% 100%)\n border: '#e2e8f0', // hsl(214.3 31.8% 91.4%) — slate-200\n text: '#020817', // hsl(222.2 84% 4.9%) — slate-950\n textMuted: '#64748b', // hsl(215.4 16.3% 46.9%) — slate-500\n },\n dark: {\n cardBg: '#020817', // hsl(222.2 84% 4.9%) — slate-950\n border: '#1e293b', // hsl(217.2 32.6% 17.5%) — slate-800\n text: '#f8fafc', // hsl(210 40% 98%) — slate-50\n textMuted: '#94a3b8', // hsl(215 20.2% 65.1%) — slate-400\n },\n} as const\n\nexport type CardTheme = keyof typeof CARD_PALETTE\n\n/** Save-button brand colours (hex) — match the `--skhema-primary*` HSL tokens. */\nexport const PRIMARY_HEX = '#cd476a' // hsl(344 57% 54%)\nexport const PRIMARY_HOVER_HEX = '#b53d5e' // hsl(344 50% 47%)\nexport const PRIMARY_PRESSED_HEX = '#9d3552' // hsl(343 50% 41%)\n\n/** Card geometry / typography shared by the renderer and the browser embed. */\nexport const CARD_RADIUS = '4px' // calc(0.1rem + 2px) ≈ 3.6px\nexport const CARD_SHADOW =\n '0 1px 3px 0 rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1)'\nexport const CARD_SHADOW_LG =\n '0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)'\nexport const FONT_STACK =\n \"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif\"\nexport const MONO_STACK =\n \"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace\"\n\n/**\n * Shared base card CSS variables for light and dark modes.\n */\nexport const CARD_VARS = {\n light: {\n '--skhema-bg': 'hsl(0 0% 100%)',\n '--skhema-card': 'hsl(0 0% 100%)',\n '--skhema-border': 'hsl(214.3 31.8% 91.4%)',\n '--skhema-text': 'hsl(222.2 84% 4.9%)',\n '--skhema-text-muted': 'hsl(215.4 16.3% 46.9%)',\n '--skhema-accent': 'hsl(210 40% 96%)',\n '--skhema-surface-subtle': 'hsl(210 40% 97%)',\n },\n dark: {\n '--skhema-bg': 'hsl(222.2 84% 4.9%)',\n '--skhema-card': 'hsl(222.2 84% 4.9%)',\n '--skhema-border': 'hsl(217.2 32.6% 17.5%)',\n '--skhema-text': 'hsl(210 40% 98%)',\n '--skhema-text-muted': 'hsl(215 20.2% 65.1%)',\n '--skhema-accent': 'hsl(217.2 32.6% 17.5%)',\n '--skhema-surface-subtle': 'hsl(217.2 32.6% 12%)',\n },\n} as const\n\n/**\n * User SVG icon (inline, no external deps).\n */\nexport const USER_ICON_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2\"/><circle cx=\"12\" cy=\"7\" r=\"4\"/></svg>`\n","/**\n * DOM-free colour utilities for the email-safe card renderer.\n *\n * The card palette (`COMPONENT_COLORS`) is authored in `oklch()` for the live\n * browser embed, but email clients can't parse `oklch()` and `<style>` is\n * stripped, so every colour must be a pre-computed sRGB hex with all styles\n * inlined. This converts a single `oklch(...)` token to hex, compositing any\n * alpha over a flat background (white for light cards, the dark card surface\n * for dark cards) so the result is opaque and email-safe.\n *\n * This is the single source of the conversion that `sk comms curated` and the\n * `CuratedElements` email template previously duplicated.\n */\n\n/** Brand pink (hsl(344 57% 54%)) — fallback when a component palette is missing. */\nexport const BRAND_PINK = '#cd476a'\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const h = hex.replace('#', '')\n return [\n parseInt(h.slice(0, 2), 16),\n parseInt(h.slice(2, 4), 16),\n parseInt(h.slice(4, 6), 16),\n ]\n}\n\n/**\n * Convert a single `oklch(...)` token to an email-safe sRGB hex. When the token\n * carries an alpha (e.g. the badge `bg` @ 0.15 / `border` @ 0.3), the colour is\n * composited over `overHex` so the result is a flat opaque hex.\n *\n * @param token An `oklch(L C H)` or `oklch(L C H / A)` string.\n * @param overHex The flat background to composite alpha over (default white).\n */\nexport function oklchToHex(token: string, overHex = '#ffffff'): string {\n const m = token.match(\n /oklch\\(\\s*([\\d.]+)\\s+([\\d.]+)\\s+([\\d.]+)\\s*(?:\\/\\s*([\\d.]+)\\s*)?\\)/\n )\n if (!m) return BRAND_PINK\n const L = parseFloat(m[1])\n const C = parseFloat(m[2])\n const H = parseFloat(m[3])\n const alpha = m[4] !== undefined ? parseFloat(m[4]) : 1\n const h = (H * Math.PI) / 180\n const a = C * Math.cos(h)\n const b = C * Math.sin(h)\n const l_ = L + 0.3963377774 * a + 0.2158037573 * b\n const m_ = L - 0.1055613458 * a - 0.0638541728 * b\n const s_ = L - 0.0894841775 * a - 1.291485548 * b\n const lr = l_ ** 3\n const mr = m_ ** 3\n const sr = s_ ** 3\n const R = 4.0767416621 * lr - 3.3077115913 * mr + 0.2309699292 * sr\n const G = -1.2684380046 * lr + 2.6097574011 * mr - 0.3413193965 * sr\n const B = -0.0041960863 * lr - 0.7034186147 * mr + 1.707614701 * sr\n const gamma = (x: number): number => {\n const c = Math.max(0, Math.min(1, x))\n return c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055\n }\n let rgb = [gamma(R) * 255, gamma(G) * 255, gamma(B) * 255]\n if (alpha < 1) {\n const [br, bg, bb] = hexToRgb(overHex)\n rgb = [\n rgb[0] * alpha + br * (1 - alpha),\n rgb[1] * alpha + bg * (1 - alpha),\n rgb[2] * alpha + bb * (1 - alpha),\n ]\n }\n return (\n '#' + rgb.map((c) => Math.round(c).toString(16).padStart(2, '0')).join('')\n )\n}\n","// Organization-domain events — billing/admin audit trail + org membership.\n//\n// Persisted to `public.event` with domain='organization'. Plan/addon events are\n// trigger-sourced; gate/compliance/member events are application-sourced.\nexport const ORGANIZATION_EVENT_REGISTRY = {\n 'plan.created': {\n description: 'Organization plan row inserted (trial auto-assign or paid signup)',\n domain: 'organization',\n entityType: 'organizationPlan',\n source: 'trigger',\n webhookExposed: false,\n },\n 'plan.status_changed': {\n description: 'organizationPlan.status field transitioned',\n domain: 'organization',\n entityType: 'organizationPlan',\n source: 'trigger',\n webhookExposed: false,\n },\n 'plan.converted': {\n description: 'Organization flipped from trial/non-financial to paid (isFinancial true)',\n domain: 'organization',\n entityType: 'organizationPlan',\n source: 'trigger',\n webhookExposed: false,\n },\n 'plan.cancelled': {\n description: 'Organization plan cancelled (cancelledAt set)',\n domain: 'organization',\n entityType: 'organizationPlan',\n source: 'trigger',\n webhookExposed: false,\n },\n 'addon.added': {\n description: 'Addon purchased/activated on organization',\n domain: 'organization',\n entityType: 'organizationAddon',\n source: 'trigger',\n webhookExposed: false,\n },\n 'addon.removed': {\n description: 'Addon deactivated on organization (isActive true → false)',\n domain: 'organization',\n entityType: 'organizationAddon',\n source: 'trigger',\n webhookExposed: false,\n },\n 'gate.write_blocked': {\n description: 'Write action blocked by trial-guard/limit-guard (402 returned to client)',\n domain: 'organization',\n entityType: null,\n source: 'application',\n webhookExposed: false,\n },\n 'compliance.requirement_created': {\n description: 'Workspace compliance requirement created (admin/lead)',\n domain: 'organization',\n entityType: 'workspaceCompliance',\n source: 'application',\n webhookExposed: false,\n },\n 'compliance.requirement_updated': {\n description: 'Workspace compliance requirement updated (admin/lead)',\n domain: 'organization',\n entityType: 'workspaceCompliance',\n source: 'application',\n webhookExposed: false,\n },\n 'compliance.requirement_deleted': {\n description: 'Workspace compliance requirement deleted (admin/lead)',\n domain: 'organization',\n entityType: 'workspaceCompliance',\n source: 'application',\n webhookExposed: false,\n },\n 'compliance.member_completed': {\n description: 'Member completed a workspace compliance requirement',\n domain: 'organization',\n entityType: 'memberCompliance',\n source: 'application',\n webhookExposed: false,\n },\n 'member.added': {\n description: 'User added to the organization roster',\n domain: 'organization',\n entityType: 'member',\n // Trigger-sourced on the `member` table — robust whether the write comes\n // from Better Auth's organization plugin or an edge function.\n source: 'trigger',\n webhookExposed: true,\n },\n 'member.removed': {\n description: 'User removed from the organization roster',\n domain: 'organization',\n entityType: 'member',\n source: 'trigger',\n webhookExposed: true,\n },\n 'member.role_changed': {\n description: 'Organization member role transitioned',\n domain: 'organization',\n entityType: 'member',\n source: 'trigger',\n webhookExposed: true,\n },\n};\n","// Pod-domain events — pod activity/history (membership + workspace assignment).\n//\n// Persisted to `public.event` with domain='pod', entityType='pod',\n// entityId=<podId>. The shape-specific payload lives in `metadata` (mirrors the\n// former `podEvent.detail` jsonb). Application-sourced from `pods-manage`. NOT\n// webhook-exposed in v1.\n//\n// ASSIGNMENT-type events carry a snapshotted workspaceName (survives unassign);\n// reads MUST be filtered to workspaces the caller can see — restricted-workspace\n// names must not leak to non-members (enforced in the edge function).\nconst podEntry = (description) => ({\n description,\n domain: 'pod',\n entityType: 'pod',\n source: 'application',\n webhookExposed: false,\n});\nexport const POD_EVENT_REGISTRY = {\n 'pod.member_added': podEntry('User added to pod roster'),\n 'pod.member_removed': podEntry('User removed from pod roster'),\n 'pod.member_role_changed': podEntry('Pod member role transitioned'),\n 'pod.agent_added': podEntry('AI agent added to pod'),\n 'pod.agent_removed': podEntry('AI agent removed from pod'),\n 'pod.assignment_created': podEntry('Pod bound to a workspace'),\n 'pod.assignment_removed': podEntry('Pod unbound from a workspace'),\n 'pod.assignment_status_changed': podEntry('Pod assignment status transitioned'),\n 'pod.assignment_components_changed': podEntry('Pod assignment owned component types modified'),\n};\n","// Product-domain events — workspace/element/component/strategy/link lifecycle.\n//\n// Persisted to `public.event` with domain='product'. Application-sourced via\n// `recordEvent()` from the product edge functions. These are the v1\n// customer-exposed webhook events: identifiers/metadata only — NO strategy\n// content bodies, NO member PII (v1 redaction stance). `changedFieldKeys` lists\n// which fields changed, never their values.\nconst exposed = (description, entityType) => ({\n description,\n domain: 'product',\n entityType,\n source: 'application',\n webhookExposed: true,\n});\nexport const PRODUCT_EVENT_REGISTRY = {\n 'workspace.created': exposed('Workspace created', 'workspace'),\n 'workspace.updated': exposed('Workspace metadata updated', 'workspace'),\n 'workspace.deleted': exposed('Workspace deleted', 'workspace'),\n 'element.created': exposed('Element created', 'element'),\n 'element.updated': exposed('Element updated', 'element'),\n 'element.deleted': exposed('Element deleted', 'element'),\n 'component.created': exposed('Component created', 'component'),\n 'component.updated': exposed('Component updated', 'component'),\n 'component.deleted': exposed('Component deleted', 'component'),\n 'strategy.created': exposed('Strategy created', 'strategy'),\n 'strategy.updated': exposed('Strategy updated', 'strategy'),\n 'strategy.deleted': exposed('Strategy deleted', 'strategy'),\n 'strategy.activated': exposed('Strategy activated (ratified into effect)', 'strategy'),\n 'strategy.superseded': exposed('Strategy superseded by a newly-activated one', 'strategy'),\n 'strategy.discontinued': exposed('Strategy discontinued', 'strategy'),\n 'strategy.committed': exposed('Elements/components committed into a strategy', 'strategy'),\n 'link.created': exposed('Link created between two entities', 'link'),\n 'link.deleted': exposed('Link removed', 'link'),\n};\n","// Canonical, ecosystem-wide event taxonomy.\n//\n// One physical store (`public.event`, LIST-partitioned by `domain`) backs every\n// audit/history trail. This module is the single source of truth for event\n// shapes, the `domain.action` naming convention, and the `webhookExposed` gate.\n//\n// IMPORTANT — mirrored (intentionally duplicated) in\n// `supabase/functions/_shared/events.ts` because the private @skhema/* npm scope\n// isn't resolvable inside the Supabase Deno edge runtime at cold-boot. Any change\n// to event shapes/registries must land in BOTH files (CI diffs the two type\n// lists) plus the SQL trigger strings in the relevant migration.\nexport * from './envelope.js';\nexport * from './registry.js';\nexport * from './organization.js';\nexport * from './pod.js';\nexport * from './product.js';\nimport { ORGANIZATION_EVENT_REGISTRY, } from './organization.js';\nimport { POD_EVENT_REGISTRY, } from './pod.js';\nimport { PRODUCT_EVENT_REGISTRY, } from './product.js';\n/** Merged lookup keyed by event type. */\nexport const EVENT_REGISTRY = {\n ...ORGANIZATION_EVENT_REGISTRY,\n ...POD_EVENT_REGISTRY,\n ...PRODUCT_EVENT_REGISTRY,\n};\n/** Event types eligible for outbound customer webhook delivery. */\nexport const WEBHOOK_EXPOSED_EVENT_TYPES = Object.entries(EVENT_REGISTRY)\n .filter(([, entry]) => entry.webhookExposed)\n .map(([type]) => type);\nexport function isWebhookExposed(type) {\n return EVENT_REGISTRY[type]?.webhookExposed === true;\n}\n","export const COMPONENT_TYPES = {\n DIAGNOSIS: {\n value: 'diagnosis',\n label: 'Diagnosis',\n acronym: 'BD',\n },\n METHOD: {\n value: 'method',\n label: 'Method & Positioning',\n acronym: 'MP',\n },\n INITIATIVES: {\n value: 'initiatives',\n label: 'Portfolio of Initiatives',\n acronym: 'PI',\n },\n MEASURES: {\n value: 'measures',\n label: 'Measures',\n acronym: 'M',\n },\n SUPPORT: {\n value: 'support',\n label: 'Support Structures',\n acronym: 'SS',\n },\n};\n","export const ELEMENT_TYPES = {\n KEY_CHALLENGE: {\n value: 'key_challenge',\n label: 'Key Challenge',\n acronym: 'CHL',\n },\n SUPPORTING_FACT: {\n value: 'supporting_fact',\n label: 'Supporting Fact',\n acronym: 'FCT',\n },\n IMPACT: {\n value: 'impact',\n label: 'Impact',\n acronym: 'IMP',\n },\n GUIDING_POLICY: {\n value: 'guiding_policy',\n label: 'Guiding Policy',\n acronym: 'POL',\n },\n COMPETITOR_MOVE: {\n value: 'competitor_move',\n label: 'Competitor Move',\n acronym: 'CMV',\n },\n SCOPE: {\n value: 'scope',\n label: 'Scope',\n acronym: 'SCP',\n },\n CONSTRAINT: {\n value: 'constraint',\n label: 'Constraint',\n acronym: 'CST',\n },\n SOLUTION: {\n value: 'solution',\n label: 'Solution',\n acronym: 'SOL',\n },\n ASSUMPTION_HYPOTHESIS: {\n value: 'assumption_hypothesis',\n label: 'Assumption Hypothesis',\n acronym: 'HYP',\n },\n EXPERIMENT: {\n value: 'experiment',\n label: 'Experiment',\n acronym: 'EXP',\n },\n ACTION: {\n value: 'action',\n label: 'Action',\n acronym: 'ACT',\n },\n INVESTMENT: {\n value: 'investment',\n label: 'Investment',\n acronym: 'INV',\n },\n ESTIMATE: {\n value: 'estimate',\n label: 'Estimate',\n acronym: 'EST',\n },\n BASELINE: {\n value: 'baseline',\n label: 'Baseline',\n acronym: 'BSL',\n },\n OUTCOME: {\n value: 'outcome',\n label: 'Outcome',\n acronym: 'OCM',\n },\n PERFORMANCE_VARIABLE: {\n value: 'performance_variable',\n label: 'Performance Variable',\n acronym: 'VAR',\n },\n CAPABILITY: {\n value: 'capability',\n label: 'Capability',\n acronym: 'CAP',\n },\n SYSTEM: {\n value: 'system',\n label: 'System',\n acronym: 'SYS',\n },\n PRINCIPLE: {\n value: 'principle',\n label: 'Principle',\n acronym: 'PRN',\n },\n};\n","import { COMPONENT_TYPES } from './components.js';\nimport { ELEMENT_TYPES } from './elements.js';\nexport const SKHEMA_MAPPING = {\n componentFlow: [\n COMPONENT_TYPES.DIAGNOSIS,\n COMPONENT_TYPES.METHOD,\n COMPONENT_TYPES.INITIATIVES,\n COMPONENT_TYPES.MEASURES,\n COMPONENT_TYPES.SUPPORT,\n ],\n elementFlow: {\n [COMPONENT_TYPES.DIAGNOSIS.value]: [\n ELEMENT_TYPES.KEY_CHALLENGE,\n ELEMENT_TYPES.SUPPORTING_FACT,\n ELEMENT_TYPES.IMPACT,\n ],\n [COMPONENT_TYPES.METHOD.value]: [\n ELEMENT_TYPES.GUIDING_POLICY,\n ELEMENT_TYPES.COMPETITOR_MOVE,\n ELEMENT_TYPES.SCOPE,\n ELEMENT_TYPES.CONSTRAINT,\n ],\n [COMPONENT_TYPES.INITIATIVES.value]: [\n ELEMENT_TYPES.SOLUTION,\n ELEMENT_TYPES.ASSUMPTION_HYPOTHESIS,\n ELEMENT_TYPES.EXPERIMENT,\n ELEMENT_TYPES.ACTION,\n ELEMENT_TYPES.ESTIMATE,\n ELEMENT_TYPES.INVESTMENT,\n ],\n [COMPONENT_TYPES.MEASURES.value]: [\n ELEMENT_TYPES.BASELINE,\n ELEMENT_TYPES.OUTCOME,\n ELEMENT_TYPES.PERFORMANCE_VARIABLE,\n ],\n [COMPONENT_TYPES.SUPPORT.value]: [\n ELEMENT_TYPES.CAPABILITY,\n ELEMENT_TYPES.SYSTEM,\n ELEMENT_TYPES.PRINCIPLE,\n ],\n },\n};\n/**\n * Defines which component types each type can link to for decision context.\n * This represents the upstream dependencies in the challenge-based strategy chain.\n * Diagnosis is always the root (no upstream context).\n */\nexport const COMPONENT_CONTEXT_SOURCES = {\n [COMPONENT_TYPES.DIAGNOSIS.value]: [],\n [COMPONENT_TYPES.METHOD.value]: [COMPONENT_TYPES.DIAGNOSIS.value],\n [COMPONENT_TYPES.INITIATIVES.value]: [\n COMPONENT_TYPES.DIAGNOSIS.value,\n COMPONENT_TYPES.METHOD.value,\n ],\n [COMPONENT_TYPES.MEASURES.value]: [\n COMPONENT_TYPES.DIAGNOSIS.value,\n COMPONENT_TYPES.METHOD.value,\n COMPONENT_TYPES.INITIATIVES.value,\n ],\n [COMPONENT_TYPES.SUPPORT.value]: [\n COMPONENT_TYPES.DIAGNOSIS.value,\n COMPONENT_TYPES.METHOD.value,\n COMPONENT_TYPES.INITIATIVES.value,\n ],\n};\nexport function getContextSourcesForComponent(componentValue) {\n return COMPONENT_CONTEXT_SOURCES[componentValue] ?? [];\n}\nexport function canLinkToComponentType(sourceComponentType, targetComponentType) {\n const sources = getContextSourcesForComponent(sourceComponentType);\n return sources.includes(targetComponentType);\n}\nexport function getElementsForComponent(componentValue) {\n return SKHEMA_MAPPING.elementFlow[componentValue];\n}\nexport function isValidElementForComponent(element, componentValue) {\n const validElements = getElementsForComponent(componentValue);\n return validElements.includes(element);\n}\nexport function findComponentByValue(value) {\n return Object.values(COMPONENT_TYPES).find((component) => component.value === value);\n}\nexport function findElementByValue(value) {\n return Object.values(ELEMENT_TYPES).find((element) => element.value === value);\n}\n","import type { ComponentValue, ElementValue } from '@skhema/types'\nimport { COMPONENT_TYPES, SKHEMA_MAPPING } from '@skhema/types'\n\n/**\n * Validate that a string is a valid component type.\n */\nexport function isValidComponentType(\n componentType: string\n): componentType is ComponentValue {\n const validTypes = Object.values(COMPONENT_TYPES).map((t) => t.value)\n return validTypes.includes(componentType as ComponentValue)\n}\n\n/**\n * Validate that an element type belongs to the given component type.\n */\nexport function validateElementBelongsToComponent(\n elementType: string,\n componentType: string\n): boolean {\n if (!isValidComponentType(componentType)) return false\n\n const validElements =\n SKHEMA_MAPPING.elementFlow[\n componentType as keyof typeof SKHEMA_MAPPING.elementFlow\n ]\n if (!validElements) return false\n\n return validElements.some((e) => e.value === elementType)\n}\n\n/**\n * Get the human-readable label for a component type.\n */\nexport function getComponentTypeLabel(componentType: string): string {\n const type = Object.values(COMPONENT_TYPES).find(\n (t) => t.value === componentType\n )\n return type?.label || componentType\n}\n\n/**\n * Get the acronym for a component type (e.g., 'BD' for diagnosis).\n */\nexport function getComponentTypeAcronym(componentType: string): string {\n const type = Object.values(COMPONENT_TYPES).find(\n (t) => t.value === componentType\n )\n return type?.acronym || componentType.substring(0, 2).toUpperCase()\n}\n\n/**\n * Resolve which component type an element belongs to.\n * Returns the component value, or 'diagnosis' as fallback.\n */\nexport function resolveComponentType(elementType: string): ComponentValue {\n for (const [componentValue, elements] of Object.entries(\n SKHEMA_MAPPING.elementFlow\n )) {\n if (elements.some((e) => e.value === elementType)) {\n return componentValue as ComponentValue\n }\n }\n return 'diagnosis'\n}\n\n/**\n * Get all valid element types for a given component, ordered per the mapping.\n */\nexport function getElementTypesForComponent(\n componentType: string\n): ElementValue[] {\n if (!isValidComponentType(componentType)) return []\n const elements =\n SKHEMA_MAPPING.elementFlow[\n componentType as keyof typeof SKHEMA_MAPPING.elementFlow\n ]\n return elements?.map((e) => e.value) || []\n}\n","/**\n * Content sanitization utilities for Skhema cards.\n *\n * DOM-free by design: `sanitizeContent` is imported by the email-safe card\n * renderer (`@skhema/embed/render`), which must run in Node / email / CLI\n * runtimes with no `document`. Keep every export here free of DOM access at\n * call time (the legacy `stripHtml` below is the one exception and is not used\n * by the renderer).\n */\n\n/**\n * HTML entity encoding for basic XSS protection. Regex-based (no DOM) so it is\n * safe in non-browser runtimes. Escapes the text-context entities plus quotes.\n */\nfunction htmlEncode(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\n/**\n * Sanitizes content to prevent XSS attacks and removes URLs\n * @param content The raw content to sanitize\n * @returns Sanitized HTML-safe content with URLs removed\n */\nexport function sanitizeContent(content: string): string {\n // Strip URLs first\n let sanitized = stripUrls(content)\n\n // Encode all HTML entities to prevent script injection\n sanitized = htmlEncode(sanitized)\n\n // Preserve line breaks for readability\n sanitized = sanitized.replace(/\\n/g, '<br>')\n\n // Apply text wrapping rules for long text\n sanitized = applyTextWrapping(sanitized)\n\n return sanitized\n}\n\n/**\n * Strips all URLs from the content\n * @param text The text containing potential URLs\n * @returns Text with all URLs removed\n */\nfunction stripUrls(text: string): string {\n // Comprehensive URL patterns to remove\n const patterns = [\n // Standard URLs with protocols\n /https?:\\/\\/[^\\s<>\"{}|\\\\^`[\\]]+/gi,\n // FTP URLs\n /ftp:\\/\\/[^\\s<>\"{}|\\\\^`[\\]]+/gi,\n // URLs without protocol but with www\n /www\\.[^\\s<>\"{}|\\\\^`[\\]]+/gi,\n // Email-like patterns\n /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/gi,\n // Common domain patterns (anything.com, anything.org, etc.)\n /(?:^|\\s)([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}(?:\\/[^\\s]*)?/gi,\n ]\n\n let stripped = text\n patterns.forEach((pattern) => {\n stripped = stripped.replace(pattern, '')\n })\n\n // Clean up any multiple spaces left after URL removal\n stripped = stripped.replace(/\\s+/g, ' ').trim()\n\n return stripped\n}\n\n/**\n * Applies intelligent text wrapping to prevent layout breaking\n * @param text The text to apply wrapping rules to\n * @returns Text with appropriate wrapping hints\n */\nfunction applyTextWrapping(text: string): string {\n // Split text into words\n const words = text.split(/(\\s+)/)\n\n return words\n .map((word) => {\n // Skip if it's whitespace or already contains HTML\n if (/^\\s+$/.test(word) || word.includes('<')) {\n return word\n }\n\n // For very long words (>30 chars), add word-break opportunities\n if (word.length > 30) {\n // Insert zero-width spaces every 10 characters for breaking\n return word.replace(/(.{10})/g, '$1\\u200B')\n }\n\n return word\n })\n .join('')\n}\n\n/**\n * Validates if content contains potentially malicious patterns or URLs\n * @param content The content to validate\n * @returns Object with validation status and detected issues\n */\nexport function validateContentSecurity(content: string): {\n isSecure: boolean\n issues: string[]\n} {\n const issues: string[] = []\n\n // Check for script tags\n if (/<script[\\s>]/i.test(content)) {\n issues.push('Script tags detected')\n }\n\n // Check for event handlers\n if (/on\\w+\\s*=/i.test(content)) {\n issues.push('Event handlers detected')\n }\n\n // Check for javascript: protocol\n if (/javascript:/i.test(content)) {\n issues.push('JavaScript protocol detected')\n }\n\n // Check for data: URLs that could contain scripts\n if (/data:[^,]*script/i.test(content)) {\n issues.push('Data URL with script detected')\n }\n\n // Check for iframe tags\n if (/<iframe[\\s>]/i.test(content)) {\n issues.push('Iframe tags detected')\n }\n\n // Check for URLs (since we want to disallow them)\n if (/https?:\\/\\//i.test(content) || /www\\./i.test(content)) {\n issues.push('URLs detected in content')\n }\n\n return {\n isSecure: issues.length === 0,\n issues,\n }\n}\n\n/**\n * Strips all HTML tags from content\n * @param content The content to strip\n * @returns Plain text content\n */\nexport function stripHtml(content: string): string {\n const div = document.createElement('div')\n div.innerHTML = content\n return div.textContent || div.innerText || ''\n}\n\n/**\n * Checks if content contains any URLs\n * @param content The content to check\n * @returns True if URLs are found\n */\nexport function containsUrls(content: string): boolean {\n const urlPatterns = [\n /https?:\\/\\//i,\n /ftp:\\/\\//i,\n /www\\./i,\n /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/i,\n /(?:^|\\s)([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}(?:\\/[^\\s]*)?/i,\n ]\n\n return urlPatterns.some((pattern) => pattern.test(content))\n}\n","import type { ElementValue } from '@skhema/types'\nimport { ELEMENT_TYPES } from '@skhema/types'\n\nexport function isValidElementType(\n elementType: string\n): elementType is ElementValue {\n const validTypes = Object.values(ELEMENT_TYPES).map((type) => type.value)\n return validTypes.includes(elementType as ElementValue)\n}\n\nexport function validateAttributes(element: HTMLElement): {\n isValid: boolean\n errors: string[]\n elementType?: ElementValue\n contributorId?: string\n} {\n const errors: string[] = []\n\n const elementType = element.getAttribute('element-type')\n const contributorId = element.getAttribute('contributor-id')\n\n if (!elementType) {\n errors.push('Missing required attribute: element-type')\n } else if (!isValidElementType(elementType)) {\n const validTypes = Object.values(ELEMENT_TYPES)\n .map((t) => t.value)\n .join(', ')\n errors.push(\n `Invalid element-type \"${elementType}\". Valid types: ${validTypes}`\n )\n }\n\n if (!contributorId) {\n errors.push('Missing required attribute: contributor-id')\n } else if (contributorId.trim().length === 0) {\n errors.push('contributor-id cannot be empty')\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n elementType: isValidElementType(elementType || '')\n ? (elementType as ElementValue)\n : undefined,\n contributorId: contributorId || undefined,\n }\n}\n\nexport function getElementTypeLabel(elementType: ElementValue): string {\n const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType)\n return type?.label || elementType\n}\n\nexport function getElementTypeAcronym(elementType: ElementValue): string {\n const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType)\n return type?.acronym || elementType.substring(0, 2).toUpperCase()\n}\n","/**\n * `@skhema/embed/render` — the canonical, DOM-free source of truth for the\n * official Skhema element / component card HTML.\n *\n * `renderElementCardHtml(data)` / `renderComponentCardHtml(data)` return\n * EMAIL-SAFE HTML: a `role=\"presentation\"` table layout with every style\n * inlined as flat hex (no shadow DOM, no `<style>`, no `oklch()`, no CSS vars).\n * The same output is used three ways:\n *\n * 1. the live browser embed (`SkhemaElement` / `SkhemaComponent` inject it\n * into shadow DOM and layer hover/transition CSS on top);\n * 2. the `CuratedElements` email template (injected verbatim); and\n * 3. any third-party / contributor email generator.\n *\n * The module is pure — importing it never touches the DOM, so it is safe in\n * Node, edge, and email build runtimes. It builds NO URLs: callers pass the\n * fully-formed `saveUrl` (e.g. the `/save` handoff) so each surface controls\n * its own UTM tagging.\n */\nimport type { ElementValue } from '@skhema/types'\nimport {\n CARD_PALETTE,\n CARD_RADIUS,\n CARD_SHADOW,\n COMPONENT_COLORS,\n FONT_STACK,\n MONO_STACK,\n PRIMARY_HEX,\n USER_ICON_SVG,\n type CardTheme,\n} from '../styles/design-tokens.js'\nimport { oklchToHex } from '../utils/color.js'\nimport {\n getComponentTypeAcronym,\n getComponentTypeLabel,\n resolveComponentType,\n} from '../utils/component-validation.js'\nimport { sanitizeContent } from '../utils/sanitization.js'\nimport { getElementTypeLabel } from '../utils/validation.js'\n\n/* ------------------------------------------------------------------ *\n * Public data shapes (documented contract — see README \"render\") *\n * ------------------------------------------------------------------ */\n\n/** Card theme. Email is always `'light'`; the browser embed passes the\n * detected page theme. Defaults to `'light'` when omitted. */\nexport type { CardTheme }\n\n/** Author attribution shared by both card kinds. */\ninterface AuthorFields {\n /** Display name. When omitted, falls back to a humanised `contributorId`. */\n authorName?: string | null\n /** Public contributor slug — when present, the name links to the profile. */\n authorSlug?: string | null\n /** Contributor id, used only for the name fallback when `authorName` is unset. */\n contributorId?: string | null\n}\n\n/** Input for {@link renderElementCardHtml}. */\nexport interface ElementCardData extends AuthorFields {\n /** Skhema element type value, e.g. `\"key_challenge\"`. */\n elementType: string\n /** The element content / premise (plain text; sanitised + escaped here). */\n content: string\n /** Pre-built handoff URL for the \"Save to Skhema\" button. */\n saveUrl: string\n /** Card theme (default `'light'`). */\n theme?: CardTheme\n}\n\n/** A single element row inside a component card. */\nexport interface ComponentCardElement {\n elementType: string\n content: string\n}\n\n/** Input for {@link renderComponentCardHtml}. */\nexport interface ComponentCardData extends AuthorFields {\n /** Skhema component type value, e.g. `\"diagnosis\"`. */\n componentType: string\n /** Optional component title shown after a \"—\" separator in the header. */\n title?: string | null\n /** The component's elements, in display order. */\n elements: ComponentCardElement[]\n /** Pre-built handoff URL for the \"Save to Skhema\" button. */\n saveUrl: string\n /** Card theme (default `'light'`). */\n theme?: CardTheme\n}\n\n/* ------------------------------------------------------------------ *\n * Internal helpers *\n * ------------------------------------------------------------------ */\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction escapeAttr(text: string): string {\n return escapeHtml(text).replace(/'/g, '&#39;')\n}\n\nfunction humaniseContributorId(contributorId: string): string {\n return contributorId\n .split(/[_-]/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ')\n}\n\ninterface BadgePalette {\n badgeBg: string\n badgeText: string\n badgeBorder: string\n topBorder: string\n}\n\n/** Resolve the email-safe badge / top-border hex for a component type + theme. */\nfunction resolveBadgePalette(\n componentType: string,\n theme: CardTheme\n): BadgePalette {\n const surface = CARD_PALETTE[theme].cardBg\n const colors =\n COMPONENT_COLORS[componentType as keyof typeof COMPONENT_COLORS]\n if (!colors) {\n return {\n badgeBg: surface,\n badgeText: PRIMARY_HEX,\n badgeBorder: PRIMARY_HEX,\n topBorder: PRIMARY_HEX,\n }\n }\n const text = oklchToHex(colors.text)\n return {\n badgeBg: oklchToHex(colors.bg, surface),\n badgeText: text,\n badgeBorder: oklchToHex(colors.border, surface),\n topBorder: text,\n }\n}\n\n/** Contributor line inner HTML: person icon + \"By {author}\" (linked if slug). */\nfunction renderAuthorHtml(author: AuthorFields, mutedColor: string): string {\n let label = ''\n if (author.authorName && author.authorName.trim()) {\n const name = escapeHtml(author.authorName.trim())\n label = author.authorSlug\n ? `By <a href=\"https://skhema.com/contributors/${encodeURIComponent(\n author.authorSlug\n )}\" style=\"color:${mutedColor};text-decoration:none;\" target=\"_blank\" rel=\"noopener noreferrer\">${name}</a>`\n : `By ${name}`\n } else if (author.contributorId && author.contributorId.trim()) {\n label = `By ${escapeHtml(humaniseContributorId(author.contributorId.trim()))}`\n }\n\n if (!label) return '&nbsp;'\n\n return (\n `<span style=\"display:inline-block;width:14px;height:14px;vertical-align:middle;color:${mutedColor};\">${USER_ICON_SVG}</span>` +\n `<span style=\"vertical-align:middle;padding-left:6px;\">${label}</span>`\n )\n}\n\n/** The shared footer (contributor line + save button + attribution). */\nfunction renderFooter(\n saveUrl: string,\n author: AuthorFields,\n theme: CardTheme\n): string {\n const p = CARD_PALETTE[theme]\n return (\n `<tr><td style=\"padding:12px 16px;border-top:1px solid ${p.border};\">` +\n `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"width:100%;border-collapse:collapse;\"><tr>` +\n `<td style=\"vertical-align:middle;\"><span style=\"font-size:12px;color:${p.textMuted};\">${renderAuthorHtml(\n author,\n p.textMuted\n )}</span></td>` +\n `<td style=\"text-align:right;vertical-align:middle;white-space:nowrap;\">` +\n `<a href=\"${escapeAttr(saveUrl)}\" class=\"skhema-save-btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Save this to Skhema\" style=\"display:inline-block;background:${PRIMARY_HEX};color:#ffffff;font-size:12px;font-weight:500;text-decoration:none;padding:6px 14px;border-radius:${CARD_RADIUS};white-space:nowrap;\">Save to Skhema &rarr;</a>` +\n `</td></tr></table>` +\n `<div style=\"font-size:11px;line-height:1.4;color:${p.textMuted};margin-top:8px;\">Strategy powered by <a href=\"https://skhema.com\" style=\"color:${p.textMuted};text-decoration:underline;\" target=\"_blank\" rel=\"noopener noreferrer\">Skhema</a></div>` +\n `</td></tr>`\n )\n}\n\n/** Header row: acronym badge + type label (+ optional \"— title\" for components). */\nfunction renderHeader(\n badge: BadgePalette,\n acronym: string,\n typeLabel: string,\n labelColor: string,\n theme: CardTheme,\n title?: string | null\n): string {\n const p = CARD_PALETTE[theme]\n const titleHtml = title\n ? `<span style=\"color:${p.textMuted};\"> &mdash; </span><span style=\"font-weight:600;color:${p.text};\">${escapeHtml(\n title\n )}</span>`\n : ''\n return (\n `<tr><td style=\"padding:12px 16px;border-bottom:1px solid ${p.border};\">` +\n `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"width:100%;border-collapse:collapse;\"><tr>` +\n `<td style=\"width:1%;white-space:nowrap;padding-right:8px;vertical-align:middle;\">` +\n `<span class=\"skhema-acronym-badge\" title=\"${escapeAttr(typeLabel)}\" style=\"display:inline-block;font-family:${MONO_STACK};font-size:10px;font-weight:600;letter-spacing:0.02em;padding:2px 6px;border-radius:2px;background:${badge.badgeBg};color:${badge.badgeText};border:1px solid ${badge.badgeBorder};\">${escapeHtml(\n acronym\n )}</span></td>` +\n `<td style=\"vertical-align:middle;\"><span style=\"font-size:13px;font-weight:500;color:${labelColor};\">${escapeHtml(\n typeLabel\n )}${titleHtml}</span></td>` +\n `</tr></table></td></tr>`\n )\n}\n\n/** Open the outer card table with inline card styling for the theme. */\nfunction cardOpen(theme: CardTheme, kind: 'element' | 'component'): string {\n const p = CARD_PALETTE[theme]\n return (\n `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" class=\"skhema-card\" data-skhema-kind=\"${kind}\" data-theme=\"${theme}\" ` +\n `style=\"width:100%;max-width:600px;border-collapse:separate;background:${p.cardBg};border:1px solid ${p.border};border-radius:${CARD_RADIUS};box-shadow:${CARD_SHADOW};overflow:hidden;margin:8px 0;font-family:${FONT_STACK};line-height:1.5;color:${p.text};\">`\n )\n}\n\n/* ------------------------------------------------------------------ *\n * Public renderers *\n * ------------------------------------------------------------------ */\n\n/**\n * Render the official Skhema **element** card as email-safe HTML.\n * The badge acronym + palette resolve from the element's owning component type,\n * exactly like the live `<skhema-element>` embed.\n */\nexport function renderElementCardHtml(data: ElementCardData): string {\n const theme = data.theme ?? 'light'\n const p = CARD_PALETTE[theme]\n const label = getElementTypeLabel(data.elementType as ElementValue)\n const componentType = resolveComponentType(data.elementType)\n const acronym = getComponentTypeAcronym(componentType)\n const badge = resolveBadgePalette(componentType, theme)\n\n return (\n cardOpen(theme, 'element') +\n renderHeader(badge, acronym, label, p.text, theme) +\n `<tr><td style=\"padding:16px;\">` +\n `<div style=\"font-size:15px;line-height:1.6;color:${p.text};word-wrap:break-word;overflow-wrap:break-word;\">${sanitizeContent(\n data.content\n )}</div>` +\n `</td></tr>` +\n renderFooter(data.saveUrl, data, theme) +\n `</table>`\n )\n}\n\n/**\n * Render the official Skhema **component** card as email-safe HTML — a coloured\n * top bar, header (badge + type label + optional title), per-element-type\n * groups, and the shared footer.\n */\nexport function renderComponentCardHtml(data: ComponentCardData): string {\n const theme = data.theme ?? 'light'\n const p = CARD_PALETTE[theme]\n const label = getComponentTypeLabel(data.componentType)\n const acronym = getComponentTypeAcronym(data.componentType)\n const badge = resolveBadgePalette(data.componentType, theme)\n\n // Group elements by type (preserving first-occurrence order), mirroring the\n // live `<skhema-component>` body: one small-caps label per type, with each\n // element's content as a left-ruled block beneath it.\n const groups = new Map<string, string[]>()\n for (const el of data.elements) {\n const existing = groups.get(el.elementType)\n if (existing) existing.push(el.content)\n else groups.set(el.elementType, [el.content])\n }\n\n const groupsHtml = Array.from(groups.entries())\n .map(\n ([elementType, contents]) =>\n `<div style=\"margin-bottom:16px;\">` +\n `<div style=\"text-transform:uppercase;letter-spacing:0.05em;font-family:${MONO_STACK};font-size:10px;font-weight:600;color:${p.textMuted};margin:0 0 4px;\">${escapeHtml(\n getElementTypeLabel(elementType as ElementValue)\n )}</div>` +\n contents\n .map(\n (content) =>\n `<div style=\"font-size:14px;line-height:1.6;color:${p.text};padding-left:8px;border-left:2px solid ${p.border};word-wrap:break-word;overflow-wrap:break-word;\">${sanitizeContent(\n content\n )}</div>`\n )\n .join('') +\n `</div>`\n )\n .join('')\n\n return (\n cardOpen(theme, 'component') +\n `<tr><td style=\"height:2px;line-height:2px;font-size:0;background:${badge.topBorder};\">&nbsp;</td></tr>` +\n renderHeader(badge, acronym, label, p.textMuted, theme, data.title) +\n `<tr><td style=\"padding:16px;\">${groupsHtml}</td></tr>` +\n renderFooter(data.saveUrl, data, theme) +\n `</table>`\n )\n}\n","import { COMPONENT_TYPES } from './components.js';\nimport { ELEMENT_TYPES } from './elements.js';\nexport const COMPONENT_GUIDANCE = {\n [COMPONENT_TYPES.DIAGNOSIS.value]: {\n purpose: 'Frame the situation: name the addressable challenges worth solving and the evidence behind them.',\n goodInput: [\n 'A specific, falsifiable challenge — not a vague concern',\n 'Facts and data that make the challenge real',\n 'The impact of leaving it unaddressed',\n ],\n commonMistakes: [\n 'Jumping to solutions before the challenge is clear',\n 'Listing symptoms instead of the underlying problem',\n 'Stating challenges with no supporting evidence',\n ],\n nextAction: 'Add a Key Challenge, then back it with Supporting Facts and its Impact.',\n docsPath: 'method/diagnosis',\n },\n [COMPONENT_TYPES.METHOD.value]: {\n purpose: 'Set the overall approach: the guiding policy, scope, and constraints that shape how you respond to the diagnosis.',\n goodInput: [\n 'A guiding policy that commits to a direction',\n \"Explicit scope — what's in and what's out\",\n 'Constraints and relevant competitor moves',\n ],\n commonMistakes: [\n 'Writing goals instead of an approach',\n 'Leaving scope open-ended',\n 'Ignoring competitor positioning',\n ],\n nextAction: 'Define a Guiding Policy, then bound it with Scope and Constraints.',\n docsPath: 'method/method-positioning',\n },\n [COMPONENT_TYPES.INITIATIVES.value]: {\n purpose: 'Turn the approach into testable initiatives: solutions, the assumptions behind them, experiments to validate, and the actions and investment to execute.',\n goodInput: [\n 'Solutions that follow from the guiding policy',\n 'The assumptions each solution depends on',\n 'Experiments that would validate or kill an idea',\n ],\n commonMistakes: [\n 'Committing to initiatives without testing assumptions',\n 'Actions with no owner, estimate, or investment',\n 'Keeping initiatives that failed their experiment',\n ],\n nextAction: 'Propose a Solution, list its Assumptions, then design an Experiment to test it.',\n docsPath: 'method/initiatives',\n },\n [COMPONENT_TYPES.MEASURES.value]: {\n purpose: \"Decide how you'll know it's working: baselines, target outcomes, and the performance variables that move them.\",\n goodInput: [\n 'A current-state baseline to measure against',\n 'Clear primary and secondary outcomes',\n 'The independent variables that drive those outcomes',\n ],\n commonMistakes: [\n 'Setting targets with no baseline',\n 'Measuring activity instead of outcomes',\n \"Tracking variables you can't influence\",\n ],\n nextAction: 'Establish a Baseline, define the Outcome you want, then identify the Performance Variables.',\n docsPath: 'method/measures',\n },\n [COMPONENT_TYPES.SUPPORT.value]: {\n purpose: 'Name what must be true to sustain the strategy: capabilities, systems, and principles that underpin execution.',\n goodInput: [\n 'Capabilities the strategy requires',\n 'Systems and processes that enable execution',\n 'Principles that guide day-to-day decisions',\n ],\n commonMistakes: [\n 'Assuming capabilities exist without checking',\n 'Adding systems that no initiative needs',\n 'Principles too vague to guide a real decision',\n ],\n nextAction: 'List the Capabilities you need, the Systems that enable them, and the Principles that govern them.',\n docsPath: 'method/support-structures',\n },\n};\nexport const ELEMENT_GUIDANCE = {\n // ── Diagnosis ───────────────────────────────────────────────────────────\n [ELEMENT_TYPES.KEY_CHALLENGE.value]: {\n summary: 'State an existing, foreseeable, or hypothetical challenge, key issue, or problem.',\n goodInput: [\n 'One challenge per element, stated specifically',\n 'Phrased as a problem, not a solution',\n ],\n commonMistakes: ['Bundling several problems together', 'Writing a goal'],\n },\n [ELEMENT_TYPES.SUPPORTING_FACT.value]: {\n summary: 'Add evidence or data that supports the diagnosis.',\n goodInput: [\n 'Verifiable facts, figures, or observations',\n 'A source where possible',\n ],\n commonMistakes: ['Opinions presented as facts'],\n },\n [ELEMENT_TYPES.IMPACT.value]: {\n summary: 'Describe the business impact if the challenge is left unaddressed.',\n goodInput: ['A concrete consequence: cost, risk, or missed opportunity'],\n commonMistakes: [\"Vague 'it would be bad' statements\"],\n },\n // ── Method & Positioning ────────────────────────────────────────────────\n [ELEMENT_TYPES.GUIDING_POLICY.value]: {\n summary: 'Make a broad command that specifies an overall approach, method, or move to overcome the stated challenge.',\n goodInput: [\n 'A clear directional commitment',\n 'Consistent with the diagnosis',\n ],\n commonMistakes: ['A goal or target rather than an approach'],\n },\n [ELEMENT_TYPES.COMPETITOR_MOVE.value]: {\n summary: 'Note a competitor action that influences your strategy.',\n goodInput: ['A specific, observed competitor action'],\n },\n [ELEMENT_TYPES.SCOPE.value]: {\n summary: 'Define the boundaries or focus area for this method.',\n goodInput: [\"What's explicitly in and out of scope\"],\n commonMistakes: ['Leaving scope open-ended'],\n },\n [ELEMENT_TYPES.CONSTRAINT.value]: {\n summary: 'Identify a limitation or boundary condition.',\n goodInput: ['A real constraint: resource, regulatory, technical, or time'],\n },\n // ── Portfolio of Initiatives ────────────────────────────────────────────\n [ELEMENT_TYPES.SOLUTION.value]: {\n summary: 'Propose a generalized solution to the stated challenge, consistent with the Guiding Policy.',\n goodInput: [\n 'Follows from the guiding policy',\n 'Addresses the diagnosed challenge',\n ],\n commonMistakes: ['Solutions unrelated to the policy'],\n },\n [ELEMENT_TYPES.ASSUMPTION_HYPOTHESIS.value]: {\n summary: 'List the assumptions and hypotheses that underpin each solution alternative.',\n goodInput: ['A testable belief the solution depends on'],\n commonMistakes: ['Stating facts instead of assumptions'],\n },\n [ELEMENT_TYPES.EXPERIMENT.value]: {\n summary: 'Propose a test to validate the selected hypothesis.',\n goodInput: ['A test with a clear pass/fail signal'],\n commonMistakes: [\"Experiments that can't disprove the hypothesis\"],\n },\n [ELEMENT_TYPES.ACTION.value]: {\n summary: 'Specifically state the actions to address the stated challenge.',\n goodInput: ['A concrete, assignable action'],\n commonMistakes: ['Actions with no owner'],\n },\n [ELEMENT_TYPES.INVESTMENT.value]: {\n summary: 'Estimate the investment required, expected returns, and timeframe. List dependencies, known risks, and pre-requisites.',\n goodInput: [\n 'Cost, expected return, and timeframe',\n 'Dependencies and known risks',\n ],\n },\n [ELEMENT_TYPES.ESTIMATE.value]: {\n summary: 'Appraise and size the solution alternative.',\n goodInput: ['A quantitative estimate or projection'],\n },\n // ── Measures ────────────────────────────────────────────────────────────\n [ELEMENT_TYPES.BASELINE.value]: {\n summary: 'Establish a current-state baseline measurement.',\n goodInput: ['A measured current value to compare against'],\n commonMistakes: ['Guessing instead of measuring'],\n },\n [ELEMENT_TYPES.OUTCOME.value]: {\n summary: 'Define the desired outcome or result.',\n goodInput: ['An outcome, not an activity'],\n },\n [ELEMENT_TYPES.PERFORMANCE_VARIABLE.value]: {\n summary: 'Identify an independent variable with a material influence on the primary and secondary outcomes.',\n goodInput: ['A driver you can actually influence'],\n commonMistakes: ['Tracking variables outside your control'],\n },\n // ── Support Structures ──────────────────────────────────────────────────\n [ELEMENT_TYPES.CAPABILITY.value]: {\n summary: 'Describe a capability needed to support your strategy.',\n goodInput: ['A capability an initiative actually requires'],\n },\n [ELEMENT_TYPES.SYSTEM.value]: {\n summary: 'Identify a system or process that enables execution.',\n goodInput: ['A system tied to a capability or initiative'],\n },\n [ELEMENT_TYPES.PRINCIPLE.value]: {\n summary: 'State a guiding principle for decision-making.',\n goodInput: ['A principle concrete enough to guide a real decision'],\n commonMistakes: ['Platitudes too vague to act on'],\n },\n};\n/**\n * Logical next-step suggestions keyed by the element type just completed.\n * Partial — not every element type has a defined \"what next\" (e.g. terminal\n * or optional elements). Use {@link getNextStepSuggestions} for a safe read.\n */\nexport const NEXT_STEP_GUIDANCE = {\n [ELEMENT_TYPES.KEY_CHALLENGE.value]: [\n {\n element: ELEMENT_TYPES.SUPPORTING_FACT.value,\n component: COMPONENT_TYPES.DIAGNOSIS.value,\n reason: 'Document facts that validate this challenge',\n guidance: \"Add supporting facts that provide evidence for your key challenge. These facts should be verifiable and directly related to the challenge you've identified.\",\n },\n {\n element: ELEMENT_TYPES.IMPACT.value,\n component: COMPONENT_TYPES.DIAGNOSIS.value,\n reason: 'Define the business impact of this challenge',\n guidance: 'Articulate the specific business impacts this challenge creates. Focus on measurable consequences and strategic implications.',\n },\n ],\n [ELEMENT_TYPES.SUPPORTING_FACT.value]: [\n {\n element: ELEMENT_TYPES.IMPACT.value,\n component: COMPONENT_TYPES.DIAGNOSIS.value,\n reason: 'Connect facts to business impact',\n guidance: 'Now that you have supporting facts, define how these facts create specific business impacts. What are the consequences?',\n },\n {\n element: ELEMENT_TYPES.SUPPORTING_FACT.value,\n component: COMPONENT_TYPES.DIAGNOSIS.value,\n reason: 'Add more supporting evidence',\n guidance: 'Consider adding additional supporting facts to build a stronger foundation for your challenge.',\n },\n ],\n [ELEMENT_TYPES.IMPACT.value]: [\n {\n element: ELEMENT_TYPES.GUIDING_POLICY.value,\n component: COMPONENT_TYPES.METHOD.value,\n reason: 'Develop strategic response to the diagnosed impact',\n guidance: 'With your diagnosis complete, create a guiding policy that addresses the challenge and its impacts. This sets the strategic direction.',\n },\n {\n element: ELEMENT_TYPES.SUPPORTING_FACT.value,\n component: COMPONENT_TYPES.DIAGNOSIS.value,\n reason: 'Strengthen diagnosis with more evidence',\n guidance: 'Consider adding more supporting facts to create a more comprehensive diagnosis.',\n },\n ],\n [ELEMENT_TYPES.GUIDING_POLICY.value]: [\n {\n element: ELEMENT_TYPES.SCOPE.value,\n component: COMPONENT_TYPES.METHOD.value,\n reason: 'Define boundaries for your strategy',\n guidance: \"Clarify what's included and excluded from your strategic approach. This helps focus execution efforts.\",\n },\n {\n element: ELEMENT_TYPES.COMPETITOR_MOVE.value,\n component: COMPONENT_TYPES.METHOD.value,\n reason: 'Consider competitive landscape',\n guidance: \"Analyze how competitors might respond to your strategy or what moves they're already making in this space.\",\n },\n ],\n [ELEMENT_TYPES.SCOPE.value]: [\n {\n element: ELEMENT_TYPES.CONSTRAINT.value,\n component: COMPONENT_TYPES.METHOD.value,\n reason: 'Identify limiting factors',\n guidance: 'Define constraints that will shape your strategy execution. These help ensure realistic planning.',\n },\n {\n element: ELEMENT_TYPES.SOLUTION.value,\n component: COMPONENT_TYPES.INITIATIVES.value,\n reason: 'Begin developing solution options',\n guidance: 'Start exploring specific solution alternatives that align with your guiding policy and scope.',\n },\n ],\n [ELEMENT_TYPES.CONSTRAINT.value]: [\n {\n element: ELEMENT_TYPES.SOLUTION.value,\n component: COMPONENT_TYPES.INITIATIVES.value,\n reason: 'Develop solutions within constraints',\n guidance: 'Create solution alternatives that work within your identified constraints. Focus on practical, achievable options.',\n },\n ],\n [ELEMENT_TYPES.SOLUTION.value]: [\n {\n element: ELEMENT_TYPES.ASSUMPTION_HYPOTHESIS.value,\n component: COMPONENT_TYPES.INITIATIVES.value,\n reason: 'Identify assumptions behind solutions',\n guidance: 'Document the key assumptions your solution alternatives depend on. These will need validation.',\n },\n {\n element: ELEMENT_TYPES.ACTION.value,\n component: COMPONENT_TYPES.INITIATIVES.value,\n reason: 'Define concrete actions',\n guidance: 'Break down your solution into specific, actionable steps that teams can execute.',\n },\n ],\n [ELEMENT_TYPES.ASSUMPTION_HYPOTHESIS.value]: [\n {\n element: ELEMENT_TYPES.EXPERIMENT.value,\n component: COMPONENT_TYPES.INITIATIVES.value,\n reason: 'Design tests for your assumptions',\n guidance: 'Create experiments to validate or invalidate your key assumptions before full implementation.',\n },\n ],\n [ELEMENT_TYPES.ACTION.value]: [\n {\n element: ELEMENT_TYPES.ESTIMATE.value,\n component: COMPONENT_TYPES.INITIATIVES.value,\n reason: 'Quantify action requirements',\n guidance: 'Estimate the resources, time, and effort required for your actions. This supports planning and prioritization.',\n },\n {\n element: ELEMENT_TYPES.BASELINE.value,\n component: COMPONENT_TYPES.MEASURES.value,\n reason: 'Establish measurement baseline',\n guidance: 'Define current state metrics to measure progress against as you execute your actions.',\n },\n ],\n [ELEMENT_TYPES.ESTIMATE.value]: [\n {\n element: ELEMENT_TYPES.INVESTMENT.value,\n component: COMPONENT_TYPES.INITIATIVES.value,\n reason: 'Define required investments',\n guidance: 'Specify the financial and resource investments needed to execute your strategy.',\n },\n ],\n [ELEMENT_TYPES.BASELINE.value]: [\n {\n element: ELEMENT_TYPES.OUTCOME.value,\n component: COMPONENT_TYPES.MEASURES.value,\n reason: 'Define target outcomes',\n guidance: 'Specify the desired outcomes you want to achieve. These should be measurable and time-bound.',\n },\n ],\n [ELEMENT_TYPES.OUTCOME.value]: [\n {\n element: ELEMENT_TYPES.PERFORMANCE_VARIABLE.value,\n component: COMPONENT_TYPES.MEASURES.value,\n reason: 'Define tracking metrics',\n guidance: \"Identify the specific variables you'll track to monitor progress toward your outcomes.\",\n },\n {\n element: ELEMENT_TYPES.CAPABILITY.value,\n component: COMPONENT_TYPES.SUPPORT.value,\n reason: 'Identify required capabilities',\n guidance: 'Define the organizational capabilities needed to achieve your outcomes.',\n },\n ],\n};\n/** Returns component-level guidance for a component value, if defined. */\nexport function getComponentGuidance(componentValue) {\n return COMPONENT_GUIDANCE[componentValue];\n}\n/** Returns element-type-level guidance for an element value, if defined. */\nexport function getElementGuidance(elementValue) {\n return ELEMENT_GUIDANCE[elementValue];\n}\n/**\n * Returns the logical next-step suggestions for the element type just\n * completed. Returns an empty array when none are defined.\n */\nexport function getNextStepSuggestions(elementValue) {\n return NEXT_STEP_GUIDANCE[elementValue] ?? [];\n}\n","import type {\n ComponentContentData,\n ComponentEmbedAnalytics,\n ContentData,\n EmbedAnalytics,\n} from '../components/types.js'\n\n/**\n * Analytics endpoint — Supabase edge function via Cloudflare proxy.\n * Falls back to direct Supabase URL if proxy is not configured.\n */\nconst ANALYTICS_ENDPOINT =\n 'https://analytics.skhema.com/functions/v1/embed-manage'\n\n// Cookie-based tracking management\ninterface TrackedEmbed {\n contentHash: string\n timestamp: number\n}\n\nconst TRACKING_COOKIE_NAME = '_sk'\nconst TRACKING_EXPIRY_HOURS = 24\nconst MAX_TRACKED_ITEMS = 50 // Prevent cookie from growing too large\n\nfunction getTrackedEmbeds(): TrackedEmbed[] {\n try {\n const cookie = document.cookie\n .split('; ')\n .find((row) => row.startsWith(`${TRACKING_COOKIE_NAME}=`))\n\n if (!cookie) return []\n\n const data = JSON.parse(decodeURIComponent(cookie.split('=')[1]))\n const now = Date.now()\n const cutoff = now - TRACKING_EXPIRY_HOURS * 60 * 60 * 1000\n\n // Filter out expired entries\n return data.filter((item: TrackedEmbed) => item.timestamp > cutoff)\n } catch {\n return []\n }\n}\n\nfunction setTrackedEmbeds(tracked: TrackedEmbed[]): void {\n try {\n // Keep only the most recent entries to prevent cookie bloat\n const limited = tracked.slice(-MAX_TRACKED_ITEMS)\n const expires = new Date(\n Date.now() + TRACKING_EXPIRY_HOURS * 60 * 60 * 1000\n )\n\n document.cookie = `${TRACKING_COOKIE_NAME}=${encodeURIComponent(\n JSON.stringify(limited)\n )}; expires=${expires.toUTCString()}; path=/; SameSite=Lax`\n } catch {\n // Fail silently if cookie storage fails\n }\n}\n\nfunction hasBeenTracked(contentHash: string): boolean {\n const tracked = getTrackedEmbeds()\n return tracked.some((item) => item.contentHash === contentHash)\n}\n\nfunction markAsTracked(contentHash: string): void {\n const tracked = getTrackedEmbeds()\n tracked.push({\n contentHash,\n timestamp: Date.now(),\n })\n setTrackedEmbeds(tracked)\n}\n\n// Batching system for analytics\ninterface BatchedAnalytics {\n embeds: EmbedAnalytics[]\n clicks: ContentData[]\n componentEmbeds: ComponentEmbedAnalytics[]\n componentClicks: ComponentContentData[]\n}\n\nclass AnalyticsBatcher {\n private batch: BatchedAnalytics = {\n embeds: [],\n clicks: [],\n componentEmbeds: [],\n componentClicks: [],\n }\n private batchTimeout: number | null = null\n private readonly BATCH_DELAY = 2000 // 2 seconds\n private readonly MAX_BATCH_SIZE = 10\n\n addEmbedLoad(analytics: EmbedAnalytics): void {\n this.batch.embeds.push(analytics)\n this.scheduleBatchSend()\n }\n\n addClick(contentData: ContentData): void {\n this.batch.clicks.push(contentData)\n this.scheduleBatchSend()\n }\n\n addComponentEmbedLoad(analytics: ComponentEmbedAnalytics): void {\n this.batch.componentEmbeds.push(analytics)\n this.scheduleBatchSend()\n }\n\n addComponentClick(contentData: ComponentContentData): void {\n this.batch.componentClicks.push(contentData)\n this.scheduleBatchSend()\n }\n\n private scheduleBatchSend(): void {\n // Clear existing timeout\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout)\n }\n\n // Send immediately if any batch is full\n const totalItems =\n this.batch.embeds.length +\n this.batch.clicks.length +\n this.batch.componentEmbeds.length +\n this.batch.componentClicks.length\n\n if (totalItems >= this.MAX_BATCH_SIZE) {\n this.sendBatch()\n return\n }\n\n // Otherwise, wait for more events or timeout\n this.batchTimeout = window.setTimeout(() => {\n this.sendBatch()\n }, this.BATCH_DELAY)\n }\n\n private async sendBatch(): Promise<void> {\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout)\n this.batchTimeout = null\n }\n\n const currentBatch = { ...this.batch }\n this.batch = {\n embeds: [],\n clicks: [],\n componentEmbeds: [],\n componentClicks: [],\n }\n\n const hasItems =\n currentBatch.embeds.length > 0 ||\n currentBatch.clicks.length > 0 ||\n currentBatch.componentEmbeds.length > 0 ||\n currentBatch.componentClicks.length > 0\n\n if (!hasItems) return\n\n // Send embeds if any\n if (currentBatch.embeds.length > 0) {\n await this.sendEmbeds(currentBatch.embeds)\n }\n\n // Send clicks if any\n if (currentBatch.clicks.length > 0) {\n await this.sendClicks(currentBatch.clicks)\n }\n\n // Send component embeds if any\n if (currentBatch.componentEmbeds.length > 0) {\n await this.sendComponentEmbeds(currentBatch.componentEmbeds)\n }\n\n // Send component clicks if any\n if (currentBatch.componentClicks.length > 0) {\n await this.sendComponentClicks(currentBatch.componentClicks)\n }\n }\n\n private async sendEmbeds(embeds: EmbedAnalytics[]): Promise<void> {\n if (embeds.length === 0) return\n\n try {\n const payload = {\n action: 'embed',\n events: embeds.map((embed) => ({\n contributor_id: embed.contributorId,\n element_type: embed.elementType,\n content_hash: embed.contentHash,\n content: embed.content,\n page_url: embed.pageUrl,\n page_title: embed.pageTitle || '',\n user_agent: embed.userAgent || '',\n })),\n }\n\n await sendWithRetry(ANALYTICS_ENDPOINT, payload, 'json')\n } catch (error) {\n console.debug('Embed tracking failed:', error)\n }\n }\n\n private async sendClicks(clicks: ContentData[]): Promise<void> {\n // Send clicks individually since they typically navigate away from page\n for (const click of clicks) {\n try {\n const data = {\n action: 'click',\n contributor_id: click.contributor_id,\n content_hash: click.content_hash,\n source_url: click.source_url,\n element_type: click.element_type,\n }\n\n fetch(ANALYTICS_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n credentials: 'omit',\n keepalive: true,\n }).catch(() => {\n // Fail silently\n })\n } catch (error) {\n console.debug('Click tracking failed:', error)\n }\n }\n }\n\n private async sendComponentEmbeds(\n embeds: ComponentEmbedAnalytics[]\n ): Promise<void> {\n if (embeds.length === 0) return\n\n // Edge function expects flat fields at top level, not wrapped in events[]\n for (const embed of embeds) {\n try {\n const payload = {\n action: 'component_embed',\n contributor_id: embed.contributorId,\n component_type: embed.componentType,\n component_hash: embed.componentHash,\n title: embed.title,\n elements: embed.elements.map((el) => ({\n element_type: el.elementType,\n content: el.content,\n content_hash: el.contentHash,\n })),\n page_url: embed.pageUrl,\n page_title: embed.pageTitle || '',\n user_agent: embed.userAgent || '',\n }\n\n await sendWithRetry(ANALYTICS_ENDPOINT, payload, 'json')\n } catch (error) {\n console.debug('Component embed tracking failed:', error)\n }\n }\n }\n\n private async sendComponentClicks(\n clicks: ComponentContentData[]\n ): Promise<void> {\n for (const click of clicks) {\n try {\n const data = {\n action: 'component_click',\n contributor_id: click.contributor_id,\n component_type: click.component_type,\n component_hash: click.component_hash,\n title: click.title,\n source_url: click.source_url,\n }\n\n fetch(ANALYTICS_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n credentials: 'omit',\n keepalive: true,\n }).catch(() => {\n // Fail silently\n })\n } catch (error) {\n console.debug('Component click tracking failed:', error)\n }\n }\n }\n\n // Ensure batch is sent when page unloads.\n // Uses sendBeacon for reliability — async fetch won't complete during unload.\n flush(): void {\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout)\n this.batchTimeout = null\n }\n\n const currentBatch = { ...this.batch }\n this.batch = {\n embeds: [],\n clicks: [],\n componentEmbeds: [],\n componentClicks: [],\n }\n\n // Send embeds via beacon\n if (currentBatch.embeds.length > 0) {\n const payload = {\n action: 'embed',\n events: currentBatch.embeds.map((embed) => ({\n contributor_id: embed.contributorId,\n element_type: embed.elementType,\n content_hash: embed.contentHash,\n content: embed.content,\n page_url: embed.pageUrl,\n page_title: embed.pageTitle || '',\n user_agent: embed.userAgent || '',\n })),\n }\n this.sendViaBeacon(payload)\n }\n\n // Send clicks via beacon\n for (const click of currentBatch.clicks) {\n this.sendViaBeacon({\n action: 'click',\n contributor_id: click.contributor_id,\n content_hash: click.content_hash,\n source_url: click.source_url,\n element_type: click.element_type,\n })\n }\n\n // Send component embeds via beacon (flat fields, not wrapped in events[])\n for (const embed of currentBatch.componentEmbeds) {\n this.sendViaBeacon({\n action: 'component_embed',\n contributor_id: embed.contributorId,\n component_type: embed.componentType,\n component_hash: embed.componentHash,\n title: embed.title,\n elements: embed.elements.map((el) => ({\n element_type: el.elementType,\n content: el.content,\n content_hash: el.contentHash,\n })),\n page_url: embed.pageUrl,\n page_title: embed.pageTitle || '',\n user_agent: embed.userAgent || '',\n })\n }\n\n // Send component clicks via beacon\n for (const click of currentBatch.componentClicks) {\n this.sendViaBeacon({\n action: 'component_click',\n contributor_id: click.contributor_id,\n component_type: click.component_type,\n component_hash: click.component_hash,\n title: click.title,\n source_url: click.source_url,\n })\n }\n }\n\n private sendViaBeacon(payload: Record<string, unknown>): void {\n try {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(\n ANALYTICS_ENDPOINT,\n new Blob([JSON.stringify(payload)], { type: 'application/json' })\n )\n } else {\n // Fallback to fetch with keepalive\n fetch(ANALYTICS_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n credentials: 'omit',\n keepalive: true,\n }).catch(() => {})\n }\n } catch {\n // Fail silently during unload\n }\n }\n}\n\n// Global batcher instance\nconst analyticsBatcher = new AnalyticsBatcher()\n\n// Flush on page unload\nif (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => {\n analyticsBatcher.flush()\n })\n\n // Also flush on visibility change (mobile browsers)\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n analyticsBatcher.flush()\n }\n })\n}\n\n// Retry logic with exponential backoff\nasync function sendWithRetry(\n url: string,\n data: URLSearchParams | Record<string, unknown>,\n contentType: 'json' | 'urlencoded' = 'json',\n maxRetries = 3\n): Promise<void> {\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n const options: RequestInit = {\n method: 'POST',\n credentials: 'omit',\n keepalive: true,\n }\n\n if (contentType === 'json') {\n options.headers = { 'Content-Type': 'application/json' }\n options.body = JSON.stringify(data)\n } else {\n options.body = data as URLSearchParams\n }\n\n const response = await fetch(url, options)\n\n if (response.ok) return\n\n if (response.status >= 400 && response.status < 500) {\n // Client error, don't retry\n break\n }\n } catch (error) {\n if (attempt === maxRetries - 1) {\n console.debug('Analytics failed after retries:', error)\n return\n }\n }\n\n // Exponential backoff: 1s, 2s, 4s\n await new Promise((resolve) =>\n setTimeout(resolve, Math.pow(2, attempt) * 1000)\n )\n }\n}\n\n// Main tracking functions\nexport async function trackEmbedLoad(analytics: EmbedAnalytics): Promise<void> {\n try {\n // Check if this embed has already been tracked\n if (hasBeenTracked(analytics.contentHash)) {\n console.debug('Embed already tracked, skipping:', analytics.contentHash)\n return\n }\n\n // Mark as tracked before sending to prevent race conditions\n markAsTracked(analytics.contentHash)\n\n // Add to batch instead of sending immediately\n analyticsBatcher.addEmbedLoad(analytics)\n } catch (error) {\n console.debug('Analytics tracking failed:', error)\n }\n}\n\nexport async function trackClick(contentData: ContentData): Promise<void> {\n try {\n // Add to batch instead of sending immediately\n analyticsBatcher.addClick(contentData)\n } catch (error) {\n console.debug('Click tracking failed:', error)\n }\n}\n\nexport async function trackComponentEmbedLoad(\n analytics: ComponentEmbedAnalytics\n): Promise<void> {\n try {\n // Check if this component has already been tracked\n if (hasBeenTracked(analytics.componentHash)) {\n console.debug(\n 'Component embed already tracked, skipping:',\n analytics.componentHash\n )\n return\n }\n\n // Mark as tracked before sending to prevent race conditions\n markAsTracked(analytics.componentHash)\n\n // Add to batch instead of sending immediately\n analyticsBatcher.addComponentEmbedLoad(analytics)\n } catch (error) {\n console.debug('Component analytics tracking failed:', error)\n }\n}\n\nexport async function trackComponentClick(\n contentData: ComponentContentData\n): Promise<void> {\n try {\n analyticsBatcher.addComponentClick(contentData)\n } catch (error) {\n console.debug('Component click tracking failed:', error)\n }\n}\n\nexport function shouldTrackAnalytics(element: HTMLElement): boolean {\n const trackAnalytics = element.getAttribute('track-analytics')\n return trackAnalytics !== 'false'\n}\n","export function generateContentHash(content: string): string {\n // Simple hash function for content identification\n let hash = 0\n const cleanContent = content.trim().substring(0, 200)\n\n for (let i = 0; i < cleanContent.length; i++) {\n const char = cleanContent.charCodeAt(i)\n hash = (hash << 5) - hash + char\n hash = hash & hash // Convert to 32bit integer\n }\n\n return Math.abs(hash).toString(36).substring(0, 12)\n}\n\n/**\n * Generate a hash for a component by concatenating sorted element content.\n * Used for component-level deduplication and analytics.\n */\nexport function generateComponentHash(\n elements: Array<{ elementType: string; content: string }>\n): string {\n const sorted = [...elements].sort((a, b) => {\n const typeCompare = a.elementType.localeCompare(b.elementType)\n if (typeCompare !== 0) return typeCompare\n return a.content.localeCompare(b.content)\n })\n\n const combined = sorted.map((e) => `${e.elementType}:${e.content}`).join('|')\n return generateContentHash(combined)\n}\n","import type { ComponentValue, ElementValue } from '@skhema/types'\nimport { getComponentTypeLabel } from './component-validation.js'\nimport { generateContentHash } from './hash.js'\nimport { getElementTypeLabel } from './validation.js'\n\nexport function generateStructuredData(\n content: string,\n elementType: ElementValue,\n contributorId: string,\n sourceUrl: string\n): object {\n return {\n '@context': 'https://schema.org',\n '@type': 'AnalysisContent',\n text: content,\n analysisType: elementType,\n category: getElementTypeLabel(elementType),\n contributor: contributorId,\n url: generateRedirectUrl(content, elementType, contributorId),\n provider: {\n '@type': 'Organization',\n name: 'Skhema',\n url: 'https://skhema.com',\n },\n isPartOf: {\n '@type': 'WebPage',\n url: sourceUrl,\n },\n dateCreated: new Date().toISOString(),\n platform: 'Skhema',\n }\n}\n\nexport function generateRedirectUrl(\n content: string,\n elementType: ElementValue,\n contributorId: string,\n options: {\n baseUrl?: string\n utmSource?: string\n utmMedium?: string\n utmCampaign?: string\n } = {}\n): string {\n const baseUrl = options.baseUrl || 'https://app.skhema.com/save' // This page will handle the authentication and content saving\n const contentHash = generateContentHash(content)\n const sourceUrl = encodeURIComponent(window.location.href)\n const timestamp = Date.now()\n\n const params = new URLSearchParams({\n source: sourceUrl,\n t: timestamp.toString(),\n utm_source: options.utmSource || 'web_component',\n utm_medium: options.utmMedium || 'embedded',\n utm_campaign: options.utmCampaign || elementType,\n utm_content: contributorId,\n })\n\n return `${baseUrl}?type=contributor&contributor_id=${contributorId}&element_type=${elementType}&content_hash=${contentHash}&${params.toString()}`\n // return `${baseUrl}/contributor_id=${contributorId}&element_type=${elementType}&content_hash=${contentHash}?${params.toString()}`;\n}\n\nexport function createMetaTags(\n content: string,\n elementType: ElementValue,\n contributorId: string\n): string {\n const label = getElementTypeLabel(elementType)\n\n return `\n <div itemscope itemtype=\"https://schema.org/AnalysisContent\" style=\"display:none;\">\n <meta itemprop=\"analysisType\" content=\"${elementType}\">\n <meta itemprop=\"text\" content=\"${content}\">\n <meta itemprop=\"contributor\" content=\"${contributorId}\">\n <meta itemprop=\"category\" content=\"${label}\">\n <meta itemprop=\"platform\" content=\"Skhema\">\n </div>\n `\n}\n\nexport function generateComponentStructuredData(\n title: string,\n componentType: ComponentValue,\n contributorId: string,\n elements: Array<{ elementType: string; content: string }>,\n sourceUrl: string\n): object {\n const componentLabel = getComponentTypeLabel(componentType)\n\n return {\n '@context': 'https://schema.org',\n '@type': 'CreativeWork',\n name: title,\n description: `${componentLabel} strategy component`,\n category: componentLabel,\n contributor: contributorId,\n url: generateComponentRedirectUrl(\n generateContentHash(elements.map((e) => e.content).join('|')),\n componentType,\n contributorId\n ),\n hasPart: elements.map((el) => ({\n '@type': 'AnalysisContent',\n text: el.content,\n analysisType: el.elementType,\n category: getElementTypeLabel(el.elementType as ElementValue),\n })),\n provider: {\n '@type': 'Organization',\n name: 'Skhema',\n url: 'https://skhema.com',\n },\n isPartOf: {\n '@type': 'WebPage',\n url: sourceUrl,\n },\n dateCreated: new Date().toISOString(),\n platform: 'Skhema',\n }\n}\n\nexport function generateComponentRedirectUrl(\n componentHash: string,\n componentType: ComponentValue,\n contributorId: string,\n options: {\n baseUrl?: string\n utmSource?: string\n utmMedium?: string\n utmCampaign?: string\n } = {}\n): string {\n const baseUrl = options.baseUrl || 'https://app.skhema.com/save'\n const sourceUrl = encodeURIComponent(window.location.href)\n const timestamp = Date.now()\n\n const params = new URLSearchParams({\n source: sourceUrl,\n t: timestamp.toString(),\n utm_source: options.utmSource || 'web_component',\n utm_medium: options.utmMedium || 'embedded',\n utm_campaign: options.utmCampaign || componentType,\n utm_content: contributorId,\n })\n\n return `${baseUrl}?type=component&component_type=${componentType}&component_hash=${componentHash}&contributor_id=${contributorId}&${params.toString()}`\n}\n\nexport function createAriaAttributes(\n elementType: ElementValue\n): Record<string, string> {\n const label = getElementTypeLabel(elementType)\n\n return {\n role: 'article',\n 'aria-label': `${label} - Strategic element`,\n 'aria-describedby': 'skhema-description',\n }\n}\n","import type { ComponentValue, ElementValue } from '@skhema/types'\nimport { renderComponentCardHtml } from '../render/index.js'\nimport {\n CARD_SHADOW_LG,\n PRIMARY_HOVER_HEX,\n PRIMARY_PRESSED_HEX,\n} from '../styles/design-tokens.js'\nimport {\n shouldTrackAnalytics,\n trackComponentClick,\n trackComponentEmbedLoad,\n} from '../utils/analytics.js'\nimport {\n getComponentTypeLabel,\n getElementTypesForComponent,\n isValidComponentType,\n validateElementBelongsToComponent,\n} from '../utils/component-validation.js'\nimport { generateComponentHash } from '../utils/hash.js'\nimport {\n generateComponentRedirectUrl,\n generateComponentStructuredData,\n} from '../utils/seo.js'\nimport type { SkhemaElement } from './SkhemaElement.js'\nimport type {\n ComponentContentData,\n ComponentEmbedAnalytics,\n NestedElementData,\n SkhemaComponentAttributes,\n SkhemaComponentEventMap,\n} from './types.js'\n\n// Browser-only styles. The card STRUCTURE + base colours come from the shared,\n// email-safe `@skhema/embed/render` output (inline hex). This block carries the\n// progressive enhancements (hover/transition over `.skhema-card`) plus the\n// shadow-DOM-only skeleton + error states (unchanged CSS-var styling).\nconst styles = `\n:host {\n --skhema-primary: hsl(344 57% 54%);\n --skhema-primary-hover: hsl(344 50% 47%);\n --skhema-secondary: hsl(345 100% 75%);\n\n /* Light mode colors */\n --skhema-bg: hsl(0 0% 100%);\n --skhema-card: hsl(0 0% 100%);\n --skhema-border: hsl(214.3 31.8% 91.4%);\n --skhema-text: hsl(222.2 84% 4.9%);\n --skhema-text-muted: hsl(215.4 16.3% 46.9%);\n --skhema-accent: hsl(210 40% 96%);\n --skhema-surface-subtle: hsl(210 40% 97%);\n\n /* Shadows */\n --skhema-shadow: 0 1px 3px 0 hsl(0 0 0 / 0.1), 0 1px 2px -1px hsl(0 0 0 / 0.1);\n --skhema-shadow-md: 0 4px 6px -1px hsl(0 0 0 / 0.1), 0 2px 4px -2px hsl(0 0 0 / 0.1);\n --skhema-shadow-lg: 0 10px 15px -3px hsl(0 0 0 / 0.1), 0 4px 6px -4px hsl(0 0 0 / 0.1);\n --skhema-radius: 0.1rem;\n\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif;\n line-height: 1.5;\n color: var(--skhema-text);\n}\n\n/* Dark mode (skeleton/error only) */\n.skhema-component-card[data-theme=\"dark\"],\n.skhema-skeleton[data-theme=\"dark\"] {\n --skhema-bg: hsl(222.2 84% 4.9%);\n --skhema-card: hsl(222.2 84% 4.9%);\n --skhema-border: hsl(217.2 32.6% 17.5%);\n --skhema-text: hsl(210 40% 98%);\n --skhema-text-muted: hsl(215 20.2% 65.1%);\n --skhema-accent: hsl(217.2 32.6% 17.5%);\n --skhema-surface-subtle: hsl(217.2 32.6% 12%);\n}\n\n/* Progressive enhancement over the inline-styled, rendered card. Inline styles\n win on specificity, so the changed properties use !important to layer on. */\n.skhema-card {\n transition: box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1), transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.skhema-card:hover {\n box-shadow: ${CARD_SHADOW_LG} !important;\n transform: translateY(-1px);\n}\n\n.skhema-save-btn {\n transition: background 0.15s ease;\n}\n\n.skhema-save-btn:hover {\n background: ${PRIMARY_HOVER_HEX} !important;\n}\n\n.skhema-save-btn:active {\n background: ${PRIMARY_PRESSED_HEX} !important;\n}\n\n/* Error card wrapper (browser-only state) */\n.skhema-component-card {\n background: var(--skhema-card);\n border: 1px solid var(--skhema-border);\n border-radius: calc(var(--skhema-radius) + 2px);\n box-shadow: var(--skhema-shadow);\n max-width: 600px;\n margin: 8px 0;\n overflow: hidden;\n}\n\n/* Error state */\n.skhema-error {\n background: hsl(0 93% 94%);\n border: 1px solid hsl(0 84% 60%);\n color: hsl(0 74% 42%);\n padding: 12px;\n border-radius: calc(var(--skhema-radius) + 2px);\n font-size: 13px;\n}\n\n.skhema-error-title {\n font-weight: 600;\n margin-bottom: 8px;\n}\n\n.skhema-error-list {\n margin: 0;\n padding-left: 16px;\n}\n\n/* Skeleton loading state */\n.skhema-skeleton {\n background: var(--skhema-card);\n border: 1px solid var(--skhema-border);\n border-radius: calc(var(--skhema-radius) + 2px);\n padding: 16px;\n box-shadow: var(--skhema-shadow);\n max-width: 600px;\n margin: 8px 0;\n animation: skeletonPulse 1.5s ease-in-out infinite;\n}\n\n.skhema-skeleton-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 12px;\n}\n\n.skhema-skeleton-badge {\n width: 32px;\n height: 20px;\n border-radius: 2px;\n background: linear-gradient(90deg,\n var(--skhema-border) 25%,\n var(--skhema-accent) 50%,\n var(--skhema-border) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n}\n\n.skhema-skeleton-text {\n flex: 1;\n}\n\n.skhema-skeleton-line {\n height: 12px;\n background: linear-gradient(90deg,\n var(--skhema-border) 25%,\n var(--skhema-accent) 50%,\n var(--skhema-border) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n border-radius: calc(var(--skhema-radius) + 2px);\n margin: 6px 0;\n}\n\n.skhema-skeleton-line.short { width: 40%; }\n.skhema-skeleton-line.medium { width: 70%; }\n\n.skhema-skeleton-content {\n margin: 16px 0;\n}\n\n@keyframes skeletonPulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.8; }\n}\n\n@keyframes shimmer {\n 0% { background-position: -200% 0; }\n 100% { background-position: 200% 0; }\n}\n\n/* Accessibility */\n@media (prefers-reduced-motion: reduce) {\n .skhema-card,\n .skhema-save-btn {\n transition: none;\n }\n}\n\n.skhema-structured-data {\n display: none !important;\n}\n`\n\nexport class SkhemaComponent extends HTMLElement {\n private shadow: ShadowRoot\n private contentData: ComponentContentData | null = null\n private componentConnected = false\n private hasTrackedLoad = false\n private pendingRender: number | null = null\n private themeObserver: MutationObserver | null = null\n private mediaQueryListener: MediaQueryList | null = null\n\n constructor() {\n super()\n this.shadow = this.attachShadow({ mode: 'closed' })\n this.renderSkeleton()\n }\n\n static get observedAttributes(): (keyof SkhemaComponentAttributes)[] {\n return [\n 'component-type',\n 'contributor-id',\n 'author-name',\n 'author-slug',\n 'contributor-name',\n 'title',\n 'theme',\n 'track-analytics',\n 'source-url',\n ]\n }\n\n connectedCallback() {\n if (this.componentConnected) return\n this.componentConnected = true\n\n try {\n this.addPreconnectHints()\n\n // Listen for child element ready events (fired by children that upgrade after us)\n this.addEventListener('skhema:element-ready', () => {\n this.scheduleRender()\n })\n\n // Children may have already upgraded and dispatched skhema:element-ready\n // before our listener was set up (registration order: skhema-element first).\n // Wait for any pending custom element upgrades, then render.\n if (typeof customElements.whenDefined === 'function') {\n customElements.whenDefined('skhema-element').then(() => {\n this.scheduleRender()\n })\n } else {\n this.scheduleRender()\n }\n\n this.setupThemeListeners()\n } catch (error) {\n this.renderError('Failed to initialize component', error)\n }\n }\n\n disconnectedCallback() {\n this.cleanupThemeListeners()\n if (this.pendingRender !== null) {\n cancelAnimationFrame(this.pendingRender)\n this.pendingRender = null\n }\n }\n\n attributeChangedCallback(\n _name: keyof SkhemaComponentAttributes,\n oldValue: string | null,\n newValue: string | null\n ) {\n if (oldValue !== newValue && this.componentConnected) {\n this.scheduleRender()\n }\n }\n\n private scheduleRender() {\n if (this.pendingRender !== null) {\n cancelAnimationFrame(this.pendingRender)\n }\n this.pendingRender = requestAnimationFrame(() => {\n this.pendingRender = null\n this.render()\n })\n }\n\n private render() {\n const validation = this.validateComponentAttributes()\n\n if (!validation.isValid) {\n this.renderError('Invalid component attributes', validation.errors)\n return\n }\n\n const componentType = validation.componentType!\n const contributorId = validation.contributorId!\n const title = this.getAttribute('title') || ''\n\n // Gather child element data\n const childElements = this.gatherChildElements(componentType)\n\n if (childElements.length === 0) {\n this.renderError('Component requires child elements', [\n 'Add <skhema-element> children inside this component',\n ])\n return\n }\n\n const componentHash = generateComponentHash(\n childElements.map((el) => ({\n elementType: el.elementType,\n content: el.content,\n }))\n )\n\n this.contentData = {\n contributor_id: contributorId,\n component_type: componentType,\n component_hash: componentHash,\n title,\n elements: childElements.map((el) => ({\n element_type: el.elementType,\n content: el.content,\n content_hash: el.contentHash,\n })),\n source_url: this.getAttribute('source-url') || window.location.href,\n timestamp: new Date().toISOString(),\n page_title: document.title,\n }\n\n this.renderContent(childElements)\n this.addStructuredData()\n this.trackLoad()\n }\n\n private validateComponentAttributes(): {\n isValid: boolean\n errors: string[]\n componentType?: ComponentValue\n contributorId?: string\n } {\n const errors: string[] = []\n const componentType = this.getAttribute('component-type')\n const contributorId = this.getAttribute('contributor-id')\n\n if (!componentType) {\n errors.push('Missing required attribute: component-type')\n } else if (!isValidComponentType(componentType)) {\n errors.push(`Invalid component-type \"${componentType}\"`)\n }\n\n if (!contributorId) {\n errors.push('Missing required attribute: contributor-id')\n } else if (contributorId.trim().length === 0) {\n errors.push('contributor-id cannot be empty')\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n componentType: isValidComponentType(componentType || '')\n ? (componentType as ComponentValue)\n : undefined,\n contributorId: contributorId || undefined,\n }\n }\n\n private gatherChildElements(\n componentType: ComponentValue\n ): NestedElementData[] {\n const children = Array.from(\n this.querySelectorAll('skhema-element')\n ) as SkhemaElement[]\n\n const elements: NestedElementData[] = []\n\n for (const child of children) {\n const data = child.getElementData?.()\n if (!data) continue\n\n // Validate element belongs to this component type\n if (!validateElementBelongsToComponent(data.elementType, componentType)) {\n console.warn(\n `skhema-component: element type \"${data.elementType}\" does not belong to component type \"${componentType}\"`\n )\n // Warn but don't block — still include the element\n }\n\n elements.push(data)\n }\n\n // Sort by elementFlow order for the component type\n const orderedTypes = getElementTypesForComponent(componentType)\n\n elements.sort((a, b) => {\n const aIdx = orderedTypes.indexOf(a.elementType as ElementValue)\n const bIdx = orderedTypes.indexOf(b.elementType as ElementValue)\n // Unknown types go to the end\n const aOrder = aIdx === -1 ? orderedTypes.length : aIdx\n const bOrder = bIdx === -1 ? orderedTypes.length : bIdx\n return aOrder - bOrder\n })\n\n return elements\n }\n\n private renderContent(elements: NestedElementData[]) {\n if (!this.contentData) return\n\n const { component_type, contributor_id, title } = this.contentData\n const componentLabel = getComponentTypeLabel(component_type)\n\n const themeAttribute = this.getAttribute('theme') || 'auto'\n const actualTheme = this.getActualTheme(themeAttribute)\n\n const redirectUrl = generateComponentRedirectUrl(\n this.contentData.component_hash,\n component_type,\n contributor_id\n )\n\n // Set ARIA attributes\n this.setAttribute('role', 'article')\n this.setAttribute(\n 'aria-label',\n `${componentLabel} component${title ? ` — ${title}` : ''}`\n )\n\n // Card structure + base styling come from the shared, email-safe renderer\n // (single source of truth). The renderer groups elements by type; the\n // browser-only `<style>` layers hover / transitions on top.\n const cardHtml = renderComponentCardHtml({\n componentType: component_type,\n title,\n elements: elements.map((el) => ({\n elementType: el.elementType,\n content: el.content,\n })),\n saveUrl: redirectUrl,\n authorName:\n this.getAttribute('author-name') ||\n this.getAttribute('contributor-name'),\n authorSlug: this.getAttribute('author-slug'),\n contributorId: contributor_id,\n theme: actualTheme,\n })\n\n this.shadow.innerHTML = `<style>${styles}</style>${cardHtml}<slot style=\"display:none;\"></slot>`\n\n // Add click listener\n const saveBtn = this.shadow.querySelector(\n '.skhema-save-btn'\n ) as HTMLAnchorElement\n if (saveBtn) {\n saveBtn.addEventListener('click', (event) => {\n this.handleSaveClick(event)\n })\n }\n }\n\n private getActualTheme(themeAttribute: string): 'light' | 'dark' {\n if (themeAttribute === 'light' || themeAttribute === 'dark') {\n return themeAttribute\n }\n\n const htmlElement = document.documentElement\n const bodyElement = document.body\n\n const htmlTheme =\n htmlElement.getAttribute('data-theme') ||\n htmlElement.getAttribute('theme') ||\n htmlElement.className.match(/theme-(\\w+)/)?.[1]\n\n const bodyTheme =\n bodyElement.getAttribute('data-theme') ||\n bodyElement.getAttribute('theme') ||\n bodyElement.className.match(/theme-(\\w+)/)?.[1]\n\n const hasDarkClass =\n htmlElement.classList.contains('dark') ||\n bodyElement.classList.contains('dark') ||\n htmlElement.classList.contains('dark-mode') ||\n bodyElement.classList.contains('dark-mode')\n\n if (hasDarkClass || htmlTheme === 'dark' || bodyTheme === 'dark') {\n return 'dark'\n }\n\n const computedStyles = window.getComputedStyle(htmlElement)\n const colorScheme = computedStyles.getPropertyValue('color-scheme')\n if (colorScheme && colorScheme.includes('dark')) {\n return 'dark'\n }\n\n if (\n window.matchMedia &&\n window.matchMedia('(prefers-color-scheme: dark)').matches\n ) {\n return 'dark'\n }\n\n return 'light'\n }\n\n private addPreconnectHints() {\n if (\n document.querySelector(\n 'link[rel=\"preconnect\"][href*=\"analytics.skhema.com\"]'\n )\n ) {\n return\n }\n\n try {\n const preconnectApi = document.createElement('link')\n preconnectApi.rel = 'preconnect'\n preconnectApi.href = 'https://analytics.skhema.com'\n document.head.appendChild(preconnectApi)\n\n const dnsPrefetch = document.createElement('link')\n dnsPrefetch.rel = 'dns-prefetch'\n dnsPrefetch.href = 'https://skhema.com'\n document.head.appendChild(dnsPrefetch)\n } catch (error) {\n console.debug('Failed to add preconnect hints:', error)\n }\n }\n\n private renderSkeleton() {\n const themeAttribute = this.getAttribute('theme') || 'auto'\n const actualTheme = this.getActualTheme(themeAttribute)\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n\n <div class=\"skhema-skeleton\" data-theme=\"${actualTheme}\">\n <div class=\"skhema-skeleton-header\">\n <div class=\"skhema-skeleton-badge\"></div>\n <div class=\"skhema-skeleton-text\">\n <div class=\"skhema-skeleton-line medium\"></div>\n </div>\n </div>\n <div class=\"skhema-skeleton-content\">\n <div class=\"skhema-skeleton-line short\"></div>\n <div class=\"skhema-skeleton-line\"></div>\n <div class=\"skhema-skeleton-line short\"></div>\n <div class=\"skhema-skeleton-line\"></div>\n <div class=\"skhema-skeleton-line medium\"></div>\n </div>\n </div>\n\n <slot style=\"display:none;\"></slot>\n `\n }\n\n private renderError(title: string, errors: string | string[] | unknown) {\n const errorList = Array.isArray(errors) ? errors : [String(errors)]\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n\n <div class=\"skhema-component-card\">\n <div class=\"skhema-error\">\n <div class=\"skhema-error-title\">Skhema Component Error: ${title}</div>\n <ul class=\"skhema-error-list\">\n ${errorList.map((error) => `<li>${error}</li>`).join('')}\n </ul>\n </div>\n </div>\n\n <slot style=\"display:none;\"></slot>\n `\n\n this.dispatchEvent(\n new CustomEvent('skhema:error', {\n detail: { error: title, details: errors },\n bubbles: true,\n })\n )\n }\n\n private addStructuredData() {\n if (!this.contentData) return\n\n const { component_type, contributor_id, title, elements, source_url } =\n this.contentData\n\n const structuredData = generateComponentStructuredData(\n title,\n component_type,\n contributor_id,\n elements.map((el) => ({\n elementType: el.element_type,\n content: el.content,\n })),\n source_url\n )\n\n const script = document.createElement('script')\n script.type = 'application/ld+json'\n script.textContent = JSON.stringify(structuredData)\n script.className = 'skhema-structured-data'\n document.head.appendChild(script)\n }\n\n private async trackLoad() {\n if (\n !shouldTrackAnalytics(this as HTMLElement) ||\n !this.contentData ||\n this.hasTrackedLoad\n ) {\n return\n }\n\n this.hasTrackedLoad = true\n\n const analytics: ComponentEmbedAnalytics = {\n contributorId: this.contentData.contributor_id,\n componentType: this.contentData.component_type,\n componentHash: this.contentData.component_hash,\n title: this.contentData.title,\n elements: this.contentData.elements.map((el) => ({\n elementType: el.element_type,\n content: el.content,\n contentHash: el.content_hash,\n })),\n pageUrl: window.location.href,\n pageTitle: document.title,\n timestamp: Date.now(),\n userAgent: navigator.userAgent,\n }\n\n await trackComponentEmbedLoad(analytics)\n\n this.dispatchEvent(\n new CustomEvent('skhema:component-load', {\n detail: analytics,\n bubbles: true,\n })\n )\n }\n\n private async handleSaveClick(_event: Event) {\n if (!this.contentData) return\n\n if (shouldTrackAnalytics(this as HTMLElement)) {\n await trackComponentClick(this.contentData)\n }\n\n this.dispatchEvent(\n new CustomEvent('skhema:component-click', {\n detail: this.contentData,\n bubbles: true,\n })\n )\n }\n\n // Public API\n public getContentData(): ComponentContentData | null {\n return this.contentData\n }\n\n public refresh(): void {\n this.render()\n }\n\n private setupThemeListeners(): void {\n const themeAttribute = this.getAttribute('theme')\n if (themeAttribute === 'auto' || !themeAttribute) {\n if (window.matchMedia) {\n this.mediaQueryListener = window.matchMedia(\n '(prefers-color-scheme: dark)'\n )\n const handleThemeChange = () => this.updateTheme()\n this.mediaQueryListener.addEventListener('change', handleThemeChange)\n }\n\n this.themeObserver = new MutationObserver(() => this.updateTheme())\n\n this.themeObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme'],\n })\n\n this.themeObserver.observe(document.body, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme'],\n })\n }\n }\n\n private cleanupThemeListeners(): void {\n if (this.themeObserver) {\n this.themeObserver.disconnect()\n this.themeObserver = null\n }\n if (this.mediaQueryListener) {\n this.mediaQueryListener = null\n }\n }\n\n private updateTheme(): void {\n // Theme is baked into the rendered card's inline hex, so re-render rather\n // than flip an attribute.\n const themeAttribute = this.getAttribute('theme') || 'auto'\n if (themeAttribute === 'auto' && this.contentData) {\n this.scheduleRender()\n }\n }\n}\n\n// Type augmentation for custom events and JSX elements\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface HTMLElementEventMap extends SkhemaComponentEventMap {}\n\n interface SkhemaComponentJSX extends Partial<SkhemaComponentAttributes> {\n [key: string]: unknown\n }\n\n interface JSXIntrinsicElements {\n 'skhema-component': SkhemaComponentJSX\n }\n}\n","import { renderElementCardHtml } from '../render/index.js'\nimport {\n CARD_SHADOW_LG,\n PRIMARY_HEX,\n PRIMARY_HOVER_HEX,\n PRIMARY_PRESSED_HEX,\n} from '../styles/design-tokens.js'\nimport {\n shouldTrackAnalytics,\n trackClick,\n trackEmbedLoad,\n} from '../utils/analytics.js'\nimport { generateContentHash } from '../utils/hash.js'\nimport { validateContentSecurity } from '../utils/sanitization.js'\nimport {\n createAriaAttributes,\n createMetaTags,\n generateRedirectUrl,\n generateStructuredData,\n} from '../utils/seo.js'\nimport { validateAttributes } from '../utils/validation.js'\nimport type {\n ContentData,\n EmbedAnalytics,\n NestedElementData,\n SkhemaElementAttributes,\n SkhemaElementEventMap,\n} from './types.js'\n\n// Browser-only styles. The card STRUCTURE + base colours now come from the\n// shared, email-safe `@skhema/embed/render` output (inline hex), so this block\n// only carries: (1) the progressive enhancements email/CLI can't have — hover\n// + transitions over the rendered `.skhema-card` — and (2) the shadow-DOM-only\n// skeleton + error states (which keep the original CSS-var styling verbatim).\nconst styles = `\n:host {\n /* Skhema Brand Colors - matching UI library */\n --skhema-primary: hsl(344 57% 54%); /* #cd476a */\n --skhema-primary-hover: hsl(344 50% 47%); /* #b53d5e */\n --skhema-primary-pressed: hsl(343 50% 41%); /* #9d3552 */\n --skhema-secondary: hsl(345 100% 75%); /* #ff82a2 */\n\n /* Light mode colors */\n --skhema-bg: hsl(0 0% 100%);\n --skhema-card: hsl(0 0% 100%);\n --skhema-border: hsl(214.3 31.8% 91.4%);\n --skhema-text: hsl(222.2 84% 4.9%);\n --skhema-text-muted: hsl(215.4 16.3% 46.9%);\n --skhema-accent: hsl(210 40% 96%);\n\n /* Shadows matching UI library */\n --skhema-shadow: 0 1px 3px 0 hsl(0 0 0 / 0.1), 0 1px 2px -1px hsl(0 0 0 / 0.1);\n --skhema-shadow-md: 0 4px 6px -1px hsl(0 0 0 / 0.1), 0 2px 4px -2px hsl(0 0 0 / 0.1);\n --skhema-shadow-lg: 0 10px 15px -3px hsl(0 0 0 / 0.1), 0 4px 6px -4px hsl(0 0 0 / 0.1);\n --skhema-radius: 0.1rem;\n\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif;\n line-height: 1.5;\n color: var(--skhema-text);\n}\n\n/* Dark mode styles - applied via data-theme attribute (skeleton/error only) */\n.skhema-element-card[data-theme=\"dark\"],\n.skhema-skeleton[data-theme=\"dark\"] {\n --skhema-bg: hsl(222.2 84% 4.9%);\n --skhema-card: hsl(222.2 84% 4.9%);\n --skhema-border: hsl(217.2 32.6% 17.5%);\n --skhema-text: hsl(210 40% 98%);\n --skhema-text-muted: hsl(215 20.2% 65.1%);\n --skhema-accent: hsl(217.2 32.6% 17.5%);\n}\n\n/* Progressive enhancement over the inline-styled, rendered card. Inline styles\n win on specificity, so the changed properties use !important to layer on. */\n.skhema-card {\n transition: box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1), transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.skhema-card:hover {\n box-shadow: ${CARD_SHADOW_LG} !important;\n transform: translateY(-1px);\n}\n\n.skhema-card[data-skhema-kind=\"element\"]:hover {\n border-color: ${PRIMARY_HEX} !important;\n}\n\n.skhema-save-btn {\n transition: background 0.15s ease;\n}\n\n.skhema-save-btn:hover {\n background: ${PRIMARY_HOVER_HEX} !important;\n}\n\n.skhema-save-btn:active {\n background: ${PRIMARY_PRESSED_HEX} !important;\n}\n\n/* Error card wrapper (browser-only state) */\n.skhema-element-card {\n background: var(--skhema-card);\n border: 1px solid var(--skhema-border);\n border-radius: calc(var(--skhema-radius) + 2px);\n box-shadow: var(--skhema-shadow);\n max-width: 600px;\n margin: 8px 0;\n overflow: hidden;\n}\n\n/* Error state */\n.skhema-error {\n background: hsl(0 93% 94%);\n border: 1px solid hsl(0 84% 60%);\n color: hsl(0 74% 42%);\n padding: 12px;\n border-radius: calc(var(--skhema-radius) + 2px);\n font-size: 13px;\n}\n\n.skhema-error-title {\n font-weight: 600;\n margin-bottom: 8px;\n}\n\n.skhema-error-list {\n margin: 0;\n padding-left: 16px;\n}\n\n/* Skeleton loading state */\n.skhema-skeleton {\n background: var(--skhema-card);\n border: 1px solid var(--skhema-border);\n border-radius: calc(var(--skhema-radius) + 2px);\n padding: 16px;\n box-shadow: var(--skhema-shadow);\n max-width: 600px;\n margin: 8px 0;\n animation: skeletonPulse 1.5s ease-in-out infinite;\n}\n\n.skhema-skeleton-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 12px;\n}\n\n.skhema-skeleton-badge {\n width: 32px;\n height: 20px;\n border-radius: 2px;\n background: linear-gradient(90deg,\n var(--skhema-border) 25%,\n var(--skhema-accent) 50%,\n var(--skhema-border) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n}\n\n.skhema-skeleton-text {\n flex: 1;\n}\n\n.skhema-skeleton-line {\n height: 12px;\n background: linear-gradient(90deg,\n var(--skhema-border) 25%,\n var(--skhema-accent) 50%,\n var(--skhema-border) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n border-radius: calc(var(--skhema-radius) + 2px);\n margin: 6px 0;\n}\n\n.skhema-skeleton-line.short {\n width: 40%;\n}\n\n.skhema-skeleton-line.medium {\n width: 70%;\n}\n\n.skhema-skeleton-content {\n margin: 16px 0;\n}\n\n@keyframes skeletonPulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.8;\n }\n}\n\n@keyframes shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n\n/* Accessibility */\n@media (prefers-reduced-motion: reduce) {\n .skhema-card,\n .skhema-save-btn {\n transition: none;\n }\n}\n\n.skhema-structured-data {\n display: none !important;\n}\n`\n\nexport class SkhemaElement extends HTMLElement {\n private shadow: ShadowRoot\n private contentData: ContentData | null = null\n private componentConnected = false\n private hasTrackedLoad = false\n private nestedMode = false\n private themeObserver: MutationObserver | null = null\n private mediaQueryListener: MediaQueryList | null = null\n\n constructor() {\n super()\n this.shadow = this.attachShadow({ mode: 'closed' })\n // Show skeleton immediately while component initializes\n this.renderSkeleton()\n }\n\n static get observedAttributes(): (keyof SkhemaElementAttributes)[] {\n return [\n 'element-type',\n 'contributor-id',\n 'author-name',\n 'author-slug',\n 'contributor-name',\n 'content',\n 'source-url',\n 'theme',\n 'track-analytics',\n ]\n }\n\n connectedCallback() {\n if (this.componentConnected) return\n this.componentConnected = true\n\n // Check if nested inside a <skhema-component>\n if (this.closest('skhema-component')) {\n this.nestedMode = true\n\n // Validate and parse content\n const validation = validateAttributes(this as HTMLElement)\n if (!validation.isValid) {\n console.warn(\n 'skhema-element: invalid attributes in nested mode',\n validation.errors\n )\n return\n }\n\n const content = this.getContent()\n if (!content.trim()) {\n console.warn('skhema-element: empty content in nested mode')\n return\n }\n\n this.contentData = {\n contributor_id: validation.contributorId!,\n element_type: validation.elementType!,\n content: content,\n content_hash: generateContentHash(content),\n source_url: this.getAttribute('source-url') || window.location.href,\n timestamp: new Date().toISOString(),\n page_title: document.title,\n }\n\n // Clear shadow DOM — parent component handles rendering\n this.shadow.innerHTML = ''\n\n // Dispatch ready event so parent component can discover us\n this.dispatchEvent(\n new CustomEvent('skhema:element-ready', {\n bubbles: true,\n composed: true,\n detail: this.getElementData(),\n })\n )\n return\n }\n\n try {\n // Add preconnect hints for faster analytics loading\n this.addPreconnectHints()\n\n // Use requestAnimationFrame for smoother render\n requestAnimationFrame(() => {\n this.render()\n this.trackLoad()\n this.setupThemeListeners()\n })\n } catch (error) {\n this.renderError('Failed to initialize component', error)\n }\n }\n\n disconnectedCallback() {\n this.cleanupThemeListeners()\n }\n\n attributeChangedCallback(\n _name: keyof SkhemaElementAttributes,\n oldValue: string | null,\n newValue: string | null\n ) {\n if (oldValue !== newValue && this.componentConnected && !this.nestedMode) {\n this.render()\n }\n }\n\n /**\n * Returns element data for parent <skhema-component> consumption.\n */\n public getElementData(): NestedElementData | null {\n if (!this.contentData) return null\n return {\n elementType: this.contentData.element_type,\n content: this.contentData.content,\n contentHash: this.contentData.content_hash,\n }\n }\n\n private render() {\n const validation = validateAttributes(this as HTMLElement)\n\n if (!validation.isValid) {\n this.renderError('Invalid component attributes', validation.errors)\n return\n }\n\n const content = this.getContent()\n if (!content.trim()) {\n this.renderError('Component requires content', [\n 'Add content between the opening and closing tags, or use the content attribute',\n ])\n return\n }\n\n // Validate content security\n const securityValidation = validateContentSecurity(content)\n if (!securityValidation.isSecure) {\n this.renderError(\n 'Content security validation failed',\n securityValidation.issues\n )\n return\n }\n\n this.contentData = {\n contributor_id: validation.contributorId!,\n element_type: validation.elementType!,\n content: content,\n content_hash: generateContentHash(content),\n source_url: this.getAttribute('source-url') || window.location.href,\n timestamp: new Date().toISOString(),\n page_title: document.title,\n }\n\n this.renderContent()\n this.addStructuredData()\n }\n\n private getContent(): string {\n return this.getAttribute('content') || this.textContent || ''\n }\n\n private renderContent() {\n if (!this.contentData) return\n\n const { element_type, contributor_id, content } = this.contentData\n const redirectUrl = generateRedirectUrl(\n content,\n element_type,\n contributor_id\n )\n\n // Determine the actual theme to use\n const themeAttribute = this.getAttribute('theme') || 'auto'\n const actualTheme = this.getActualTheme(themeAttribute)\n\n // Set ARIA attributes on host element\n const ariaAttrs = createAriaAttributes(element_type)\n Object.entries(ariaAttrs).forEach(([key, value]) => {\n this.setAttribute(key, value)\n })\n\n // Card structure + base styling come from the shared, email-safe renderer\n // (single source of truth). The browser-only `<style>` layers hover /\n // transitions on top.\n const cardHtml = renderElementCardHtml({\n elementType: element_type,\n content,\n saveUrl: redirectUrl,\n authorName:\n this.getAttribute('author-name') ||\n this.getAttribute('contributor-name'),\n authorSlug: this.getAttribute('author-slug'),\n contributorId: contributor_id,\n theme: actualTheme,\n })\n\n this.shadow.innerHTML = `<style>${styles}</style>${cardHtml}`\n\n // Add click event listener\n const saveBtn = this.shadow.querySelector(\n '.skhema-save-btn'\n ) as HTMLAnchorElement\n if (saveBtn) {\n saveBtn.addEventListener('click', (event) => {\n this.handleSaveClick(event)\n })\n }\n }\n\n private getActualTheme(themeAttribute: string): 'light' | 'dark' {\n if (themeAttribute === 'light' || themeAttribute === 'dark') {\n return themeAttribute\n }\n\n // Auto mode - detect from system/page\n // First check if the page has set a data-theme or theme attribute\n const htmlElement = document.documentElement\n const bodyElement = document.body\n\n // Check common theme attributes on html or body\n const htmlTheme =\n htmlElement.getAttribute('data-theme') ||\n htmlElement.getAttribute('theme') ||\n htmlElement.className.match(/theme-(\\w+)/)?.[1]\n\n const bodyTheme =\n bodyElement.getAttribute('data-theme') ||\n bodyElement.getAttribute('theme') ||\n bodyElement.className.match(/theme-(\\w+)/)?.[1]\n\n // Check for dark mode classes\n const hasDarkClass =\n htmlElement.classList.contains('dark') ||\n bodyElement.classList.contains('dark') ||\n htmlElement.classList.contains('dark-mode') ||\n bodyElement.classList.contains('dark-mode')\n\n if (hasDarkClass || htmlTheme === 'dark' || bodyTheme === 'dark') {\n return 'dark'\n }\n\n // Check CSS custom properties that might indicate theme\n const computedStyles = window.getComputedStyle(htmlElement)\n const colorScheme = computedStyles.getPropertyValue('color-scheme')\n if (colorScheme && colorScheme.includes('dark')) {\n return 'dark'\n }\n\n // Check system preference\n if (\n window.matchMedia &&\n window.matchMedia('(prefers-color-scheme: dark)').matches\n ) {\n return 'dark'\n }\n\n // Default to light\n return 'light'\n }\n\n private addPreconnectHints() {\n // Only add preconnect hints once per page\n if (\n document.querySelector(\n 'link[rel=\"preconnect\"][href*=\"analytics.skhema.com\"]'\n )\n ) {\n return\n }\n\n try {\n // Preconnect to analytics API\n const preconnectApi = document.createElement('link')\n preconnectApi.rel = 'preconnect'\n preconnectApi.href = 'https://analytics.skhema.com'\n document.head.appendChild(preconnectApi)\n\n // DNS prefetch for main domain\n const dnsPrefetch = document.createElement('link')\n dnsPrefetch.rel = 'dns-prefetch'\n dnsPrefetch.href = 'https://skhema.com'\n document.head.appendChild(dnsPrefetch)\n } catch (error) {\n console.debug('Failed to add preconnect hints:', error)\n }\n }\n\n private renderSkeleton() {\n // Determine theme for skeleton\n const themeAttribute = this.getAttribute('theme') || 'auto'\n const actualTheme = this.getActualTheme(themeAttribute)\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n\n <div class=\"skhema-skeleton\" data-theme=\"${actualTheme}\">\n <div class=\"skhema-skeleton-header\">\n <div class=\"skhema-skeleton-badge\"></div>\n <div class=\"skhema-skeleton-text\">\n <div class=\"skhema-skeleton-line medium\"></div>\n </div>\n </div>\n <div class=\"skhema-skeleton-content\">\n <div class=\"skhema-skeleton-line\"></div>\n <div class=\"skhema-skeleton-line\"></div>\n <div class=\"skhema-skeleton-line medium\"></div>\n </div>\n </div>\n `\n }\n\n private renderError(title: string, errors: string | string[] | unknown) {\n const errorList = Array.isArray(errors) ? errors : [String(errors)]\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n\n <div class=\"skhema-element-card\">\n <div class=\"skhema-error\">\n <div class=\"skhema-error-title\">Skhema Component Error: ${title}</div>\n <ul class=\"skhema-error-list\">\n ${errorList.map((error) => `<li>${error}</li>`).join('')}\n </ul>\n </div>\n </div>\n `\n\n // Dispatch error event\n this.dispatchEvent(\n new CustomEvent('skhema:error', {\n detail: { error: title, details: errors },\n bubbles: true,\n })\n )\n }\n\n private addStructuredData() {\n if (!this.contentData) return\n\n const { content, element_type, contributor_id, source_url } =\n this.contentData\n\n // Add structured data to the document head\n const structuredData = generateStructuredData(\n content,\n element_type,\n contributor_id,\n source_url\n )\n const script = document.createElement('script')\n script.type = 'application/ld+json'\n script.textContent = JSON.stringify(structuredData)\n script.className = 'skhema-structured-data'\n document.head.appendChild(script)\n\n // Add meta tags for SEO\n const metaDiv = document.createElement('div')\n metaDiv.innerHTML = createMetaTags(content, element_type, contributor_id)\n metaDiv.className = 'skhema-structured-data'\n document.body.appendChild(metaDiv)\n }\n\n private async trackLoad() {\n if (\n !shouldTrackAnalytics(this as HTMLElement) ||\n !this.contentData ||\n this.hasTrackedLoad\n ) {\n return\n }\n\n this.hasTrackedLoad = true\n\n const analytics: EmbedAnalytics = {\n contributorId: this.contentData.contributor_id,\n elementType: this.contentData.element_type,\n contentHash: this.contentData.content_hash,\n content: this.contentData.content,\n pageUrl: window.location.href,\n pageTitle: document.title,\n timestamp: Date.now(),\n userAgent: navigator.userAgent,\n }\n\n await trackEmbedLoad(analytics)\n\n // Dispatch load event\n this.dispatchEvent(\n new CustomEvent('skhema:load', {\n detail: analytics,\n bubbles: true,\n })\n )\n }\n\n private async handleSaveClick(_event: Event) {\n if (!this.contentData) return\n\n // Track click analytics\n if (shouldTrackAnalytics(this as HTMLElement)) {\n await trackClick(this.contentData)\n }\n\n // Dispatch click event\n this.dispatchEvent(\n new CustomEvent('skhema:click', {\n detail: this.contentData,\n bubbles: true,\n })\n )\n }\n\n // Public API methods\n public getContentData(): ContentData | null {\n return this.contentData\n }\n\n public refresh(): void {\n if (!this.nestedMode) {\n this.render()\n }\n }\n\n private setupThemeListeners(): void {\n // Only set up listeners if theme is 'auto' or not specified\n const themeAttribute = this.getAttribute('theme')\n if (themeAttribute === 'auto' || !themeAttribute) {\n // Listen for system theme changes\n if (window.matchMedia) {\n this.mediaQueryListener = window.matchMedia(\n '(prefers-color-scheme: dark)'\n )\n const handleThemeChange = () => this.updateTheme()\n this.mediaQueryListener.addEventListener('change', handleThemeChange)\n }\n\n // Listen for theme changes on html/body elements\n this.themeObserver = new MutationObserver(() => this.updateTheme())\n\n // Observe both html and body for attribute changes\n this.themeObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme'],\n })\n\n this.themeObserver.observe(document.body, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme'],\n })\n }\n }\n\n private cleanupThemeListeners(): void {\n if (this.themeObserver) {\n this.themeObserver.disconnect()\n this.themeObserver = null\n }\n\n if (this.mediaQueryListener) {\n // Note: We can't remove the specific listener without storing the reference\n // but setting to null will allow garbage collection\n this.mediaQueryListener = null\n }\n }\n\n private updateTheme(): void {\n // Only update if component is using auto theme. Theme is now baked into the\n // rendered card's inline hex, so re-render rather than flip an attribute.\n const themeAttribute = this.getAttribute('theme') || 'auto'\n if (themeAttribute === 'auto' && this.contentData && !this.nestedMode) {\n this.renderContent()\n }\n }\n}\n\n// Type augmentation for custom events and JSX elements\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface HTMLElementEventMap extends SkhemaElementEventMap {}\n\n interface SkhemaElementJSX extends Partial<SkhemaElementAttributes> {\n [key: string]: unknown\n }\n\n // Module augmentation for JSX without using namespace\n interface JSXIntrinsicElements {\n 'skhema-element': SkhemaElementJSX\n }\n}\n","// CDN bundle entry point - automatically registers the components\nimport { SkhemaComponent } from './components/SkhemaComponent.js'\nimport { SkhemaElement } from './components/SkhemaElement.js'\n\n// Register the custom elements for CDN usage\nif (!customElements.get('skhema-element')) {\n customElements.define(\n 'skhema-element',\n SkhemaElement as CustomElementConstructor\n )\n}\n\nif (!customElements.get('skhema-component')) {\n customElements.define(\n 'skhema-component',\n SkhemaComponent as CustomElementConstructor\n )\n}\n\n// Expose on global for UMD builds\nif (typeof window !== 'undefined') {\n ;(\n window as unknown as {\n SkhemaElement: typeof SkhemaElement\n SkhemaComponent: typeof SkhemaComponent\n }\n ).SkhemaElement = SkhemaElement\n ;(\n window as unknown as {\n SkhemaElement: typeof SkhemaElement\n SkhemaComponent: typeof SkhemaComponent\n }\n ).SkhemaComponent = SkhemaComponent\n}\n\nexport { SkhemaComponent, SkhemaElement }\n"],"names":["COMPONENT_COLORS","diagnosis","bg","text","border","method","initiatives","measures","support","CARD_PALETTE","light","cardBg","textMuted","dark","PRIMARY_HEX","PRIMARY_HOVER_HEX","PRIMARY_PRESSED_HEX","CARD_SHADOW_LG","MONO_STACK","oklchToHex","token","overHex","m","match","L","parseFloat","C","H","alpha","h","Math","PI","a","cos","b","sin","lr","mr","sr","G","B","gamma","x","c","max","min","pow","rgb","br","bb","hex","replace","parseInt","slice","hexToRgb","map","round","toString","padStart","join","podEntry","description","domain","entityType","source","webhookExposed","exposed","EVENT_REGISTRY","Object","entries","filter","entry","type","COMPONENT_TYPES","DIAGNOSIS","value","label","acronym","METHOD","INITIATIVES","MEASURES","SUPPORT","ELEMENT_TYPES","KEY_CHALLENGE","SUPPORTING_FACT","IMPACT","GUIDING_POLICY","COMPETITOR_MOVE","SCOPE","CONSTRAINT","SOLUTION","ASSUMPTION_HYPOTHESIS","EXPERIMENT","ACTION","INVESTMENT","ESTIMATE","BASELINE","OUTCOME","PERFORMANCE_VARIABLE","CAPABILITY","SYSTEM","PRINCIPLE","SKHEMA_MAPPING","elementFlow","isValidComponentType","componentType","values","t","includes","validateElementBelongsToComponent","elementType","validElements","some","e","getComponentTypeLabel","find","getComponentTypeAcronym","substring","toUpperCase","sanitizeContent","content","sanitized","stripped","forEach","pattern","trim","stripUrls","split","word","test","length","isValidElementType","validateAttributes","element","errors","getAttribute","contributorId","validTypes","push","isValid","getElementTypeLabel","escapeHtml","escapeAttr","resolveBadgePalette","theme","surface","colors","badgeBg","badgeText","badgeBorder","topBorder","renderFooter","saveUrl","author","p","mutedColor","authorName","name","authorSlug","encodeURIComponent","charAt","toLowerCase","renderAuthorHtml","renderHeader","badge","typeLabel","labelColor","title","titleHtml","cardOpen","kind","renderElementCardHtml","data","componentValue","elements","resolveComponentType","ANALYTICS_ENDPOINT","getTrackedEmbeds","cookie","document","row","startsWith","JSON","parse","decodeURIComponent","cutoff","Date","now","TRACKING_EXPIRY_HOURS","item","timestamp","hasBeenTracked","contentHash","markAsTracked","tracked","limited","expires","stringify","toUTCString","setTrackedEmbeds","analyticsBatcher","constructor","this","batch","embeds","clicks","componentEmbeds","componentClicks","batchTimeout","BATCH_DELAY","MAX_BATCH_SIZE","addEmbedLoad","analytics","scheduleBatchSend","addClick","contentData","addComponentEmbedLoad","addComponentClick","clearTimeout","sendBatch","window","setTimeout","currentBatch","sendEmbeds","sendClicks","sendComponentEmbeds","sendComponentClicks","payload","action","events","embed","contributor_id","element_type","content_hash","page_url","pageUrl","page_title","pageTitle","user_agent","userAgent","sendWithRetry","error","console","debug","click","source_url","fetch","headers","body","credentials","keepalive","catch","component_type","component_hash","componentHash","el","flush","sendViaBeacon","navigator","sendBeacon","Blob","async","url","contentType","maxRetries","attempt","options","response","ok","status","Promise","resolve","shouldTrackAnalytics","generateContentHash","hash","cleanContent","i","charCodeAt","abs","generateRedirectUrl","baseUrl","sourceUrl","location","href","URLSearchParams","utm_source","utmSource","utm_medium","utmMedium","utm_campaign","utmCampaign","utm_content","generateComponentRedirectUrl","addEventListener","visibilityState","styles","SkhemaComponent","HTMLElement","super","componentConnected","hasTrackedLoad","pendingRender","themeObserver","mediaQueryListener","shadow","attachShadow","mode","renderSkeleton","observedAttributes","connectedCallback","addPreconnectHints","scheduleRender","customElements","whenDefined","then","setupThemeListeners","renderError","disconnectedCallback","cleanupThemeListeners","cancelAnimationFrame","attributeChangedCallback","_name","oldValue","newValue","requestAnimationFrame","render","validation","validateComponentAttributes","childElements","gatherChildElements","sort","typeCompare","localeCompare","toISOString","renderContent","addStructuredData","trackLoad","children","Array","from","querySelectorAll","child","getElementData","warn","orderedTypes","getElementTypesForComponent","aIdx","indexOf","bIdx","componentLabel","themeAttribute","actualTheme","getActualTheme","redirectUrl","setAttribute","cardHtml","groups","Map","existing","get","set","groupsHtml","contents","renderComponentCardHtml","innerHTML","saveBtn","querySelector","event","handleSaveClick","htmlElement","documentElement","bodyElement","htmlTheme","className","bodyTheme","classList","contains","colorScheme","getComputedStyle","getPropertyValue","matchMedia","matches","preconnectApi","createElement","rel","head","appendChild","dnsPrefetch","errorList","isArray","String","dispatchEvent","CustomEvent","detail","details","bubbles","structuredData","category","contributor","hasPart","analysisType","provider","isPartOf","dateCreated","platform","generateComponentStructuredData","script","textContent","trackComponentEmbedLoad","_event","trackComponentClick","getContentData","refresh","handleThemeChange","updateTheme","MutationObserver","observe","attributes","attributeFilter","disconnect","SkhemaElement","nestedMode","closest","getContent","composed","securityValidation","issues","isSecure","validateContentSecurity","ariaAttrs","role","key","generateStructuredData","metaDiv","createMetaTags","trackEmbedLoad","trackClick","define"],"mappings":"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"}
1
+ {"version":3,"file":"embed.min.js","sources":["../src/styles/design-tokens.ts","../src/utils/color.ts","../../skhema-method/dist/vocabulary/components.js","../../skhema-method/dist/vocabulary/elements.js","../../skhema-method/dist/vocabulary/mapping.js","../src/utils/component-validation.ts","../src/utils/sanitization.ts","../src/utils/validation.ts","../src/render/index.ts","../src/utils/analytics.ts","../src/utils/hash.ts","../src/utils/seo.ts","../src/components/SkhemaComponent.ts","../src/components/SkhemaElement.ts","../src/cdn.ts"],"sourcesContent":["/**\n * Hardcoded color tokens for component-type badge colors.\n * Embeds can't rely on host page CSS vars, so we use inline values.\n * Source: skhema-web/src/app/globals.css oklch tokens converted to HSL-ish for browser compat.\n */\n\nexport const COMPONENT_COLORS = {\n diagnosis: {\n bg: 'oklch(0.65 0.06 25 / 0.15)',\n text: 'oklch(0.65 0.06 25)',\n border: 'oklch(0.65 0.06 25 / 0.3)',\n },\n method: {\n bg: 'oklch(0.6 0.06 250 / 0.15)',\n text: 'oklch(0.6 0.06 250)',\n border: 'oklch(0.6 0.06 250 / 0.3)',\n },\n initiatives: {\n bg: 'oklch(0.6 0.06 155 / 0.15)',\n text: 'oklch(0.6 0.06 155)',\n border: 'oklch(0.6 0.06 155 / 0.3)',\n },\n measures: {\n bg: 'oklch(0.6 0.06 300 / 0.15)',\n text: 'oklch(0.6 0.06 300)',\n border: 'oklch(0.6 0.06 300 / 0.3)',\n },\n support: {\n bg: 'oklch(0.65 0.06 65 / 0.15)',\n text: 'oklch(0.65 0.06 65)',\n border: 'oklch(0.65 0.06 65 / 0.3)',\n },\n} as const\n\nexport type ComponentColorKey = keyof typeof COMPONENT_COLORS\n\n/**\n * Email-safe flat hex palette for the card surface, per theme. These mirror the\n * Tailwind slate values behind the `CARD_VARS` HSL tokens, pre-resolved to hex\n * because email clients ignore `<style>` / CSS vars. The card renderer\n * (`@skhema/embed/render`) inlines these so the live browser embed and email\n * share one colour system (see the \"converge on hex\" decision).\n */\nexport const CARD_PALETTE = {\n light: {\n cardBg: '#ffffff', // hsl(0 0% 100%)\n border: '#e2e8f0', // hsl(214.3 31.8% 91.4%) — slate-200\n text: '#020817', // hsl(222.2 84% 4.9%) — slate-950\n textMuted: '#64748b', // hsl(215.4 16.3% 46.9%) — slate-500\n },\n dark: {\n cardBg: '#020817', // hsl(222.2 84% 4.9%) — slate-950\n border: '#1e293b', // hsl(217.2 32.6% 17.5%) — slate-800\n text: '#f8fafc', // hsl(210 40% 98%) — slate-50\n textMuted: '#94a3b8', // hsl(215 20.2% 65.1%) — slate-400\n },\n} as const\n\nexport type CardTheme = keyof typeof CARD_PALETTE\n\n/** Save-button brand colours (hex) — match the `--skhema-primary*` HSL tokens. */\nexport const PRIMARY_HEX = '#cd476a' // hsl(344 57% 54%)\nexport const PRIMARY_HOVER_HEX = '#b53d5e' // hsl(344 50% 47%)\nexport const PRIMARY_PRESSED_HEX = '#9d3552' // hsl(343 50% 41%)\n\n/** Card geometry / typography shared by the renderer and the browser embed. */\nexport const CARD_RADIUS = '4px' // calc(0.1rem + 2px) ≈ 3.6px\nexport const CARD_SHADOW =\n '0 1px 3px 0 rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1)'\nexport const CARD_SHADOW_LG =\n '0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)'\nexport const FONT_STACK =\n \"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif\"\nexport const MONO_STACK =\n \"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace\"\n\n/**\n * Shared base card CSS variables for light and dark modes.\n */\nexport const CARD_VARS = {\n light: {\n '--skhema-bg': 'hsl(0 0% 100%)',\n '--skhema-card': 'hsl(0 0% 100%)',\n '--skhema-border': 'hsl(214.3 31.8% 91.4%)',\n '--skhema-text': 'hsl(222.2 84% 4.9%)',\n '--skhema-text-muted': 'hsl(215.4 16.3% 46.9%)',\n '--skhema-accent': 'hsl(210 40% 96%)',\n '--skhema-surface-subtle': 'hsl(210 40% 97%)',\n },\n dark: {\n '--skhema-bg': 'hsl(222.2 84% 4.9%)',\n '--skhema-card': 'hsl(222.2 84% 4.9%)',\n '--skhema-border': 'hsl(217.2 32.6% 17.5%)',\n '--skhema-text': 'hsl(210 40% 98%)',\n '--skhema-text-muted': 'hsl(215 20.2% 65.1%)',\n '--skhema-accent': 'hsl(217.2 32.6% 17.5%)',\n '--skhema-surface-subtle': 'hsl(217.2 32.6% 12%)',\n },\n} as const\n\n/**\n * User SVG icon (inline, no external deps).\n */\nexport const USER_ICON_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2\"/><circle cx=\"12\" cy=\"7\" r=\"4\"/></svg>`\n","/**\n * DOM-free colour utilities for the email-safe card renderer.\n *\n * The card palette (`COMPONENT_COLORS`) is authored in `oklch()` for the live\n * browser embed, but email clients can't parse `oklch()` and `<style>` is\n * stripped, so every colour must be a pre-computed sRGB hex with all styles\n * inlined. This converts a single `oklch(...)` token to hex, compositing any\n * alpha over a flat background (white for light cards, the dark card surface\n * for dark cards) so the result is opaque and email-safe.\n *\n * This is the single source of the conversion that `sk comms curated` and the\n * `CuratedElements` email template previously duplicated.\n */\n\n/** Brand pink (hsl(344 57% 54%)) — fallback when a component palette is missing. */\nexport const BRAND_PINK = '#cd476a'\n\nfunction hexToRgb(hex: string): [number, number, number] {\n const h = hex.replace('#', '')\n return [\n parseInt(h.slice(0, 2), 16),\n parseInt(h.slice(2, 4), 16),\n parseInt(h.slice(4, 6), 16),\n ]\n}\n\n/**\n * Convert a single `oklch(...)` token to an email-safe sRGB hex. When the token\n * carries an alpha (e.g. the badge `bg` @ 0.15 / `border` @ 0.3), the colour is\n * composited over `overHex` so the result is a flat opaque hex.\n *\n * @param token An `oklch(L C H)` or `oklch(L C H / A)` string.\n * @param overHex The flat background to composite alpha over (default white).\n */\nexport function oklchToHex(token: string, overHex = '#ffffff'): string {\n const m = token.match(\n /oklch\\(\\s*([\\d.]+)\\s+([\\d.]+)\\s+([\\d.]+)\\s*(?:\\/\\s*([\\d.]+)\\s*)?\\)/\n )\n if (!m) return BRAND_PINK\n const L = parseFloat(m[1])\n const C = parseFloat(m[2])\n const H = parseFloat(m[3])\n const alpha = m[4] !== undefined ? parseFloat(m[4]) : 1\n const h = (H * Math.PI) / 180\n const a = C * Math.cos(h)\n const b = C * Math.sin(h)\n const l_ = L + 0.3963377774 * a + 0.2158037573 * b\n const m_ = L - 0.1055613458 * a - 0.0638541728 * b\n const s_ = L - 0.0894841775 * a - 1.291485548 * b\n const lr = l_ ** 3\n const mr = m_ ** 3\n const sr = s_ ** 3\n const R = 4.0767416621 * lr - 3.3077115913 * mr + 0.2309699292 * sr\n const G = -1.2684380046 * lr + 2.6097574011 * mr - 0.3413193965 * sr\n const B = -0.0041960863 * lr - 0.7034186147 * mr + 1.707614701 * sr\n const gamma = (x: number): number => {\n const c = Math.max(0, Math.min(1, x))\n return c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055\n }\n let rgb = [gamma(R) * 255, gamma(G) * 255, gamma(B) * 255]\n if (alpha < 1) {\n const [br, bg, bb] = hexToRgb(overHex)\n rgb = [\n rgb[0] * alpha + br * (1 - alpha),\n rgb[1] * alpha + bg * (1 - alpha),\n rgb[2] * alpha + bb * (1 - alpha),\n ]\n }\n return (\n '#' + rgb.map((c) => Math.round(c).toString(16).padStart(2, '0')).join('')\n )\n}\n","export const COMPONENT_TYPES = {\n DIAGNOSIS: {\n value: 'diagnosis',\n label: 'Diagnosis',\n acronym: 'BD',\n },\n METHOD: {\n value: 'method',\n label: 'Method & Positioning',\n acronym: 'MP',\n },\n INITIATIVES: {\n value: 'initiatives',\n label: 'Portfolio of Initiatives',\n acronym: 'PI',\n },\n MEASURES: {\n value: 'measures',\n label: 'Measures',\n acronym: 'M',\n },\n SUPPORT: {\n value: 'support',\n label: 'Support Structures',\n acronym: 'SS',\n },\n};\n","export const ELEMENT_TYPES = {\n KEY_CHALLENGE: {\n value: 'key_challenge',\n label: 'Key Challenge',\n acronym: 'CHL',\n },\n SUPPORTING_FACT: {\n value: 'supporting_fact',\n label: 'Supporting Fact',\n acronym: 'FCT',\n },\n IMPACT: {\n value: 'impact',\n label: 'Impact',\n acronym: 'IMP',\n },\n GUIDING_POLICY: {\n value: 'guiding_policy',\n label: 'Guiding Policy',\n acronym: 'POL',\n },\n COMPETITOR_MOVE: {\n value: 'competitor_move',\n label: 'Competitor Move',\n acronym: 'CMV',\n },\n SCOPE: {\n value: 'scope',\n label: 'Scope',\n acronym: 'SCP',\n },\n CONSTRAINT: {\n value: 'constraint',\n label: 'Constraint',\n acronym: 'CST',\n },\n SOLUTION: {\n value: 'solution',\n label: 'Solution',\n acronym: 'SOL',\n },\n ASSUMPTION_HYPOTHESIS: {\n value: 'assumption_hypothesis',\n label: 'Assumption Hypothesis',\n acronym: 'HYP',\n },\n EXPERIMENT: {\n value: 'experiment',\n label: 'Experiment',\n acronym: 'EXP',\n },\n ACTION: {\n value: 'action',\n label: 'Action',\n acronym: 'ACT',\n },\n INVESTMENT: {\n value: 'investment',\n label: 'Investment',\n acronym: 'INV',\n },\n ESTIMATE: {\n value: 'estimate',\n label: 'Estimate',\n acronym: 'EST',\n },\n BASELINE: {\n value: 'baseline',\n label: 'Baseline',\n acronym: 'BSL',\n },\n OUTCOME: {\n value: 'outcome',\n label: 'Outcome',\n acronym: 'OCM',\n },\n PERFORMANCE_VARIABLE: {\n value: 'performance_variable',\n label: 'Performance Variable',\n acronym: 'VAR',\n },\n CAPABILITY: {\n value: 'capability',\n label: 'Capability',\n acronym: 'CAP',\n },\n SYSTEM: {\n value: 'system',\n label: 'System',\n acronym: 'SYS',\n },\n PRINCIPLE: {\n value: 'principle',\n label: 'Principle',\n acronym: 'PRN',\n },\n};\n","import { COMPONENT_TYPES } from './components.js';\nimport { ELEMENT_TYPES } from './elements.js';\nexport const SKHEMA_MAPPING = {\n componentFlow: [\n COMPONENT_TYPES.DIAGNOSIS,\n COMPONENT_TYPES.METHOD,\n COMPONENT_TYPES.INITIATIVES,\n COMPONENT_TYPES.MEASURES,\n COMPONENT_TYPES.SUPPORT,\n ],\n elementFlow: {\n [COMPONENT_TYPES.DIAGNOSIS.value]: [\n ELEMENT_TYPES.KEY_CHALLENGE,\n ELEMENT_TYPES.SUPPORTING_FACT,\n ELEMENT_TYPES.IMPACT,\n ],\n [COMPONENT_TYPES.METHOD.value]: [\n ELEMENT_TYPES.GUIDING_POLICY,\n ELEMENT_TYPES.COMPETITOR_MOVE,\n ELEMENT_TYPES.SCOPE,\n ELEMENT_TYPES.CONSTRAINT,\n ],\n [COMPONENT_TYPES.INITIATIVES.value]: [\n ELEMENT_TYPES.SOLUTION,\n ELEMENT_TYPES.ASSUMPTION_HYPOTHESIS,\n ELEMENT_TYPES.EXPERIMENT,\n ELEMENT_TYPES.ACTION,\n ELEMENT_TYPES.ESTIMATE,\n ELEMENT_TYPES.INVESTMENT,\n ],\n [COMPONENT_TYPES.MEASURES.value]: [\n ELEMENT_TYPES.BASELINE,\n ELEMENT_TYPES.OUTCOME,\n ELEMENT_TYPES.PERFORMANCE_VARIABLE,\n ],\n [COMPONENT_TYPES.SUPPORT.value]: [\n ELEMENT_TYPES.CAPABILITY,\n ELEMENT_TYPES.SYSTEM,\n ELEMENT_TYPES.PRINCIPLE,\n ],\n },\n};\n/**\n * Defines which component types each type can link to for decision context.\n * This represents the upstream dependencies in the challenge-based strategy chain.\n * Diagnosis is always the root (no upstream context).\n */\nexport const COMPONENT_CONTEXT_SOURCES = {\n [COMPONENT_TYPES.DIAGNOSIS.value]: [],\n [COMPONENT_TYPES.METHOD.value]: [COMPONENT_TYPES.DIAGNOSIS.value],\n [COMPONENT_TYPES.INITIATIVES.value]: [\n COMPONENT_TYPES.DIAGNOSIS.value,\n COMPONENT_TYPES.METHOD.value,\n ],\n [COMPONENT_TYPES.MEASURES.value]: [\n COMPONENT_TYPES.DIAGNOSIS.value,\n COMPONENT_TYPES.METHOD.value,\n COMPONENT_TYPES.INITIATIVES.value,\n ],\n [COMPONENT_TYPES.SUPPORT.value]: [\n COMPONENT_TYPES.DIAGNOSIS.value,\n COMPONENT_TYPES.METHOD.value,\n COMPONENT_TYPES.INITIATIVES.value,\n ],\n};\nexport function getContextSourcesForComponent(componentValue) {\n return COMPONENT_CONTEXT_SOURCES[componentValue] ?? [];\n}\nexport function canLinkToComponentType(sourceComponentType, targetComponentType) {\n const sources = getContextSourcesForComponent(sourceComponentType);\n return sources.includes(targetComponentType);\n}\nexport function getElementsForComponent(componentValue) {\n return SKHEMA_MAPPING.elementFlow[componentValue];\n}\nexport function isValidElementForComponent(element, componentValue) {\n const validElements = getElementsForComponent(componentValue);\n return validElements.includes(element);\n}\nexport function findComponentByValue(value) {\n return Object.values(COMPONENT_TYPES).find((component) => component.value === value);\n}\nexport function findElementByValue(value) {\n return Object.values(ELEMENT_TYPES).find((element) => element.value === value);\n}\n","import type { ComponentValue, ElementValue } from '@skhema/method/vocabulary'\nimport { COMPONENT_TYPES, SKHEMA_MAPPING } from '@skhema/method/vocabulary'\n\n/**\n * Validate that a string is a valid component type.\n */\nexport function isValidComponentType(\n componentType: string\n): componentType is ComponentValue {\n const validTypes = Object.values(COMPONENT_TYPES).map((t) => t.value)\n return validTypes.includes(componentType as ComponentValue)\n}\n\n/**\n * Validate that an element type belongs to the given component type.\n */\nexport function validateElementBelongsToComponent(\n elementType: string,\n componentType: string\n): boolean {\n if (!isValidComponentType(componentType)) return false\n\n const validElements =\n SKHEMA_MAPPING.elementFlow[\n componentType as keyof typeof SKHEMA_MAPPING.elementFlow\n ]\n if (!validElements) return false\n\n return validElements.some((e) => e.value === elementType)\n}\n\n/**\n * Get the human-readable label for a component type.\n */\nexport function getComponentTypeLabel(componentType: string): string {\n const type = Object.values(COMPONENT_TYPES).find(\n (t) => t.value === componentType\n )\n return type?.label || componentType\n}\n\n/**\n * Get the acronym for a component type (e.g., 'BD' for diagnosis).\n */\nexport function getComponentTypeAcronym(componentType: string): string {\n const type = Object.values(COMPONENT_TYPES).find(\n (t) => t.value === componentType\n )\n return type?.acronym || componentType.substring(0, 2).toUpperCase()\n}\n\n/**\n * Resolve which component type an element belongs to.\n * Returns the component value, or 'diagnosis' as fallback.\n */\nexport function resolveComponentType(elementType: string): ComponentValue {\n for (const [componentValue, elements] of Object.entries(\n SKHEMA_MAPPING.elementFlow\n )) {\n if (elements.some((e) => e.value === elementType)) {\n return componentValue as ComponentValue\n }\n }\n return 'diagnosis'\n}\n\n/**\n * Get all valid element types for a given component, ordered per the mapping.\n */\nexport function getElementTypesForComponent(\n componentType: string\n): ElementValue[] {\n if (!isValidComponentType(componentType)) return []\n const elements =\n SKHEMA_MAPPING.elementFlow[\n componentType as keyof typeof SKHEMA_MAPPING.elementFlow\n ]\n return elements?.map((e) => e.value) || []\n}\n","/**\n * Content sanitization utilities for Skhema cards.\n *\n * DOM-free by design: `sanitizeContent` is imported by the email-safe card\n * renderer (`@skhema/embed/render`), which must run in Node / email / CLI\n * runtimes with no `document`. Keep every export here free of DOM access at\n * call time (the legacy `stripHtml` below is the one exception and is not used\n * by the renderer).\n */\n\n/**\n * HTML entity encoding for basic XSS protection. Regex-based (no DOM) so it is\n * safe in non-browser runtimes. Escapes the text-context entities plus quotes.\n */\nfunction htmlEncode(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\n/**\n * Sanitizes content to prevent XSS attacks and removes URLs\n * @param content The raw content to sanitize\n * @returns Sanitized HTML-safe content with URLs removed\n */\nexport function sanitizeContent(content: string): string {\n // Strip URLs first\n let sanitized = stripUrls(content)\n\n // Encode all HTML entities to prevent script injection\n sanitized = htmlEncode(sanitized)\n\n // Preserve line breaks for readability\n sanitized = sanitized.replace(/\\n/g, '<br>')\n\n // Apply text wrapping rules for long text\n sanitized = applyTextWrapping(sanitized)\n\n return sanitized\n}\n\n/**\n * Strips all URLs from the content\n * @param text The text containing potential URLs\n * @returns Text with all URLs removed\n */\nfunction stripUrls(text: string): string {\n // Comprehensive URL patterns to remove\n const patterns = [\n // Standard URLs with protocols\n /https?:\\/\\/[^\\s<>\"{}|\\\\^`[\\]]+/gi,\n // FTP URLs\n /ftp:\\/\\/[^\\s<>\"{}|\\\\^`[\\]]+/gi,\n // URLs without protocol but with www\n /www\\.[^\\s<>\"{}|\\\\^`[\\]]+/gi,\n // Email-like patterns\n /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/gi,\n // Common domain patterns (anything.com, anything.org, etc.)\n /(?:^|\\s)([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}(?:\\/[^\\s]*)?/gi,\n ]\n\n let stripped = text\n patterns.forEach((pattern) => {\n stripped = stripped.replace(pattern, '')\n })\n\n // Clean up any multiple spaces left after URL removal\n stripped = stripped.replace(/\\s+/g, ' ').trim()\n\n return stripped\n}\n\n/**\n * Applies intelligent text wrapping to prevent layout breaking\n * @param text The text to apply wrapping rules to\n * @returns Text with appropriate wrapping hints\n */\nfunction applyTextWrapping(text: string): string {\n // Split text into words\n const words = text.split(/(\\s+)/)\n\n return words\n .map((word) => {\n // Skip if it's whitespace or already contains HTML\n if (/^\\s+$/.test(word) || word.includes('<')) {\n return word\n }\n\n // For very long words (>30 chars), add word-break opportunities\n if (word.length > 30) {\n // Insert zero-width spaces every 10 characters for breaking\n return word.replace(/(.{10})/g, '$1\\u200B')\n }\n\n return word\n })\n .join('')\n}\n\n/**\n * Validates if content contains potentially malicious patterns or URLs\n * @param content The content to validate\n * @returns Object with validation status and detected issues\n */\nexport function validateContentSecurity(content: string): {\n isSecure: boolean\n issues: string[]\n} {\n const issues: string[] = []\n\n // Check for script tags\n if (/<script[\\s>]/i.test(content)) {\n issues.push('Script tags detected')\n }\n\n // Check for event handlers\n if (/on\\w+\\s*=/i.test(content)) {\n issues.push('Event handlers detected')\n }\n\n // Check for javascript: protocol\n if (/javascript:/i.test(content)) {\n issues.push('JavaScript protocol detected')\n }\n\n // Check for data: URLs that could contain scripts\n if (/data:[^,]*script/i.test(content)) {\n issues.push('Data URL with script detected')\n }\n\n // Check for iframe tags\n if (/<iframe[\\s>]/i.test(content)) {\n issues.push('Iframe tags detected')\n }\n\n // Check for URLs (since we want to disallow them)\n if (/https?:\\/\\//i.test(content) || /www\\./i.test(content)) {\n issues.push('URLs detected in content')\n }\n\n return {\n isSecure: issues.length === 0,\n issues,\n }\n}\n\n/**\n * Strips all HTML tags from content\n * @param content The content to strip\n * @returns Plain text content\n */\nexport function stripHtml(content: string): string {\n const div = document.createElement('div')\n div.innerHTML = content\n return div.textContent || div.innerText || ''\n}\n\n/**\n * Checks if content contains any URLs\n * @param content The content to check\n * @returns True if URLs are found\n */\nexport function containsUrls(content: string): boolean {\n const urlPatterns = [\n /https?:\\/\\//i,\n /ftp:\\/\\//i,\n /www\\./i,\n /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/i,\n /(?:^|\\s)([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}(?:\\/[^\\s]*)?/i,\n ]\n\n return urlPatterns.some((pattern) => pattern.test(content))\n}\n","import type { ElementValue } from '@skhema/method/vocabulary'\nimport { ELEMENT_TYPES } from '@skhema/method/vocabulary'\n\nexport function isValidElementType(\n elementType: string\n): elementType is ElementValue {\n const validTypes = Object.values(ELEMENT_TYPES).map((type) => type.value)\n return validTypes.includes(elementType as ElementValue)\n}\n\nexport function validateAttributes(element: HTMLElement): {\n isValid: boolean\n errors: string[]\n elementType?: ElementValue\n contributorId?: string\n} {\n const errors: string[] = []\n\n const elementType = element.getAttribute('element-type')\n const contributorId = element.getAttribute('contributor-id')\n\n if (!elementType) {\n errors.push('Missing required attribute: element-type')\n } else if (!isValidElementType(elementType)) {\n const validTypes = Object.values(ELEMENT_TYPES)\n .map((t) => t.value)\n .join(', ')\n errors.push(\n `Invalid element-type \"${elementType}\". Valid types: ${validTypes}`\n )\n }\n\n if (!contributorId) {\n errors.push('Missing required attribute: contributor-id')\n } else if (contributorId.trim().length === 0) {\n errors.push('contributor-id cannot be empty')\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n elementType: isValidElementType(elementType || '')\n ? (elementType as ElementValue)\n : undefined,\n contributorId: contributorId || undefined,\n }\n}\n\nexport function getElementTypeLabel(elementType: ElementValue): string {\n const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType)\n return type?.label || elementType\n}\n\nexport function getElementTypeAcronym(elementType: ElementValue): string {\n const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType)\n return type?.acronym || elementType.substring(0, 2).toUpperCase()\n}\n","/**\n * `@skhema/embed/render` — the canonical, DOM-free source of truth for the\n * official Skhema element / component card HTML.\n *\n * `renderElementCardHtml(data)` / `renderComponentCardHtml(data)` return\n * EMAIL-SAFE HTML: a `role=\"presentation\"` table layout with every style\n * inlined as flat hex (no shadow DOM, no `<style>`, no `oklch()`, no CSS vars).\n * The same output is used three ways:\n *\n * 1. the live browser embed (`SkhemaElement` / `SkhemaComponent` inject it\n * into shadow DOM and layer hover/transition CSS on top);\n * 2. the `CuratedElements` email template (injected verbatim); and\n * 3. any third-party / contributor email generator.\n *\n * The module is pure — importing it never touches the DOM, so it is safe in\n * Node, edge, and email build runtimes. It builds NO URLs: callers pass the\n * fully-formed `saveUrl` (e.g. the `/save` handoff) so each surface controls\n * its own UTM tagging.\n */\nimport type { ElementValue } from '@skhema/method/vocabulary'\nimport {\n CARD_PALETTE,\n CARD_RADIUS,\n CARD_SHADOW,\n COMPONENT_COLORS,\n FONT_STACK,\n MONO_STACK,\n PRIMARY_HEX,\n USER_ICON_SVG,\n type CardTheme,\n} from '../styles/design-tokens.js'\nimport { oklchToHex } from '../utils/color.js'\nimport {\n getComponentTypeAcronym,\n getComponentTypeLabel,\n resolveComponentType,\n} from '../utils/component-validation.js'\nimport { sanitizeContent } from '../utils/sanitization.js'\nimport { getElementTypeLabel } from '../utils/validation.js'\n\n/* ------------------------------------------------------------------ *\n * Public data shapes (documented contract — see README \"render\") *\n * ------------------------------------------------------------------ */\n\n/** Card theme. Email is always `'light'`; the browser embed passes the\n * detected page theme. Defaults to `'light'` when omitted. */\nexport type { CardTheme }\n\n/** Author attribution shared by both card kinds. */\ninterface AuthorFields {\n /** Display name. When omitted, falls back to a humanised `contributorId`. */\n authorName?: string | null\n /** Public contributor slug — when present, the name links to the profile. */\n authorSlug?: string | null\n /** Contributor id, used only for the name fallback when `authorName` is unset. */\n contributorId?: string | null\n}\n\n/** Input for {@link renderElementCardHtml}. */\nexport interface ElementCardData extends AuthorFields {\n /** Skhema element type value, e.g. `\"key_challenge\"`. */\n elementType: string\n /** The element content / premise (plain text; sanitised + escaped here). */\n content: string\n /** Pre-built handoff URL for the \"Save to Skhema\" button. */\n saveUrl: string\n /** Card theme (default `'light'`). */\n theme?: CardTheme\n}\n\n/** A single element row inside a component card. */\nexport interface ComponentCardElement {\n elementType: string\n content: string\n}\n\n/** Input for {@link renderComponentCardHtml}. */\nexport interface ComponentCardData extends AuthorFields {\n /** Skhema component type value, e.g. `\"diagnosis\"`. */\n componentType: string\n /** Optional component title shown after a \"—\" separator in the header. */\n title?: string | null\n /** The component's elements, in display order. */\n elements: ComponentCardElement[]\n /** Pre-built handoff URL for the \"Save to Skhema\" button. */\n saveUrl: string\n /** Card theme (default `'light'`). */\n theme?: CardTheme\n}\n\n/* ------------------------------------------------------------------ *\n * Internal helpers *\n * ------------------------------------------------------------------ */\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction escapeAttr(text: string): string {\n return escapeHtml(text).replace(/'/g, '&#39;')\n}\n\nfunction humaniseContributorId(contributorId: string): string {\n return contributorId\n .split(/[_-]/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ')\n}\n\ninterface BadgePalette {\n badgeBg: string\n badgeText: string\n badgeBorder: string\n topBorder: string\n}\n\n/** Resolve the email-safe badge / top-border hex for a component type + theme. */\nfunction resolveBadgePalette(\n componentType: string,\n theme: CardTheme\n): BadgePalette {\n const surface = CARD_PALETTE[theme].cardBg\n const colors =\n COMPONENT_COLORS[componentType as keyof typeof COMPONENT_COLORS]\n if (!colors) {\n return {\n badgeBg: surface,\n badgeText: PRIMARY_HEX,\n badgeBorder: PRIMARY_HEX,\n topBorder: PRIMARY_HEX,\n }\n }\n const text = oklchToHex(colors.text)\n return {\n badgeBg: oklchToHex(colors.bg, surface),\n badgeText: text,\n badgeBorder: oklchToHex(colors.border, surface),\n topBorder: text,\n }\n}\n\n/** Contributor line inner HTML: person icon + \"By {author}\" (linked if slug). */\nfunction renderAuthorHtml(author: AuthorFields, mutedColor: string): string {\n let label = ''\n if (author.authorName && author.authorName.trim()) {\n const name = escapeHtml(author.authorName.trim())\n label = author.authorSlug\n ? `By <a href=\"https://skhema.com/contributors/${encodeURIComponent(\n author.authorSlug\n )}\" style=\"color:${mutedColor};text-decoration:none;\" target=\"_blank\" rel=\"noopener noreferrer\">${name}</a>`\n : `By ${name}`\n } else if (author.contributorId && author.contributorId.trim()) {\n label = `By ${escapeHtml(humaniseContributorId(author.contributorId.trim()))}`\n }\n\n if (!label) return '&nbsp;'\n\n return (\n `<span style=\"display:inline-block;width:14px;height:14px;vertical-align:middle;color:${mutedColor};\">${USER_ICON_SVG}</span>` +\n `<span style=\"vertical-align:middle;padding-left:6px;\">${label}</span>`\n )\n}\n\n/** The shared footer (contributor line + save button + attribution). */\nfunction renderFooter(\n saveUrl: string,\n author: AuthorFields,\n theme: CardTheme\n): string {\n const p = CARD_PALETTE[theme]\n return (\n `<tr><td style=\"padding:12px 16px;border-top:1px solid ${p.border};\">` +\n `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"width:100%;border-collapse:collapse;\"><tr>` +\n `<td style=\"vertical-align:middle;\"><span style=\"font-size:12px;color:${p.textMuted};\">${renderAuthorHtml(\n author,\n p.textMuted\n )}</span></td>` +\n `<td style=\"text-align:right;vertical-align:middle;white-space:nowrap;\">` +\n `<a href=\"${escapeAttr(saveUrl)}\" class=\"skhema-save-btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Save this to Skhema\" style=\"display:inline-block;background:${PRIMARY_HEX};color:#ffffff;font-size:12px;font-weight:500;text-decoration:none;padding:6px 14px;border-radius:${CARD_RADIUS};white-space:nowrap;\">Save to Skhema &rarr;</a>` +\n `</td></tr></table>` +\n `<div style=\"font-size:11px;line-height:1.4;color:${p.textMuted};margin-top:8px;\">Strategy powered by <a href=\"https://skhema.com\" style=\"color:${p.textMuted};text-decoration:underline;\" target=\"_blank\" rel=\"noopener noreferrer\">Skhema</a></div>` +\n `</td></tr>`\n )\n}\n\n/** Header row: acronym badge + type label (+ optional \"— title\" for components). */\nfunction renderHeader(\n badge: BadgePalette,\n acronym: string,\n typeLabel: string,\n labelColor: string,\n theme: CardTheme,\n title?: string | null\n): string {\n const p = CARD_PALETTE[theme]\n const titleHtml = title\n ? `<span style=\"color:${p.textMuted};\"> &mdash; </span><span style=\"font-weight:600;color:${p.text};\">${escapeHtml(\n title\n )}</span>`\n : ''\n return (\n `<tr><td style=\"padding:12px 16px;border-bottom:1px solid ${p.border};\">` +\n `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"width:100%;border-collapse:collapse;\"><tr>` +\n `<td style=\"width:1%;white-space:nowrap;padding-right:8px;vertical-align:middle;\">` +\n `<span class=\"skhema-acronym-badge\" title=\"${escapeAttr(typeLabel)}\" style=\"display:inline-block;font-family:${MONO_STACK};font-size:10px;font-weight:600;letter-spacing:0.02em;padding:2px 6px;border-radius:2px;background:${badge.badgeBg};color:${badge.badgeText};border:1px solid ${badge.badgeBorder};\">${escapeHtml(\n acronym\n )}</span></td>` +\n `<td style=\"vertical-align:middle;\"><span style=\"font-size:13px;font-weight:500;color:${labelColor};\">${escapeHtml(\n typeLabel\n )}${titleHtml}</span></td>` +\n `</tr></table></td></tr>`\n )\n}\n\n/** Open the outer card table with inline card styling for the theme. */\nfunction cardOpen(theme: CardTheme, kind: 'element' | 'component'): string {\n const p = CARD_PALETTE[theme]\n return (\n `<table role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" class=\"skhema-card\" data-skhema-kind=\"${kind}\" data-theme=\"${theme}\" ` +\n `style=\"width:100%;max-width:600px;border-collapse:separate;background:${p.cardBg};border:1px solid ${p.border};border-radius:${CARD_RADIUS};box-shadow:${CARD_SHADOW};overflow:hidden;margin:8px 0;font-family:${FONT_STACK};line-height:1.5;color:${p.text};\">`\n )\n}\n\n/* ------------------------------------------------------------------ *\n * Public renderers *\n * ------------------------------------------------------------------ */\n\n/**\n * Render the official Skhema **element** card as email-safe HTML.\n * The badge acronym + palette resolve from the element's owning component type,\n * exactly like the live `<skhema-element>` embed.\n */\nexport function renderElementCardHtml(data: ElementCardData): string {\n const theme = data.theme ?? 'light'\n const p = CARD_PALETTE[theme]\n const label = getElementTypeLabel(data.elementType as ElementValue)\n const componentType = resolveComponentType(data.elementType)\n const acronym = getComponentTypeAcronym(componentType)\n const badge = resolveBadgePalette(componentType, theme)\n\n return (\n cardOpen(theme, 'element') +\n renderHeader(badge, acronym, label, p.text, theme) +\n `<tr><td style=\"padding:16px;\">` +\n `<div style=\"font-size:15px;line-height:1.6;color:${p.text};word-wrap:break-word;overflow-wrap:break-word;\">${sanitizeContent(\n data.content\n )}</div>` +\n `</td></tr>` +\n renderFooter(data.saveUrl, data, theme) +\n `</table>`\n )\n}\n\n/**\n * Render the official Skhema **component** card as email-safe HTML — a coloured\n * top bar, header (badge + type label + optional title), per-element-type\n * groups, and the shared footer.\n */\nexport function renderComponentCardHtml(data: ComponentCardData): string {\n const theme = data.theme ?? 'light'\n const p = CARD_PALETTE[theme]\n const label = getComponentTypeLabel(data.componentType)\n const acronym = getComponentTypeAcronym(data.componentType)\n const badge = resolveBadgePalette(data.componentType, theme)\n\n // Group elements by type (preserving first-occurrence order), mirroring the\n // live `<skhema-component>` body: one small-caps label per type, with each\n // element's content as a left-ruled block beneath it.\n const groups = new Map<string, string[]>()\n for (const el of data.elements) {\n const existing = groups.get(el.elementType)\n if (existing) existing.push(el.content)\n else groups.set(el.elementType, [el.content])\n }\n\n const groupsHtml = Array.from(groups.entries())\n .map(\n ([elementType, contents]) =>\n `<div style=\"margin-bottom:16px;\">` +\n `<div style=\"text-transform:uppercase;letter-spacing:0.05em;font-family:${MONO_STACK};font-size:10px;font-weight:600;color:${p.textMuted};margin:0 0 4px;\">${escapeHtml(\n getElementTypeLabel(elementType as ElementValue)\n )}</div>` +\n contents\n .map(\n (content) =>\n `<div style=\"font-size:14px;line-height:1.6;color:${p.text};padding-left:8px;border-left:2px solid ${p.border};word-wrap:break-word;overflow-wrap:break-word;\">${sanitizeContent(\n content\n )}</div>`\n )\n .join('') +\n `</div>`\n )\n .join('')\n\n return (\n cardOpen(theme, 'component') +\n `<tr><td style=\"height:2px;line-height:2px;font-size:0;background:${badge.topBorder};\">&nbsp;</td></tr>` +\n renderHeader(badge, acronym, label, p.textMuted, theme, data.title) +\n `<tr><td style=\"padding:16px;\">${groupsHtml}</td></tr>` +\n renderFooter(data.saveUrl, data, theme) +\n `</table>`\n )\n}\n","import type {\n ComponentContentData,\n ComponentEmbedAnalytics,\n ContentData,\n EmbedAnalytics,\n} from '../components/types.js'\n\n/**\n * Analytics endpoint — Supabase edge function via Cloudflare proxy.\n * Falls back to direct Supabase URL if proxy is not configured.\n */\nconst ANALYTICS_ENDPOINT =\n 'https://analytics.skhema.com/functions/v1/embed-manage'\n\n// Cookie-based tracking management\ninterface TrackedEmbed {\n contentHash: string\n timestamp: number\n}\n\nconst TRACKING_COOKIE_NAME = '_sk'\nconst TRACKING_EXPIRY_HOURS = 24\nconst MAX_TRACKED_ITEMS = 50 // Prevent cookie from growing too large\n\nfunction getTrackedEmbeds(): TrackedEmbed[] {\n try {\n const cookie = document.cookie\n .split('; ')\n .find((row) => row.startsWith(`${TRACKING_COOKIE_NAME}=`))\n\n if (!cookie) return []\n\n const data = JSON.parse(decodeURIComponent(cookie.split('=')[1]))\n const now = Date.now()\n const cutoff = now - TRACKING_EXPIRY_HOURS * 60 * 60 * 1000\n\n // Filter out expired entries\n return data.filter((item: TrackedEmbed) => item.timestamp > cutoff)\n } catch {\n return []\n }\n}\n\nfunction setTrackedEmbeds(tracked: TrackedEmbed[]): void {\n try {\n // Keep only the most recent entries to prevent cookie bloat\n const limited = tracked.slice(-MAX_TRACKED_ITEMS)\n const expires = new Date(\n Date.now() + TRACKING_EXPIRY_HOURS * 60 * 60 * 1000\n )\n\n document.cookie = `${TRACKING_COOKIE_NAME}=${encodeURIComponent(\n JSON.stringify(limited)\n )}; expires=${expires.toUTCString()}; path=/; SameSite=Lax`\n } catch {\n // Fail silently if cookie storage fails\n }\n}\n\nfunction hasBeenTracked(contentHash: string): boolean {\n const tracked = getTrackedEmbeds()\n return tracked.some((item) => item.contentHash === contentHash)\n}\n\nfunction markAsTracked(contentHash: string): void {\n const tracked = getTrackedEmbeds()\n tracked.push({\n contentHash,\n timestamp: Date.now(),\n })\n setTrackedEmbeds(tracked)\n}\n\n// Batching system for analytics\ninterface BatchedAnalytics {\n embeds: EmbedAnalytics[]\n clicks: ContentData[]\n componentEmbeds: ComponentEmbedAnalytics[]\n componentClicks: ComponentContentData[]\n}\n\nclass AnalyticsBatcher {\n private batch: BatchedAnalytics = {\n embeds: [],\n clicks: [],\n componentEmbeds: [],\n componentClicks: [],\n }\n private batchTimeout: number | null = null\n private readonly BATCH_DELAY = 2000 // 2 seconds\n private readonly MAX_BATCH_SIZE = 10\n\n addEmbedLoad(analytics: EmbedAnalytics): void {\n this.batch.embeds.push(analytics)\n this.scheduleBatchSend()\n }\n\n addClick(contentData: ContentData): void {\n this.batch.clicks.push(contentData)\n this.scheduleBatchSend()\n }\n\n addComponentEmbedLoad(analytics: ComponentEmbedAnalytics): void {\n this.batch.componentEmbeds.push(analytics)\n this.scheduleBatchSend()\n }\n\n addComponentClick(contentData: ComponentContentData): void {\n this.batch.componentClicks.push(contentData)\n this.scheduleBatchSend()\n }\n\n private scheduleBatchSend(): void {\n // Clear existing timeout\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout)\n }\n\n // Send immediately if any batch is full\n const totalItems =\n this.batch.embeds.length +\n this.batch.clicks.length +\n this.batch.componentEmbeds.length +\n this.batch.componentClicks.length\n\n if (totalItems >= this.MAX_BATCH_SIZE) {\n this.sendBatch()\n return\n }\n\n // Otherwise, wait for more events or timeout\n this.batchTimeout = window.setTimeout(() => {\n this.sendBatch()\n }, this.BATCH_DELAY)\n }\n\n private async sendBatch(): Promise<void> {\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout)\n this.batchTimeout = null\n }\n\n const currentBatch = { ...this.batch }\n this.batch = {\n embeds: [],\n clicks: [],\n componentEmbeds: [],\n componentClicks: [],\n }\n\n const hasItems =\n currentBatch.embeds.length > 0 ||\n currentBatch.clicks.length > 0 ||\n currentBatch.componentEmbeds.length > 0 ||\n currentBatch.componentClicks.length > 0\n\n if (!hasItems) return\n\n // Send embeds if any\n if (currentBatch.embeds.length > 0) {\n await this.sendEmbeds(currentBatch.embeds)\n }\n\n // Send clicks if any\n if (currentBatch.clicks.length > 0) {\n await this.sendClicks(currentBatch.clicks)\n }\n\n // Send component embeds if any\n if (currentBatch.componentEmbeds.length > 0) {\n await this.sendComponentEmbeds(currentBatch.componentEmbeds)\n }\n\n // Send component clicks if any\n if (currentBatch.componentClicks.length > 0) {\n await this.sendComponentClicks(currentBatch.componentClicks)\n }\n }\n\n private async sendEmbeds(embeds: EmbedAnalytics[]): Promise<void> {\n if (embeds.length === 0) return\n\n try {\n const payload = {\n action: 'embed',\n events: embeds.map((embed) => ({\n contributor_id: embed.contributorId,\n element_type: embed.elementType,\n content_hash: embed.contentHash,\n content: embed.content,\n page_url: embed.pageUrl,\n page_title: embed.pageTitle || '',\n user_agent: embed.userAgent || '',\n })),\n }\n\n await sendWithRetry(ANALYTICS_ENDPOINT, payload, 'json')\n } catch (error) {\n console.debug('Embed tracking failed:', error)\n }\n }\n\n private async sendClicks(clicks: ContentData[]): Promise<void> {\n // Send clicks individually since they typically navigate away from page\n for (const click of clicks) {\n try {\n const data = {\n action: 'click',\n contributor_id: click.contributor_id,\n content_hash: click.content_hash,\n source_url: click.source_url,\n element_type: click.element_type,\n }\n\n fetch(ANALYTICS_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n credentials: 'omit',\n keepalive: true,\n }).catch(() => {\n // Fail silently\n })\n } catch (error) {\n console.debug('Click tracking failed:', error)\n }\n }\n }\n\n private async sendComponentEmbeds(\n embeds: ComponentEmbedAnalytics[]\n ): Promise<void> {\n if (embeds.length === 0) return\n\n // Edge function expects flat fields at top level, not wrapped in events[]\n for (const embed of embeds) {\n try {\n const payload = {\n action: 'component_embed',\n contributor_id: embed.contributorId,\n component_type: embed.componentType,\n component_hash: embed.componentHash,\n title: embed.title,\n elements: embed.elements.map((el) => ({\n element_type: el.elementType,\n content: el.content,\n content_hash: el.contentHash,\n })),\n page_url: embed.pageUrl,\n page_title: embed.pageTitle || '',\n user_agent: embed.userAgent || '',\n }\n\n await sendWithRetry(ANALYTICS_ENDPOINT, payload, 'json')\n } catch (error) {\n console.debug('Component embed tracking failed:', error)\n }\n }\n }\n\n private async sendComponentClicks(\n clicks: ComponentContentData[]\n ): Promise<void> {\n for (const click of clicks) {\n try {\n const data = {\n action: 'component_click',\n contributor_id: click.contributor_id,\n component_type: click.component_type,\n component_hash: click.component_hash,\n title: click.title,\n source_url: click.source_url,\n }\n\n fetch(ANALYTICS_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n credentials: 'omit',\n keepalive: true,\n }).catch(() => {\n // Fail silently\n })\n } catch (error) {\n console.debug('Component click tracking failed:', error)\n }\n }\n }\n\n // Ensure batch is sent when page unloads.\n // Uses sendBeacon for reliability — async fetch won't complete during unload.\n flush(): void {\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout)\n this.batchTimeout = null\n }\n\n const currentBatch = { ...this.batch }\n this.batch = {\n embeds: [],\n clicks: [],\n componentEmbeds: [],\n componentClicks: [],\n }\n\n // Send embeds via beacon\n if (currentBatch.embeds.length > 0) {\n const payload = {\n action: 'embed',\n events: currentBatch.embeds.map((embed) => ({\n contributor_id: embed.contributorId,\n element_type: embed.elementType,\n content_hash: embed.contentHash,\n content: embed.content,\n page_url: embed.pageUrl,\n page_title: embed.pageTitle || '',\n user_agent: embed.userAgent || '',\n })),\n }\n this.sendViaBeacon(payload)\n }\n\n // Send clicks via beacon\n for (const click of currentBatch.clicks) {\n this.sendViaBeacon({\n action: 'click',\n contributor_id: click.contributor_id,\n content_hash: click.content_hash,\n source_url: click.source_url,\n element_type: click.element_type,\n })\n }\n\n // Send component embeds via beacon (flat fields, not wrapped in events[])\n for (const embed of currentBatch.componentEmbeds) {\n this.sendViaBeacon({\n action: 'component_embed',\n contributor_id: embed.contributorId,\n component_type: embed.componentType,\n component_hash: embed.componentHash,\n title: embed.title,\n elements: embed.elements.map((el) => ({\n element_type: el.elementType,\n content: el.content,\n content_hash: el.contentHash,\n })),\n page_url: embed.pageUrl,\n page_title: embed.pageTitle || '',\n user_agent: embed.userAgent || '',\n })\n }\n\n // Send component clicks via beacon\n for (const click of currentBatch.componentClicks) {\n this.sendViaBeacon({\n action: 'component_click',\n contributor_id: click.contributor_id,\n component_type: click.component_type,\n component_hash: click.component_hash,\n title: click.title,\n source_url: click.source_url,\n })\n }\n }\n\n private sendViaBeacon(payload: Record<string, unknown>): void {\n try {\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n navigator.sendBeacon(\n ANALYTICS_ENDPOINT,\n new Blob([JSON.stringify(payload)], { type: 'application/json' })\n )\n } else {\n // Fallback to fetch with keepalive\n fetch(ANALYTICS_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n credentials: 'omit',\n keepalive: true,\n }).catch(() => {})\n }\n } catch {\n // Fail silently during unload\n }\n }\n}\n\n// Global batcher instance\nconst analyticsBatcher = new AnalyticsBatcher()\n\n// Flush on page unload\nif (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => {\n analyticsBatcher.flush()\n })\n\n // Also flush on visibility change (mobile browsers)\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n analyticsBatcher.flush()\n }\n })\n}\n\n// Retry logic with exponential backoff\nasync function sendWithRetry(\n url: string,\n data: URLSearchParams | Record<string, unknown>,\n contentType: 'json' | 'urlencoded' = 'json',\n maxRetries = 3\n): Promise<void> {\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n const options: RequestInit = {\n method: 'POST',\n credentials: 'omit',\n keepalive: true,\n }\n\n if (contentType === 'json') {\n options.headers = { 'Content-Type': 'application/json' }\n options.body = JSON.stringify(data)\n } else {\n options.body = data as URLSearchParams\n }\n\n const response = await fetch(url, options)\n\n if (response.ok) return\n\n if (response.status >= 400 && response.status < 500) {\n // Client error, don't retry\n break\n }\n } catch (error) {\n if (attempt === maxRetries - 1) {\n console.debug('Analytics failed after retries:', error)\n return\n }\n }\n\n // Exponential backoff: 1s, 2s, 4s\n await new Promise((resolve) =>\n setTimeout(resolve, Math.pow(2, attempt) * 1000)\n )\n }\n}\n\n// Main tracking functions\nexport async function trackEmbedLoad(analytics: EmbedAnalytics): Promise<void> {\n try {\n // Check if this embed has already been tracked\n if (hasBeenTracked(analytics.contentHash)) {\n console.debug('Embed already tracked, skipping:', analytics.contentHash)\n return\n }\n\n // Mark as tracked before sending to prevent race conditions\n markAsTracked(analytics.contentHash)\n\n // Add to batch instead of sending immediately\n analyticsBatcher.addEmbedLoad(analytics)\n } catch (error) {\n console.debug('Analytics tracking failed:', error)\n }\n}\n\nexport async function trackClick(contentData: ContentData): Promise<void> {\n try {\n // Add to batch instead of sending immediately\n analyticsBatcher.addClick(contentData)\n } catch (error) {\n console.debug('Click tracking failed:', error)\n }\n}\n\nexport async function trackComponentEmbedLoad(\n analytics: ComponentEmbedAnalytics\n): Promise<void> {\n try {\n // Check if this component has already been tracked\n if (hasBeenTracked(analytics.componentHash)) {\n console.debug(\n 'Component embed already tracked, skipping:',\n analytics.componentHash\n )\n return\n }\n\n // Mark as tracked before sending to prevent race conditions\n markAsTracked(analytics.componentHash)\n\n // Add to batch instead of sending immediately\n analyticsBatcher.addComponentEmbedLoad(analytics)\n } catch (error) {\n console.debug('Component analytics tracking failed:', error)\n }\n}\n\nexport async function trackComponentClick(\n contentData: ComponentContentData\n): Promise<void> {\n try {\n analyticsBatcher.addComponentClick(contentData)\n } catch (error) {\n console.debug('Component click tracking failed:', error)\n }\n}\n\nexport function shouldTrackAnalytics(element: HTMLElement): boolean {\n const trackAnalytics = element.getAttribute('track-analytics')\n return trackAnalytics !== 'false'\n}\n","export function generateContentHash(content: string): string {\n // Simple hash function for content identification\n let hash = 0\n const cleanContent = content.trim().substring(0, 200)\n\n for (let i = 0; i < cleanContent.length; i++) {\n const char = cleanContent.charCodeAt(i)\n hash = (hash << 5) - hash + char\n hash = hash & hash // Convert to 32bit integer\n }\n\n return Math.abs(hash).toString(36).substring(0, 12)\n}\n\n/**\n * Generate a hash for a component by concatenating sorted element content.\n * Used for component-level deduplication and analytics.\n */\nexport function generateComponentHash(\n elements: Array<{ elementType: string; content: string }>\n): string {\n const sorted = [...elements].sort((a, b) => {\n const typeCompare = a.elementType.localeCompare(b.elementType)\n if (typeCompare !== 0) return typeCompare\n return a.content.localeCompare(b.content)\n })\n\n const combined = sorted.map((e) => `${e.elementType}:${e.content}`).join('|')\n return generateContentHash(combined)\n}\n","import type { ComponentValue, ElementValue } from '@skhema/method/vocabulary'\nimport { getComponentTypeLabel } from './component-validation.js'\nimport { generateContentHash } from './hash.js'\nimport { getElementTypeLabel } from './validation.js'\n\nexport function generateStructuredData(\n content: string,\n elementType: ElementValue,\n contributorId: string,\n sourceUrl: string\n): object {\n return {\n '@context': 'https://schema.org',\n '@type': 'AnalysisContent',\n text: content,\n analysisType: elementType,\n category: getElementTypeLabel(elementType),\n contributor: contributorId,\n url: generateRedirectUrl(content, elementType, contributorId),\n provider: {\n '@type': 'Organization',\n name: 'Skhema',\n url: 'https://skhema.com',\n },\n isPartOf: {\n '@type': 'WebPage',\n url: sourceUrl,\n },\n dateCreated: new Date().toISOString(),\n platform: 'Skhema',\n }\n}\n\nexport function generateRedirectUrl(\n content: string,\n elementType: ElementValue,\n contributorId: string,\n options: {\n baseUrl?: string\n utmSource?: string\n utmMedium?: string\n utmCampaign?: string\n } = {}\n): string {\n const baseUrl = options.baseUrl || 'https://app.skhema.com/save' // This page will handle the authentication and content saving\n const contentHash = generateContentHash(content)\n const sourceUrl = encodeURIComponent(window.location.href)\n const timestamp = Date.now()\n\n const params = new URLSearchParams({\n source: sourceUrl,\n t: timestamp.toString(),\n utm_source: options.utmSource || 'web_component',\n utm_medium: options.utmMedium || 'embedded',\n utm_campaign: options.utmCampaign || elementType,\n utm_content: contributorId,\n })\n\n return `${baseUrl}?type=contributor&contributor_id=${contributorId}&element_type=${elementType}&content_hash=${contentHash}&${params.toString()}`\n // return `${baseUrl}/contributor_id=${contributorId}&element_type=${elementType}&content_hash=${contentHash}?${params.toString()}`;\n}\n\nexport function createMetaTags(\n content: string,\n elementType: ElementValue,\n contributorId: string\n): string {\n const label = getElementTypeLabel(elementType)\n\n return `\n <div itemscope itemtype=\"https://schema.org/AnalysisContent\" style=\"display:none;\">\n <meta itemprop=\"analysisType\" content=\"${elementType}\">\n <meta itemprop=\"text\" content=\"${content}\">\n <meta itemprop=\"contributor\" content=\"${contributorId}\">\n <meta itemprop=\"category\" content=\"${label}\">\n <meta itemprop=\"platform\" content=\"Skhema\">\n </div>\n `\n}\n\nexport function generateComponentStructuredData(\n title: string,\n componentType: ComponentValue,\n contributorId: string,\n elements: Array<{ elementType: string; content: string }>,\n sourceUrl: string\n): object {\n const componentLabel = getComponentTypeLabel(componentType)\n\n return {\n '@context': 'https://schema.org',\n '@type': 'CreativeWork',\n name: title,\n description: `${componentLabel} strategy component`,\n category: componentLabel,\n contributor: contributorId,\n url: generateComponentRedirectUrl(\n generateContentHash(elements.map((e) => e.content).join('|')),\n componentType,\n contributorId\n ),\n hasPart: elements.map((el) => ({\n '@type': 'AnalysisContent',\n text: el.content,\n analysisType: el.elementType,\n category: getElementTypeLabel(el.elementType as ElementValue),\n })),\n provider: {\n '@type': 'Organization',\n name: 'Skhema',\n url: 'https://skhema.com',\n },\n isPartOf: {\n '@type': 'WebPage',\n url: sourceUrl,\n },\n dateCreated: new Date().toISOString(),\n platform: 'Skhema',\n }\n}\n\nexport function generateComponentRedirectUrl(\n componentHash: string,\n componentType: ComponentValue,\n contributorId: string,\n options: {\n baseUrl?: string\n utmSource?: string\n utmMedium?: string\n utmCampaign?: string\n } = {}\n): string {\n const baseUrl = options.baseUrl || 'https://app.skhema.com/save'\n const sourceUrl = encodeURIComponent(window.location.href)\n const timestamp = Date.now()\n\n const params = new URLSearchParams({\n source: sourceUrl,\n t: timestamp.toString(),\n utm_source: options.utmSource || 'web_component',\n utm_medium: options.utmMedium || 'embedded',\n utm_campaign: options.utmCampaign || componentType,\n utm_content: contributorId,\n })\n\n return `${baseUrl}?type=component&component_type=${componentType}&component_hash=${componentHash}&contributor_id=${contributorId}&${params.toString()}`\n}\n\nexport function createAriaAttributes(\n elementType: ElementValue\n): Record<string, string> {\n const label = getElementTypeLabel(elementType)\n\n return {\n role: 'article',\n 'aria-label': `${label} - Strategic element`,\n 'aria-describedby': 'skhema-description',\n }\n}\n","import type { ComponentValue, ElementValue } from '@skhema/method/vocabulary'\nimport { renderComponentCardHtml } from '../render/index.js'\nimport {\n CARD_SHADOW_LG,\n PRIMARY_HOVER_HEX,\n PRIMARY_PRESSED_HEX,\n} from '../styles/design-tokens.js'\nimport {\n shouldTrackAnalytics,\n trackComponentClick,\n trackComponentEmbedLoad,\n} from '../utils/analytics.js'\nimport {\n getComponentTypeLabel,\n getElementTypesForComponent,\n isValidComponentType,\n validateElementBelongsToComponent,\n} from '../utils/component-validation.js'\nimport { generateComponentHash } from '../utils/hash.js'\nimport {\n generateComponentRedirectUrl,\n generateComponentStructuredData,\n} from '../utils/seo.js'\nimport type { SkhemaElement } from './SkhemaElement.js'\nimport type {\n ComponentContentData,\n ComponentEmbedAnalytics,\n NestedElementData,\n SkhemaComponentAttributes,\n SkhemaComponentEventMap,\n} from './types.js'\n\n// Browser-only styles. The card STRUCTURE + base colours come from the shared,\n// email-safe `@skhema/embed/render` output (inline hex). This block carries the\n// progressive enhancements (hover/transition over `.skhema-card`) plus the\n// shadow-DOM-only skeleton + error states (unchanged CSS-var styling).\nconst styles = `\n:host {\n --skhema-primary: hsl(344 57% 54%);\n --skhema-primary-hover: hsl(344 50% 47%);\n --skhema-secondary: hsl(345 100% 75%);\n\n /* Light mode colors */\n --skhema-bg: hsl(0 0% 100%);\n --skhema-card: hsl(0 0% 100%);\n --skhema-border: hsl(214.3 31.8% 91.4%);\n --skhema-text: hsl(222.2 84% 4.9%);\n --skhema-text-muted: hsl(215.4 16.3% 46.9%);\n --skhema-accent: hsl(210 40% 96%);\n --skhema-surface-subtle: hsl(210 40% 97%);\n\n /* Shadows */\n --skhema-shadow: 0 1px 3px 0 hsl(0 0 0 / 0.1), 0 1px 2px -1px hsl(0 0 0 / 0.1);\n --skhema-shadow-md: 0 4px 6px -1px hsl(0 0 0 / 0.1), 0 2px 4px -2px hsl(0 0 0 / 0.1);\n --skhema-shadow-lg: 0 10px 15px -3px hsl(0 0 0 / 0.1), 0 4px 6px -4px hsl(0 0 0 / 0.1);\n --skhema-radius: 0.1rem;\n\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif;\n line-height: 1.5;\n color: var(--skhema-text);\n}\n\n/* Dark mode (skeleton/error only) */\n.skhema-component-card[data-theme=\"dark\"],\n.skhema-skeleton[data-theme=\"dark\"] {\n --skhema-bg: hsl(222.2 84% 4.9%);\n --skhema-card: hsl(222.2 84% 4.9%);\n --skhema-border: hsl(217.2 32.6% 17.5%);\n --skhema-text: hsl(210 40% 98%);\n --skhema-text-muted: hsl(215 20.2% 65.1%);\n --skhema-accent: hsl(217.2 32.6% 17.5%);\n --skhema-surface-subtle: hsl(217.2 32.6% 12%);\n}\n\n/* Progressive enhancement over the inline-styled, rendered card. Inline styles\n win on specificity, so the changed properties use !important to layer on. */\n.skhema-card {\n transition: box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1), transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.skhema-card:hover {\n box-shadow: ${CARD_SHADOW_LG} !important;\n transform: translateY(-1px);\n}\n\n.skhema-save-btn {\n transition: background 0.15s ease;\n}\n\n.skhema-save-btn:hover {\n background: ${PRIMARY_HOVER_HEX} !important;\n}\n\n.skhema-save-btn:active {\n background: ${PRIMARY_PRESSED_HEX} !important;\n}\n\n/* Error card wrapper (browser-only state) */\n.skhema-component-card {\n background: var(--skhema-card);\n border: 1px solid var(--skhema-border);\n border-radius: calc(var(--skhema-radius) + 2px);\n box-shadow: var(--skhema-shadow);\n max-width: 600px;\n margin: 8px 0;\n overflow: hidden;\n}\n\n/* Error state */\n.skhema-error {\n background: hsl(0 93% 94%);\n border: 1px solid hsl(0 84% 60%);\n color: hsl(0 74% 42%);\n padding: 12px;\n border-radius: calc(var(--skhema-radius) + 2px);\n font-size: 13px;\n}\n\n.skhema-error-title {\n font-weight: 600;\n margin-bottom: 8px;\n}\n\n.skhema-error-list {\n margin: 0;\n padding-left: 16px;\n}\n\n/* Skeleton loading state */\n.skhema-skeleton {\n background: var(--skhema-card);\n border: 1px solid var(--skhema-border);\n border-radius: calc(var(--skhema-radius) + 2px);\n padding: 16px;\n box-shadow: var(--skhema-shadow);\n max-width: 600px;\n margin: 8px 0;\n animation: skeletonPulse 1.5s ease-in-out infinite;\n}\n\n.skhema-skeleton-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 12px;\n}\n\n.skhema-skeleton-badge {\n width: 32px;\n height: 20px;\n border-radius: 2px;\n background: linear-gradient(90deg,\n var(--skhema-border) 25%,\n var(--skhema-accent) 50%,\n var(--skhema-border) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n}\n\n.skhema-skeleton-text {\n flex: 1;\n}\n\n.skhema-skeleton-line {\n height: 12px;\n background: linear-gradient(90deg,\n var(--skhema-border) 25%,\n var(--skhema-accent) 50%,\n var(--skhema-border) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n border-radius: calc(var(--skhema-radius) + 2px);\n margin: 6px 0;\n}\n\n.skhema-skeleton-line.short { width: 40%; }\n.skhema-skeleton-line.medium { width: 70%; }\n\n.skhema-skeleton-content {\n margin: 16px 0;\n}\n\n@keyframes skeletonPulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.8; }\n}\n\n@keyframes shimmer {\n 0% { background-position: -200% 0; }\n 100% { background-position: 200% 0; }\n}\n\n/* Accessibility */\n@media (prefers-reduced-motion: reduce) {\n .skhema-card,\n .skhema-save-btn {\n transition: none;\n }\n}\n\n.skhema-structured-data {\n display: none !important;\n}\n`\n\nexport class SkhemaComponent extends HTMLElement {\n private shadow: ShadowRoot\n private contentData: ComponentContentData | null = null\n private componentConnected = false\n private hasTrackedLoad = false\n private pendingRender: number | null = null\n private themeObserver: MutationObserver | null = null\n private mediaQueryListener: MediaQueryList | null = null\n\n constructor() {\n super()\n this.shadow = this.attachShadow({ mode: 'closed' })\n this.renderSkeleton()\n }\n\n static get observedAttributes(): (keyof SkhemaComponentAttributes)[] {\n return [\n 'component-type',\n 'contributor-id',\n 'author-name',\n 'author-slug',\n 'contributor-name',\n 'title',\n 'theme',\n 'track-analytics',\n 'source-url',\n ]\n }\n\n connectedCallback() {\n if (this.componentConnected) return\n this.componentConnected = true\n\n try {\n this.addPreconnectHints()\n\n // Listen for child element ready events (fired by children that upgrade after us)\n this.addEventListener('skhema:element-ready', () => {\n this.scheduleRender()\n })\n\n // Children may have already upgraded and dispatched skhema:element-ready\n // before our listener was set up (registration order: skhema-element first).\n // Wait for any pending custom element upgrades, then render.\n if (typeof customElements.whenDefined === 'function') {\n customElements.whenDefined('skhema-element').then(() => {\n this.scheduleRender()\n })\n } else {\n this.scheduleRender()\n }\n\n this.setupThemeListeners()\n } catch (error) {\n this.renderError('Failed to initialize component', error)\n }\n }\n\n disconnectedCallback() {\n this.cleanupThemeListeners()\n if (this.pendingRender !== null) {\n cancelAnimationFrame(this.pendingRender)\n this.pendingRender = null\n }\n }\n\n attributeChangedCallback(\n _name: keyof SkhemaComponentAttributes,\n oldValue: string | null,\n newValue: string | null\n ) {\n if (oldValue !== newValue && this.componentConnected) {\n this.scheduleRender()\n }\n }\n\n private scheduleRender() {\n if (this.pendingRender !== null) {\n cancelAnimationFrame(this.pendingRender)\n }\n this.pendingRender = requestAnimationFrame(() => {\n this.pendingRender = null\n this.render()\n })\n }\n\n private render() {\n const validation = this.validateComponentAttributes()\n\n if (!validation.isValid) {\n this.renderError('Invalid component attributes', validation.errors)\n return\n }\n\n const componentType = validation.componentType!\n const contributorId = validation.contributorId!\n const title = this.getAttribute('title') || ''\n\n // Gather child element data\n const childElements = this.gatherChildElements(componentType)\n\n if (childElements.length === 0) {\n this.renderError('Component requires child elements', [\n 'Add <skhema-element> children inside this component',\n ])\n return\n }\n\n const componentHash = generateComponentHash(\n childElements.map((el) => ({\n elementType: el.elementType,\n content: el.content,\n }))\n )\n\n this.contentData = {\n contributor_id: contributorId,\n component_type: componentType,\n component_hash: componentHash,\n title,\n elements: childElements.map((el) => ({\n element_type: el.elementType,\n content: el.content,\n content_hash: el.contentHash,\n })),\n source_url: this.getAttribute('source-url') || window.location.href,\n timestamp: new Date().toISOString(),\n page_title: document.title,\n }\n\n this.renderContent(childElements)\n this.addStructuredData()\n this.trackLoad()\n }\n\n private validateComponentAttributes(): {\n isValid: boolean\n errors: string[]\n componentType?: ComponentValue\n contributorId?: string\n } {\n const errors: string[] = []\n const componentType = this.getAttribute('component-type')\n const contributorId = this.getAttribute('contributor-id')\n\n if (!componentType) {\n errors.push('Missing required attribute: component-type')\n } else if (!isValidComponentType(componentType)) {\n errors.push(`Invalid component-type \"${componentType}\"`)\n }\n\n if (!contributorId) {\n errors.push('Missing required attribute: contributor-id')\n } else if (contributorId.trim().length === 0) {\n errors.push('contributor-id cannot be empty')\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n componentType: isValidComponentType(componentType || '')\n ? (componentType as ComponentValue)\n : undefined,\n contributorId: contributorId || undefined,\n }\n }\n\n private gatherChildElements(\n componentType: ComponentValue\n ): NestedElementData[] {\n const children = Array.from(\n this.querySelectorAll('skhema-element')\n ) as SkhemaElement[]\n\n const elements: NestedElementData[] = []\n\n for (const child of children) {\n const data = child.getElementData?.()\n if (!data) continue\n\n // Validate element belongs to this component type\n if (!validateElementBelongsToComponent(data.elementType, componentType)) {\n console.warn(\n `skhema-component: element type \"${data.elementType}\" does not belong to component type \"${componentType}\"`\n )\n // Warn but don't block — still include the element\n }\n\n elements.push(data)\n }\n\n // Sort by elementFlow order for the component type\n const orderedTypes = getElementTypesForComponent(componentType)\n\n elements.sort((a, b) => {\n const aIdx = orderedTypes.indexOf(a.elementType as ElementValue)\n const bIdx = orderedTypes.indexOf(b.elementType as ElementValue)\n // Unknown types go to the end\n const aOrder = aIdx === -1 ? orderedTypes.length : aIdx\n const bOrder = bIdx === -1 ? orderedTypes.length : bIdx\n return aOrder - bOrder\n })\n\n return elements\n }\n\n private renderContent(elements: NestedElementData[]) {\n if (!this.contentData) return\n\n const { component_type, contributor_id, title } = this.contentData\n const componentLabel = getComponentTypeLabel(component_type)\n\n const themeAttribute = this.getAttribute('theme') || 'auto'\n const actualTheme = this.getActualTheme(themeAttribute)\n\n const redirectUrl = generateComponentRedirectUrl(\n this.contentData.component_hash,\n component_type,\n contributor_id\n )\n\n // Set ARIA attributes\n this.setAttribute('role', 'article')\n this.setAttribute(\n 'aria-label',\n `${componentLabel} component${title ? ` — ${title}` : ''}`\n )\n\n // Card structure + base styling come from the shared, email-safe renderer\n // (single source of truth). The renderer groups elements by type; the\n // browser-only `<style>` layers hover / transitions on top.\n const cardHtml = renderComponentCardHtml({\n componentType: component_type,\n title,\n elements: elements.map((el) => ({\n elementType: el.elementType,\n content: el.content,\n })),\n saveUrl: redirectUrl,\n authorName:\n this.getAttribute('author-name') ||\n this.getAttribute('contributor-name'),\n authorSlug: this.getAttribute('author-slug'),\n contributorId: contributor_id,\n theme: actualTheme,\n })\n\n this.shadow.innerHTML = `<style>${styles}</style>${cardHtml}<slot style=\"display:none;\"></slot>`\n\n // Add click listener\n const saveBtn = this.shadow.querySelector(\n '.skhema-save-btn'\n ) as HTMLAnchorElement\n if (saveBtn) {\n saveBtn.addEventListener('click', (event) => {\n this.handleSaveClick(event)\n })\n }\n }\n\n private getActualTheme(themeAttribute: string): 'light' | 'dark' {\n if (themeAttribute === 'light' || themeAttribute === 'dark') {\n return themeAttribute\n }\n\n const htmlElement = document.documentElement\n const bodyElement = document.body\n\n const htmlTheme =\n htmlElement.getAttribute('data-theme') ||\n htmlElement.getAttribute('theme') ||\n htmlElement.className.match(/theme-(\\w+)/)?.[1]\n\n const bodyTheme =\n bodyElement.getAttribute('data-theme') ||\n bodyElement.getAttribute('theme') ||\n bodyElement.className.match(/theme-(\\w+)/)?.[1]\n\n const hasDarkClass =\n htmlElement.classList.contains('dark') ||\n bodyElement.classList.contains('dark') ||\n htmlElement.classList.contains('dark-mode') ||\n bodyElement.classList.contains('dark-mode')\n\n if (hasDarkClass || htmlTheme === 'dark' || bodyTheme === 'dark') {\n return 'dark'\n }\n\n const computedStyles = window.getComputedStyle(htmlElement)\n const colorScheme = computedStyles.getPropertyValue('color-scheme')\n if (colorScheme && colorScheme.includes('dark')) {\n return 'dark'\n }\n\n if (\n window.matchMedia &&\n window.matchMedia('(prefers-color-scheme: dark)').matches\n ) {\n return 'dark'\n }\n\n return 'light'\n }\n\n private addPreconnectHints() {\n if (\n document.querySelector(\n 'link[rel=\"preconnect\"][href*=\"analytics.skhema.com\"]'\n )\n ) {\n return\n }\n\n try {\n const preconnectApi = document.createElement('link')\n preconnectApi.rel = 'preconnect'\n preconnectApi.href = 'https://analytics.skhema.com'\n document.head.appendChild(preconnectApi)\n\n const dnsPrefetch = document.createElement('link')\n dnsPrefetch.rel = 'dns-prefetch'\n dnsPrefetch.href = 'https://skhema.com'\n document.head.appendChild(dnsPrefetch)\n } catch (error) {\n console.debug('Failed to add preconnect hints:', error)\n }\n }\n\n private renderSkeleton() {\n const themeAttribute = this.getAttribute('theme') || 'auto'\n const actualTheme = this.getActualTheme(themeAttribute)\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n\n <div class=\"skhema-skeleton\" data-theme=\"${actualTheme}\">\n <div class=\"skhema-skeleton-header\">\n <div class=\"skhema-skeleton-badge\"></div>\n <div class=\"skhema-skeleton-text\">\n <div class=\"skhema-skeleton-line medium\"></div>\n </div>\n </div>\n <div class=\"skhema-skeleton-content\">\n <div class=\"skhema-skeleton-line short\"></div>\n <div class=\"skhema-skeleton-line\"></div>\n <div class=\"skhema-skeleton-line short\"></div>\n <div class=\"skhema-skeleton-line\"></div>\n <div class=\"skhema-skeleton-line medium\"></div>\n </div>\n </div>\n\n <slot style=\"display:none;\"></slot>\n `\n }\n\n private renderError(title: string, errors: string | string[] | unknown) {\n const errorList = Array.isArray(errors) ? errors : [String(errors)]\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n\n <div class=\"skhema-component-card\">\n <div class=\"skhema-error\">\n <div class=\"skhema-error-title\">Skhema Component Error: ${title}</div>\n <ul class=\"skhema-error-list\">\n ${errorList.map((error) => `<li>${error}</li>`).join('')}\n </ul>\n </div>\n </div>\n\n <slot style=\"display:none;\"></slot>\n `\n\n this.dispatchEvent(\n new CustomEvent('skhema:error', {\n detail: { error: title, details: errors },\n bubbles: true,\n })\n )\n }\n\n private addStructuredData() {\n if (!this.contentData) return\n\n const { component_type, contributor_id, title, elements, source_url } =\n this.contentData\n\n const structuredData = generateComponentStructuredData(\n title,\n component_type,\n contributor_id,\n elements.map((el) => ({\n elementType: el.element_type,\n content: el.content,\n })),\n source_url\n )\n\n const script = document.createElement('script')\n script.type = 'application/ld+json'\n script.textContent = JSON.stringify(structuredData)\n script.className = 'skhema-structured-data'\n document.head.appendChild(script)\n }\n\n private async trackLoad() {\n if (\n !shouldTrackAnalytics(this as HTMLElement) ||\n !this.contentData ||\n this.hasTrackedLoad\n ) {\n return\n }\n\n this.hasTrackedLoad = true\n\n const analytics: ComponentEmbedAnalytics = {\n contributorId: this.contentData.contributor_id,\n componentType: this.contentData.component_type,\n componentHash: this.contentData.component_hash,\n title: this.contentData.title,\n elements: this.contentData.elements.map((el) => ({\n elementType: el.element_type,\n content: el.content,\n contentHash: el.content_hash,\n })),\n pageUrl: window.location.href,\n pageTitle: document.title,\n timestamp: Date.now(),\n userAgent: navigator.userAgent,\n }\n\n await trackComponentEmbedLoad(analytics)\n\n this.dispatchEvent(\n new CustomEvent('skhema:component-load', {\n detail: analytics,\n bubbles: true,\n })\n )\n }\n\n private async handleSaveClick(_event: Event) {\n if (!this.contentData) return\n\n if (shouldTrackAnalytics(this as HTMLElement)) {\n await trackComponentClick(this.contentData)\n }\n\n this.dispatchEvent(\n new CustomEvent('skhema:component-click', {\n detail: this.contentData,\n bubbles: true,\n })\n )\n }\n\n // Public API\n public getContentData(): ComponentContentData | null {\n return this.contentData\n }\n\n public refresh(): void {\n this.render()\n }\n\n private setupThemeListeners(): void {\n const themeAttribute = this.getAttribute('theme')\n if (themeAttribute === 'auto' || !themeAttribute) {\n if (window.matchMedia) {\n this.mediaQueryListener = window.matchMedia(\n '(prefers-color-scheme: dark)'\n )\n const handleThemeChange = () => this.updateTheme()\n this.mediaQueryListener.addEventListener('change', handleThemeChange)\n }\n\n this.themeObserver = new MutationObserver(() => this.updateTheme())\n\n this.themeObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme'],\n })\n\n this.themeObserver.observe(document.body, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme'],\n })\n }\n }\n\n private cleanupThemeListeners(): void {\n if (this.themeObserver) {\n this.themeObserver.disconnect()\n this.themeObserver = null\n }\n if (this.mediaQueryListener) {\n this.mediaQueryListener = null\n }\n }\n\n private updateTheme(): void {\n // Theme is baked into the rendered card's inline hex, so re-render rather\n // than flip an attribute.\n const themeAttribute = this.getAttribute('theme') || 'auto'\n if (themeAttribute === 'auto' && this.contentData) {\n this.scheduleRender()\n }\n }\n}\n\n// Type augmentation for custom events and JSX elements\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface HTMLElementEventMap extends SkhemaComponentEventMap {}\n\n interface SkhemaComponentJSX extends Partial<SkhemaComponentAttributes> {\n [key: string]: unknown\n }\n\n interface JSXIntrinsicElements {\n 'skhema-component': SkhemaComponentJSX\n }\n}\n","import { renderElementCardHtml } from '../render/index.js'\nimport {\n CARD_SHADOW_LG,\n PRIMARY_HEX,\n PRIMARY_HOVER_HEX,\n PRIMARY_PRESSED_HEX,\n} from '../styles/design-tokens.js'\nimport {\n shouldTrackAnalytics,\n trackClick,\n trackEmbedLoad,\n} from '../utils/analytics.js'\nimport { generateContentHash } from '../utils/hash.js'\nimport { validateContentSecurity } from '../utils/sanitization.js'\nimport {\n createAriaAttributes,\n createMetaTags,\n generateRedirectUrl,\n generateStructuredData,\n} from '../utils/seo.js'\nimport { validateAttributes } from '../utils/validation.js'\nimport type {\n ContentData,\n EmbedAnalytics,\n NestedElementData,\n SkhemaElementAttributes,\n SkhemaElementEventMap,\n} from './types.js'\n\n// Browser-only styles. The card STRUCTURE + base colours now come from the\n// shared, email-safe `@skhema/embed/render` output (inline hex), so this block\n// only carries: (1) the progressive enhancements email/CLI can't have — hover\n// + transitions over the rendered `.skhema-card` — and (2) the shadow-DOM-only\n// skeleton + error states (which keep the original CSS-var styling verbatim).\nconst styles = `\n:host {\n /* Skhema Brand Colors - matching UI library */\n --skhema-primary: hsl(344 57% 54%); /* #cd476a */\n --skhema-primary-hover: hsl(344 50% 47%); /* #b53d5e */\n --skhema-primary-pressed: hsl(343 50% 41%); /* #9d3552 */\n --skhema-secondary: hsl(345 100% 75%); /* #ff82a2 */\n\n /* Light mode colors */\n --skhema-bg: hsl(0 0% 100%);\n --skhema-card: hsl(0 0% 100%);\n --skhema-border: hsl(214.3 31.8% 91.4%);\n --skhema-text: hsl(222.2 84% 4.9%);\n --skhema-text-muted: hsl(215.4 16.3% 46.9%);\n --skhema-accent: hsl(210 40% 96%);\n\n /* Shadows matching UI library */\n --skhema-shadow: 0 1px 3px 0 hsl(0 0 0 / 0.1), 0 1px 2px -1px hsl(0 0 0 / 0.1);\n --skhema-shadow-md: 0 4px 6px -1px hsl(0 0 0 / 0.1), 0 2px 4px -2px hsl(0 0 0 / 0.1);\n --skhema-shadow-lg: 0 10px 15px -3px hsl(0 0 0 / 0.1), 0 4px 6px -4px hsl(0 0 0 / 0.1);\n --skhema-radius: 0.1rem;\n\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Inter', sans-serif;\n line-height: 1.5;\n color: var(--skhema-text);\n}\n\n/* Dark mode styles - applied via data-theme attribute (skeleton/error only) */\n.skhema-element-card[data-theme=\"dark\"],\n.skhema-skeleton[data-theme=\"dark\"] {\n --skhema-bg: hsl(222.2 84% 4.9%);\n --skhema-card: hsl(222.2 84% 4.9%);\n --skhema-border: hsl(217.2 32.6% 17.5%);\n --skhema-text: hsl(210 40% 98%);\n --skhema-text-muted: hsl(215 20.2% 65.1%);\n --skhema-accent: hsl(217.2 32.6% 17.5%);\n}\n\n/* Progressive enhancement over the inline-styled, rendered card. Inline styles\n win on specificity, so the changed properties use !important to layer on. */\n.skhema-card {\n transition: box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1), transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.skhema-card:hover {\n box-shadow: ${CARD_SHADOW_LG} !important;\n transform: translateY(-1px);\n}\n\n.skhema-card[data-skhema-kind=\"element\"]:hover {\n border-color: ${PRIMARY_HEX} !important;\n}\n\n.skhema-save-btn {\n transition: background 0.15s ease;\n}\n\n.skhema-save-btn:hover {\n background: ${PRIMARY_HOVER_HEX} !important;\n}\n\n.skhema-save-btn:active {\n background: ${PRIMARY_PRESSED_HEX} !important;\n}\n\n/* Error card wrapper (browser-only state) */\n.skhema-element-card {\n background: var(--skhema-card);\n border: 1px solid var(--skhema-border);\n border-radius: calc(var(--skhema-radius) + 2px);\n box-shadow: var(--skhema-shadow);\n max-width: 600px;\n margin: 8px 0;\n overflow: hidden;\n}\n\n/* Error state */\n.skhema-error {\n background: hsl(0 93% 94%);\n border: 1px solid hsl(0 84% 60%);\n color: hsl(0 74% 42%);\n padding: 12px;\n border-radius: calc(var(--skhema-radius) + 2px);\n font-size: 13px;\n}\n\n.skhema-error-title {\n font-weight: 600;\n margin-bottom: 8px;\n}\n\n.skhema-error-list {\n margin: 0;\n padding-left: 16px;\n}\n\n/* Skeleton loading state */\n.skhema-skeleton {\n background: var(--skhema-card);\n border: 1px solid var(--skhema-border);\n border-radius: calc(var(--skhema-radius) + 2px);\n padding: 16px;\n box-shadow: var(--skhema-shadow);\n max-width: 600px;\n margin: 8px 0;\n animation: skeletonPulse 1.5s ease-in-out infinite;\n}\n\n.skhema-skeleton-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 12px;\n}\n\n.skhema-skeleton-badge {\n width: 32px;\n height: 20px;\n border-radius: 2px;\n background: linear-gradient(90deg,\n var(--skhema-border) 25%,\n var(--skhema-accent) 50%,\n var(--skhema-border) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n}\n\n.skhema-skeleton-text {\n flex: 1;\n}\n\n.skhema-skeleton-line {\n height: 12px;\n background: linear-gradient(90deg,\n var(--skhema-border) 25%,\n var(--skhema-accent) 50%,\n var(--skhema-border) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n border-radius: calc(var(--skhema-radius) + 2px);\n margin: 6px 0;\n}\n\n.skhema-skeleton-line.short {\n width: 40%;\n}\n\n.skhema-skeleton-line.medium {\n width: 70%;\n}\n\n.skhema-skeleton-content {\n margin: 16px 0;\n}\n\n@keyframes skeletonPulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.8;\n }\n}\n\n@keyframes shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n\n/* Accessibility */\n@media (prefers-reduced-motion: reduce) {\n .skhema-card,\n .skhema-save-btn {\n transition: none;\n }\n}\n\n.skhema-structured-data {\n display: none !important;\n}\n`\n\nexport class SkhemaElement extends HTMLElement {\n private shadow: ShadowRoot\n private contentData: ContentData | null = null\n private componentConnected = false\n private hasTrackedLoad = false\n private nestedMode = false\n private themeObserver: MutationObserver | null = null\n private mediaQueryListener: MediaQueryList | null = null\n\n constructor() {\n super()\n this.shadow = this.attachShadow({ mode: 'closed' })\n // Show skeleton immediately while component initializes\n this.renderSkeleton()\n }\n\n static get observedAttributes(): (keyof SkhemaElementAttributes)[] {\n return [\n 'element-type',\n 'contributor-id',\n 'author-name',\n 'author-slug',\n 'contributor-name',\n 'content',\n 'source-url',\n 'theme',\n 'track-analytics',\n ]\n }\n\n connectedCallback() {\n if (this.componentConnected) return\n this.componentConnected = true\n\n // Check if nested inside a <skhema-component>\n if (this.closest('skhema-component')) {\n this.nestedMode = true\n\n // Validate and parse content\n const validation = validateAttributes(this as HTMLElement)\n if (!validation.isValid) {\n console.warn(\n 'skhema-element: invalid attributes in nested mode',\n validation.errors\n )\n return\n }\n\n const content = this.getContent()\n if (!content.trim()) {\n console.warn('skhema-element: empty content in nested mode')\n return\n }\n\n this.contentData = {\n contributor_id: validation.contributorId!,\n element_type: validation.elementType!,\n content: content,\n content_hash: generateContentHash(content),\n source_url: this.getAttribute('source-url') || window.location.href,\n timestamp: new Date().toISOString(),\n page_title: document.title,\n }\n\n // Clear shadow DOM — parent component handles rendering\n this.shadow.innerHTML = ''\n\n // Dispatch ready event so parent component can discover us\n this.dispatchEvent(\n new CustomEvent('skhema:element-ready', {\n bubbles: true,\n composed: true,\n detail: this.getElementData(),\n })\n )\n return\n }\n\n try {\n // Add preconnect hints for faster analytics loading\n this.addPreconnectHints()\n\n // Use requestAnimationFrame for smoother render\n requestAnimationFrame(() => {\n this.render()\n this.trackLoad()\n this.setupThemeListeners()\n })\n } catch (error) {\n this.renderError('Failed to initialize component', error)\n }\n }\n\n disconnectedCallback() {\n this.cleanupThemeListeners()\n }\n\n attributeChangedCallback(\n _name: keyof SkhemaElementAttributes,\n oldValue: string | null,\n newValue: string | null\n ) {\n if (oldValue !== newValue && this.componentConnected && !this.nestedMode) {\n this.render()\n }\n }\n\n /**\n * Returns element data for parent <skhema-component> consumption.\n */\n public getElementData(): NestedElementData | null {\n if (!this.contentData) return null\n return {\n elementType: this.contentData.element_type,\n content: this.contentData.content,\n contentHash: this.contentData.content_hash,\n }\n }\n\n private render() {\n const validation = validateAttributes(this as HTMLElement)\n\n if (!validation.isValid) {\n this.renderError('Invalid component attributes', validation.errors)\n return\n }\n\n const content = this.getContent()\n if (!content.trim()) {\n this.renderError('Component requires content', [\n 'Add content between the opening and closing tags, or use the content attribute',\n ])\n return\n }\n\n // Validate content security\n const securityValidation = validateContentSecurity(content)\n if (!securityValidation.isSecure) {\n this.renderError(\n 'Content security validation failed',\n securityValidation.issues\n )\n return\n }\n\n this.contentData = {\n contributor_id: validation.contributorId!,\n element_type: validation.elementType!,\n content: content,\n content_hash: generateContentHash(content),\n source_url: this.getAttribute('source-url') || window.location.href,\n timestamp: new Date().toISOString(),\n page_title: document.title,\n }\n\n this.renderContent()\n this.addStructuredData()\n }\n\n private getContent(): string {\n return this.getAttribute('content') || this.textContent || ''\n }\n\n private renderContent() {\n if (!this.contentData) return\n\n const { element_type, contributor_id, content } = this.contentData\n const redirectUrl = generateRedirectUrl(\n content,\n element_type,\n contributor_id\n )\n\n // Determine the actual theme to use\n const themeAttribute = this.getAttribute('theme') || 'auto'\n const actualTheme = this.getActualTheme(themeAttribute)\n\n // Set ARIA attributes on host element\n const ariaAttrs = createAriaAttributes(element_type)\n Object.entries(ariaAttrs).forEach(([key, value]) => {\n this.setAttribute(key, value)\n })\n\n // Card structure + base styling come from the shared, email-safe renderer\n // (single source of truth). The browser-only `<style>` layers hover /\n // transitions on top.\n const cardHtml = renderElementCardHtml({\n elementType: element_type,\n content,\n saveUrl: redirectUrl,\n authorName:\n this.getAttribute('author-name') ||\n this.getAttribute('contributor-name'),\n authorSlug: this.getAttribute('author-slug'),\n contributorId: contributor_id,\n theme: actualTheme,\n })\n\n this.shadow.innerHTML = `<style>${styles}</style>${cardHtml}`\n\n // Add click event listener\n const saveBtn = this.shadow.querySelector(\n '.skhema-save-btn'\n ) as HTMLAnchorElement\n if (saveBtn) {\n saveBtn.addEventListener('click', (event) => {\n this.handleSaveClick(event)\n })\n }\n }\n\n private getActualTheme(themeAttribute: string): 'light' | 'dark' {\n if (themeAttribute === 'light' || themeAttribute === 'dark') {\n return themeAttribute\n }\n\n // Auto mode - detect from system/page\n // First check if the page has set a data-theme or theme attribute\n const htmlElement = document.documentElement\n const bodyElement = document.body\n\n // Check common theme attributes on html or body\n const htmlTheme =\n htmlElement.getAttribute('data-theme') ||\n htmlElement.getAttribute('theme') ||\n htmlElement.className.match(/theme-(\\w+)/)?.[1]\n\n const bodyTheme =\n bodyElement.getAttribute('data-theme') ||\n bodyElement.getAttribute('theme') ||\n bodyElement.className.match(/theme-(\\w+)/)?.[1]\n\n // Check for dark mode classes\n const hasDarkClass =\n htmlElement.classList.contains('dark') ||\n bodyElement.classList.contains('dark') ||\n htmlElement.classList.contains('dark-mode') ||\n bodyElement.classList.contains('dark-mode')\n\n if (hasDarkClass || htmlTheme === 'dark' || bodyTheme === 'dark') {\n return 'dark'\n }\n\n // Check CSS custom properties that might indicate theme\n const computedStyles = window.getComputedStyle(htmlElement)\n const colorScheme = computedStyles.getPropertyValue('color-scheme')\n if (colorScheme && colorScheme.includes('dark')) {\n return 'dark'\n }\n\n // Check system preference\n if (\n window.matchMedia &&\n window.matchMedia('(prefers-color-scheme: dark)').matches\n ) {\n return 'dark'\n }\n\n // Default to light\n return 'light'\n }\n\n private addPreconnectHints() {\n // Only add preconnect hints once per page\n if (\n document.querySelector(\n 'link[rel=\"preconnect\"][href*=\"analytics.skhema.com\"]'\n )\n ) {\n return\n }\n\n try {\n // Preconnect to analytics API\n const preconnectApi = document.createElement('link')\n preconnectApi.rel = 'preconnect'\n preconnectApi.href = 'https://analytics.skhema.com'\n document.head.appendChild(preconnectApi)\n\n // DNS prefetch for main domain\n const dnsPrefetch = document.createElement('link')\n dnsPrefetch.rel = 'dns-prefetch'\n dnsPrefetch.href = 'https://skhema.com'\n document.head.appendChild(dnsPrefetch)\n } catch (error) {\n console.debug('Failed to add preconnect hints:', error)\n }\n }\n\n private renderSkeleton() {\n // Determine theme for skeleton\n const themeAttribute = this.getAttribute('theme') || 'auto'\n const actualTheme = this.getActualTheme(themeAttribute)\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n\n <div class=\"skhema-skeleton\" data-theme=\"${actualTheme}\">\n <div class=\"skhema-skeleton-header\">\n <div class=\"skhema-skeleton-badge\"></div>\n <div class=\"skhema-skeleton-text\">\n <div class=\"skhema-skeleton-line medium\"></div>\n </div>\n </div>\n <div class=\"skhema-skeleton-content\">\n <div class=\"skhema-skeleton-line\"></div>\n <div class=\"skhema-skeleton-line\"></div>\n <div class=\"skhema-skeleton-line medium\"></div>\n </div>\n </div>\n `\n }\n\n private renderError(title: string, errors: string | string[] | unknown) {\n const errorList = Array.isArray(errors) ? errors : [String(errors)]\n\n this.shadow.innerHTML = `\n <style>${styles}</style>\n\n <div class=\"skhema-element-card\">\n <div class=\"skhema-error\">\n <div class=\"skhema-error-title\">Skhema Component Error: ${title}</div>\n <ul class=\"skhema-error-list\">\n ${errorList.map((error) => `<li>${error}</li>`).join('')}\n </ul>\n </div>\n </div>\n `\n\n // Dispatch error event\n this.dispatchEvent(\n new CustomEvent('skhema:error', {\n detail: { error: title, details: errors },\n bubbles: true,\n })\n )\n }\n\n private addStructuredData() {\n if (!this.contentData) return\n\n const { content, element_type, contributor_id, source_url } =\n this.contentData\n\n // Add structured data to the document head\n const structuredData = generateStructuredData(\n content,\n element_type,\n contributor_id,\n source_url\n )\n const script = document.createElement('script')\n script.type = 'application/ld+json'\n script.textContent = JSON.stringify(structuredData)\n script.className = 'skhema-structured-data'\n document.head.appendChild(script)\n\n // Add meta tags for SEO\n const metaDiv = document.createElement('div')\n metaDiv.innerHTML = createMetaTags(content, element_type, contributor_id)\n metaDiv.className = 'skhema-structured-data'\n document.body.appendChild(metaDiv)\n }\n\n private async trackLoad() {\n if (\n !shouldTrackAnalytics(this as HTMLElement) ||\n !this.contentData ||\n this.hasTrackedLoad\n ) {\n return\n }\n\n this.hasTrackedLoad = true\n\n const analytics: EmbedAnalytics = {\n contributorId: this.contentData.contributor_id,\n elementType: this.contentData.element_type,\n contentHash: this.contentData.content_hash,\n content: this.contentData.content,\n pageUrl: window.location.href,\n pageTitle: document.title,\n timestamp: Date.now(),\n userAgent: navigator.userAgent,\n }\n\n await trackEmbedLoad(analytics)\n\n // Dispatch load event\n this.dispatchEvent(\n new CustomEvent('skhema:load', {\n detail: analytics,\n bubbles: true,\n })\n )\n }\n\n private async handleSaveClick(_event: Event) {\n if (!this.contentData) return\n\n // Track click analytics\n if (shouldTrackAnalytics(this as HTMLElement)) {\n await trackClick(this.contentData)\n }\n\n // Dispatch click event\n this.dispatchEvent(\n new CustomEvent('skhema:click', {\n detail: this.contentData,\n bubbles: true,\n })\n )\n }\n\n // Public API methods\n public getContentData(): ContentData | null {\n return this.contentData\n }\n\n public refresh(): void {\n if (!this.nestedMode) {\n this.render()\n }\n }\n\n private setupThemeListeners(): void {\n // Only set up listeners if theme is 'auto' or not specified\n const themeAttribute = this.getAttribute('theme')\n if (themeAttribute === 'auto' || !themeAttribute) {\n // Listen for system theme changes\n if (window.matchMedia) {\n this.mediaQueryListener = window.matchMedia(\n '(prefers-color-scheme: dark)'\n )\n const handleThemeChange = () => this.updateTheme()\n this.mediaQueryListener.addEventListener('change', handleThemeChange)\n }\n\n // Listen for theme changes on html/body elements\n this.themeObserver = new MutationObserver(() => this.updateTheme())\n\n // Observe both html and body for attribute changes\n this.themeObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme'],\n })\n\n this.themeObserver.observe(document.body, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme'],\n })\n }\n }\n\n private cleanupThemeListeners(): void {\n if (this.themeObserver) {\n this.themeObserver.disconnect()\n this.themeObserver = null\n }\n\n if (this.mediaQueryListener) {\n // Note: We can't remove the specific listener without storing the reference\n // but setting to null will allow garbage collection\n this.mediaQueryListener = null\n }\n }\n\n private updateTheme(): void {\n // Only update if component is using auto theme. Theme is now baked into the\n // rendered card's inline hex, so re-render rather than flip an attribute.\n const themeAttribute = this.getAttribute('theme') || 'auto'\n if (themeAttribute === 'auto' && this.contentData && !this.nestedMode) {\n this.renderContent()\n }\n }\n}\n\n// Type augmentation for custom events and JSX elements\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface HTMLElementEventMap extends SkhemaElementEventMap {}\n\n interface SkhemaElementJSX extends Partial<SkhemaElementAttributes> {\n [key: string]: unknown\n }\n\n // Module augmentation for JSX without using namespace\n interface JSXIntrinsicElements {\n 'skhema-element': SkhemaElementJSX\n }\n}\n","// CDN bundle entry point - automatically registers the components\nimport { SkhemaComponent } from './components/SkhemaComponent.js'\nimport { SkhemaElement } from './components/SkhemaElement.js'\n\n// Register the custom elements for CDN usage\nif (!customElements.get('skhema-element')) {\n customElements.define(\n 'skhema-element',\n SkhemaElement as CustomElementConstructor\n )\n}\n\nif (!customElements.get('skhema-component')) {\n customElements.define(\n 'skhema-component',\n SkhemaComponent as CustomElementConstructor\n )\n}\n\n// Expose on global for UMD builds\nif (typeof window !== 'undefined') {\n ;(\n window as unknown as {\n SkhemaElement: typeof SkhemaElement\n SkhemaComponent: typeof SkhemaComponent\n }\n ).SkhemaElement = SkhemaElement\n ;(\n window as unknown as {\n SkhemaElement: typeof SkhemaElement\n SkhemaComponent: typeof SkhemaComponent\n }\n ).SkhemaComponent = SkhemaComponent\n}\n\nexport { SkhemaComponent, SkhemaElement }\n"],"names":["COMPONENT_COLORS","diagnosis","bg","text","border","method","initiatives","measures","support","CARD_PALETTE","light","cardBg","textMuted","dark","PRIMARY_HEX","PRIMARY_HOVER_HEX","PRIMARY_PRESSED_HEX","CARD_SHADOW_LG","MONO_STACK","oklchToHex","token","overHex","m","match","L","parseFloat","C","H","alpha","h","Math","PI","a","cos","b","sin","lr","mr","sr","G","B","gamma","x","c","max","min","pow","rgb","br","bb","hex","replace","parseInt","slice","hexToRgb","map","round","toString","padStart","join","COMPONENT_TYPES","DIAGNOSIS","value","label","acronym","METHOD","INITIATIVES","MEASURES","SUPPORT","ELEMENT_TYPES","KEY_CHALLENGE","SUPPORTING_FACT","IMPACT","GUIDING_POLICY","COMPETITOR_MOVE","SCOPE","CONSTRAINT","SOLUTION","ASSUMPTION_HYPOTHESIS","EXPERIMENT","ACTION","INVESTMENT","ESTIMATE","BASELINE","OUTCOME","PERFORMANCE_VARIABLE","CAPABILITY","SYSTEM","PRINCIPLE","SKHEMA_MAPPING","elementFlow","isValidComponentType","componentType","Object","values","t","includes","validateElementBelongsToComponent","elementType","validElements","some","e","getComponentTypeLabel","type","find","getComponentTypeAcronym","substring","toUpperCase","sanitizeContent","content","sanitized","stripped","forEach","pattern","trim","stripUrls","split","word","test","length","isValidElementType","validateAttributes","element","errors","getAttribute","contributorId","validTypes","push","isValid","getElementTypeLabel","escapeHtml","escapeAttr","resolveBadgePalette","theme","surface","colors","badgeBg","badgeText","badgeBorder","topBorder","renderFooter","saveUrl","author","p","mutedColor","authorName","name","authorSlug","encodeURIComponent","charAt","toLowerCase","renderAuthorHtml","renderHeader","badge","typeLabel","labelColor","title","titleHtml","cardOpen","kind","renderElementCardHtml","data","componentValue","elements","entries","resolveComponentType","ANALYTICS_ENDPOINT","getTrackedEmbeds","cookie","document","row","startsWith","JSON","parse","decodeURIComponent","cutoff","Date","now","TRACKING_EXPIRY_HOURS","filter","item","timestamp","hasBeenTracked","contentHash","markAsTracked","tracked","limited","expires","stringify","toUTCString","setTrackedEmbeds","analyticsBatcher","constructor","this","batch","embeds","clicks","componentEmbeds","componentClicks","batchTimeout","BATCH_DELAY","MAX_BATCH_SIZE","addEmbedLoad","analytics","scheduleBatchSend","addClick","contentData","addComponentEmbedLoad","addComponentClick","clearTimeout","sendBatch","window","setTimeout","currentBatch","sendEmbeds","sendClicks","sendComponentEmbeds","sendComponentClicks","payload","action","events","embed","contributor_id","element_type","content_hash","page_url","pageUrl","page_title","pageTitle","user_agent","userAgent","sendWithRetry","error","console","debug","click","source_url","fetch","headers","body","credentials","keepalive","catch","component_type","component_hash","componentHash","el","flush","sendViaBeacon","navigator","sendBeacon","Blob","async","url","contentType","maxRetries","attempt","options","response","ok","status","Promise","resolve","shouldTrackAnalytics","generateContentHash","hash","cleanContent","i","charCodeAt","abs","generateRedirectUrl","baseUrl","sourceUrl","location","href","URLSearchParams","source","utm_source","utmSource","utm_medium","utmMedium","utm_campaign","utmCampaign","utm_content","generateComponentRedirectUrl","addEventListener","visibilityState","styles","SkhemaComponent","HTMLElement","super","componentConnected","hasTrackedLoad","pendingRender","themeObserver","mediaQueryListener","shadow","attachShadow","mode","renderSkeleton","observedAttributes","connectedCallback","addPreconnectHints","scheduleRender","customElements","whenDefined","then","setupThemeListeners","renderError","disconnectedCallback","cleanupThemeListeners","cancelAnimationFrame","attributeChangedCallback","_name","oldValue","newValue","requestAnimationFrame","render","validation","validateComponentAttributes","childElements","gatherChildElements","sort","typeCompare","localeCompare","toISOString","renderContent","addStructuredData","trackLoad","children","Array","from","querySelectorAll","child","getElementData","warn","orderedTypes","getElementTypesForComponent","aIdx","indexOf","bIdx","componentLabel","themeAttribute","actualTheme","getActualTheme","redirectUrl","setAttribute","cardHtml","groups","Map","existing","get","set","groupsHtml","contents","renderComponentCardHtml","innerHTML","saveBtn","querySelector","event","handleSaveClick","htmlElement","documentElement","bodyElement","htmlTheme","className","bodyTheme","classList","contains","colorScheme","getComputedStyle","getPropertyValue","matchMedia","matches","preconnectApi","createElement","rel","head","appendChild","dnsPrefetch","errorList","isArray","String","dispatchEvent","CustomEvent","detail","details","bubbles","structuredData","description","category","contributor","hasPart","analysisType","provider","isPartOf","dateCreated","platform","generateComponentStructuredData","script","textContent","trackComponentEmbedLoad","_event","trackComponentClick","getContentData","refresh","handleThemeChange","updateTheme","MutationObserver","observe","attributes","attributeFilter","disconnect","SkhemaElement","nestedMode","closest","getContent","composed","securityValidation","issues","isSecure","validateContentSecurity","ariaAttrs","role","key","generateStructuredData","metaDiv","createMetaTags","trackEmbedLoad","trackClick","define"],"mappings":"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"}