{"version":3,"file":"index.cjs","sources":["../../src/shared/inline-markup/parse-inline-markers.ts","../../src/shared/inline-markup/serialize-inline-markers.ts","../../src/shared/links/link-item.ts","../../src/shared/links/is-safe-href.ts","../../src/shared/links/resolve-link-attributes.ts","../../src/shared/span-styles/span-style-item.ts","../../src/shared/span-styles/resolve-class-names.ts","../../src/shared/span-styles/parse-style-declarations.ts","../../src/shared/span-styles/resolve-span-style-attributes.ts","../../src/shared/inline-markup/render-inline-markup.tsx","../../src/base-components/display/components/r-text/default-attribute-values.ts","../../src/base-components/display/components/r-text/enclosing-tags.ts","../../src/base-components/display/components/r-text/resolve-enclosing-tag.ts","../../src/base-components/display/components/r-text/RText.tsx","../../src/base-components/display/components/r-text/resolve-no-container.ts","../../src/component-identifiers.ts","../../src/base-components/display/components/r-text/display-card-entry.tsx","../../src/base-components/display/components/r-text/resolve-renders-own-element.ts","../../src/base-components/display/components/r-text/attribute-edit-mode-configs.ts","../../src/base-components/display/components/cloudinary-advanced-image/build-cloudinary-image-urls.ts","../../src/base-components/display/components/cloudinary-advanced-image/CloudinaryAdvancedImage.tsx","../../src/base-components/display/components/cloudinary-advanced-image/resolve-no-container.ts","../../src/base-components/display/components/cloudinary-advanced-image/display-card-entry.tsx","../../src/base-components/display/components/cloudinary-advanced-image/resolve-renders-own-element.ts","../../src/base-components/display/components/cloudinary-advanced-image/default-attribute-values.ts","../../src/base-components/display/components/cloudinary-advanced-image/attribute-edit-mode-configs.ts","../../src/base-components/display/components/r-video/build-video-delivery-urls.ts","../../src/base-components/display/components/r-video/RVideo.tsx","../../src/base-components/display/components/r-video/resolve-no-container.ts","../../src/base-components/display/components/r-video/display-card-entry.tsx","../../src/base-components/display/components/r-video/resolve-renders-own-element.ts","../../src/base-components/display/components/r-video/default-attribute-values.ts","../../src/base-components/display/components/r-video/attribute-edit-mode-configs.ts","../../src/shared/icons/render-icon-children.tsx","../../src/base-components/display/components/r-icon/icon-attribute-value.ts","../../src/base-components/display/components/r-icon/RIcon.tsx","../../src/base-components/display/components/r-icon/display-card-entry.tsx","../../src/base-components/display/components/r-icon/resolve-no-container.ts","../../src/base-components/display/components/r-icon/resolve-renders-own-element.ts","../../src/base-components/display/components/r-icon/default-attribute-values.ts","../../src/base-components/display/components/r-icon/attribute-edit-mode-configs.ts","../../src/shared/links/parse-inline-link-markers.ts","../../src/base-components/display/components/r-button/button-presets.ts","../../src/base-components/display/components/r-button/button-css-variables.ts","../../src/base-components/display/components/r-button/button-preset-styles.ts","../../src/base-components/display/components/r-button/button-instance-props.ts","../../src/base-components/display/components/r-button/resolve-button-preset.ts","../../src/base-components/display/components/r-button/button-attributes.ts","../../src/base-components/display/components/r-button/RButton.tsx","../../src/base-components/display/components/r-button/display-card-entry.tsx","../../src/base-components/display/components/r-button/resolve-no-container.ts","../../src/base-components/display/components/r-button/resolve-renders-own-element.ts","../../src/base-components/display/components/r-button/default-attribute-values.ts","../../src/base-components/display/components/r-button/attribute-edit-mode-configs.ts","../../src/base-components/display/components/r-divider/divider-attributes.ts","../../src/base-components/display/components/r-divider/divider-styles.ts","../../src/base-components/display/components/r-divider/RDivider.tsx","../../src/base-components/display/components/r-divider/resolve-no-container.ts","../../src/base-components/display/components/r-divider/display-card-entry.tsx","../../src/base-components/display/components/r-divider/resolve-renders-own-element.ts","../../src/base-components/display/components/r-divider/default-attribute-values.ts","../../src/base-components/display/components/r-divider/attribute-edit-mode-configs.ts","../../src/base-components/display/components/r-embed/embed-attributes.ts","../../src/base-components/display/components/r-embed/embed-providers.ts","../../src/base-components/display/components/r-embed/resolve-embed.ts","../../src/base-components/display/components/r-embed/REmbed.tsx","../../src/base-components/display/components/r-embed/resolve-no-container.ts","../../src/base-components/display/components/r-embed/display-card-entry.tsx","../../src/base-components/display/components/r-embed/resolve-renders-own-element.ts","../../src/base-components/display/components/r-embed/default-attribute-values.ts","../../src/base-components/display/components/r-embed/attribute-edit-mode-configs.ts","../../src/base-components/display/components/r-list/list-attributes.ts","../../src/base-components/display/components/r-list/RList.tsx","../../src/base-components/display/components/r-list/resolve-no-container.ts","../../src/base-components/display/components/r-list/display-card-entry.tsx","../../src/base-components/display/components/r-list/resolve-renders-own-element.ts","../../src/base-components/display/components/r-list/default-attribute-values.ts","../../src/base-components/display/components/r-list/attribute-edit-mode-configs.ts","../../src/base-components/display/components/r-accordion/accordion-attributes.ts","../../src/base-components/display/components/r-accordion/RAccordion.tsx","../../src/base-components/display/components/r-accordion/resolve-no-container.ts","../../src/base-components/display/components/r-accordion/display-card-entry.tsx","../../src/base-components/display/components/r-accordion/resolve-renders-own-element.ts","../../src/base-components/display/components/r-accordion/default-attribute-values.ts","../../src/base-components/display/components/r-accordion/attribute-edit-mode-configs.ts","../../src/base-components/display/components/r-mount/mount-attributes.ts","../../src/base-components/display/components/r-mount/RMount.tsx","../../src/base-components/display/components/r-mount/resolve-no-container.ts","../../src/base-components/display/card-groups.tsx","../../src/base-components/display/card-entries/card-entries.tsx","../../src/base-components/display/components/r-mount/display-card-entry.tsx","../../src/base-components/display/components/r-mount/resolve-renders-own-element.ts","../../src/base-components/display/components/r-mount/default-attribute-values.ts","../../src/base-components/display/components/r-mount/attribute-edit-mode-configs.ts","../../src/get-source-components-plugin.ts","../../src/base-components/display/component-attributes/component-name-to-modify-attribute.ts","../../src/base-components/display/component-attributes/attribute-list-for-component-types.ts","../../src/get-component-metadata-for-editing.ts","../../src/base-components/editor-preview/card-groups.tsx","../../src/base-components/editor-preview/card-entries/card-entries.tsx","../../src/base-components/editor-preview/components/r-text/preview-card-entry.tsx","../../src/base-components/editor-preview/components/cloudinary-advanced-image/preview-card-entry.tsx","../../src/base-components/editor-preview/components/r-video/preview-card-entry.tsx","../../src/base-components/editor-preview/components/r-icon/preview-card-entry.tsx","../../src/base-components/editor-preview/components/r-button/preview-card-entry.tsx","../../src/base-components/editor-preview/components/r-divider/preview-card-entry.tsx","../../src/base-components/editor-preview/components/r-embed/preview-card-entry.tsx","../../src/base-components/editor-preview/components/r-list/preview-card-entry.tsx","../../src/base-components/editor-preview/components/r-accordion/preview-card-entry.tsx","../../src/base-components/editor-preview/components/r-mount/preview-card-entry.tsx","../../src/get-preview-components.ts","../../src/base-components/display/components/r-button/button-library-stylesheet.ts","../../src/base-components/display/components/r-accordion/accordion-library-stylesheet.ts","../../src/component-library-module.ts"],"sourcesContent":["/**\n * The formatting one marker can carry. Independent flags rather than a union,\n * because a span can be bold and italic and struck at once.\n */\nexport type InlineMarks = { bold: boolean; italic: boolean; strike: boolean };\n\nexport type MarkupTextRun = { kind: 'text'; text: string };\n\nexport type MarkupSpanRun = {\n  kind: 'span';\n  text: string;\n  /**\n   * 1-based position in the link list, when the marker carried an index. A\n   * span can be marked without linking (`{b|word}`) and linked without marks\n   * (`{1|word}`), so this is optional and the marks are always present.\n   */\n  index?: number;\n  /**\n   * 1-based position in the styling list, when the marker carried a `.index`.\n   * Independent of everything else on the span: `{.1|word}` is styled only,\n   * `{b2.1|word}` is bold, linked, and styled.\n   */\n  styleIndex?: number;\n  marks: InlineMarks;\n};\n\nexport type MarkupBreakRun = { kind: 'break' };\n\nexport type MarkupRun = MarkupTextRun | MarkupSpanRun | MarkupBreakRun;\n\n/**\n * `{` + optional mark letters + optional link index + optional `.` and styling\n * index + `|` + text + `}`.\n *\n * Sticky, so it can be tried at one position without slicing the string. The\n * text is everything up to the first `}`, which is what makes markers\n * non-nesting.\n *\n * The dot is what tells the two indexes apart, and it reads as the CSS class\n * selector it usually stands for. It cannot be confused with a link index,\n * because a link index is bare digits and always comes first.\n */\nconst MARKER = /\\{([a-z]*)(\\d*)(?:\\.(\\d+))?\\|([^}]*)\\}/y;\n\n/**\n * One letter per mark. Order is not significant (`{bi1|x}` and `{ib1|x}` are\n * the same span) and repeats are harmless, because the letters resolve to a\n * set.\n *\n * **`r` must never become a mark letter.** `br` is the line-break token below,\n * and it is only unambiguous while `r` cannot appear in a mark sequence.\n */\nconst MARK_LETTERS: Readonly<Record<string, keyof InlineMarks>> = {\n  b: 'bold',\n  i: 'italic',\n  s: 'strike',\n};\n\n/** The one marker that carries no text: `{br|}`. */\nconst BREAK_TOKEN = 'br';\n\nconst NO_MARKS: InlineMarks = { bold: false, italic: false, strike: false };\n\n/**\n * Letters to a mark set, or `undefined` when any letter is not a mark. An\n * unknown letter makes the whole marker literal text rather than a span with\n * the bad letter ignored: an author who mistypes should see it.\n */\nconst resolveMarks = (letters: string): InlineMarks | undefined => {\n  if (letters === '') {\n    return { ...NO_MARKS };\n  }\n\n  const marks: InlineMarks = { ...NO_MARKS };\n  for (const letter of letters) {\n    const mark = MARK_LETTERS[letter];\n    if (mark === undefined) {\n      return undefined;\n    }\n    marks[mark] = true;\n  }\n  return marks;\n};\n\n/**\n * Splits authored text into plain runs, marked/linked spans, and line breaks.\n *\n * The grammar, and everything it deliberately leaves as literal text, is\n * specified in `rulecms/__docs__/__ARCHITECTURE__/INLINE_MARKUP_ARCHITECTURE.md`.\n * In short: a marker is `{`, then lowercase mark letters, then a link index,\n * then `.` and a styling index, then `|`, then its text, then `}`. At least one\n * of those three must be present, and everything that does not match that shape\n * stays verbatim — `{name}`, `{99}`, a lone `{`, JSON fragments, an unterminated\n * marker, an unknown mark letter, and empty span text.\n *\n * Indexes are 1-based and normalize through a base-10 parse, so `{01|x}` and\n * `{1|x}` point at the same entry. Resolving an index to an entry is the\n * caller's job; this stays pure and free of React.\n */\nexport function parseInlineMarkers(value: string): MarkupRun[] {\n  if (typeof value !== 'string' || value === '') {\n    return [];\n  }\n\n  const runs: MarkupRun[] = [];\n  let pendingText = '';\n  let cursor = 0;\n\n  const flushPendingText = () => {\n    if (pendingText !== '') {\n      runs.push({ kind: 'text', text: pendingText });\n      pendingText = '';\n    }\n  };\n\n  while (cursor < value.length) {\n    const open = value.indexOf('{', cursor);\n    if (open === -1) {\n      pendingText += value.slice(cursor);\n      break;\n    }\n\n    MARKER.lastIndex = open;\n    const match = MARKER.exec(value);\n    const run = match ? resolveRun(match[1], match[2], match[3], match[4]) : undefined;\n\n    if (run === undefined) {\n      // Not a marker: keep the brace as text and look for the next one.\n      pendingText += value.slice(cursor, open + 1);\n      cursor = open + 1;\n      continue;\n    }\n\n    pendingText += value.slice(cursor, open);\n    flushPendingText();\n    runs.push(run);\n    cursor = open + (match as RegExpExecArray)[0].length;\n  }\n\n  flushPendingText();\n\n  return runs;\n}\n\n/**\n * One matched `{…|…}` to the run it means, or `undefined` when the match is\n * not a marker after all and its opening brace is literal text.\n *\n * `styleDigits` is `undefined` when the marker carried no dot at all, which is\n * different from a dot with nothing after it — the regex does not match that,\n * so it never reaches here.\n */\nconst resolveRun = (\n  letters: string,\n  digits: string,\n  styleDigits: string | undefined,\n  text: string\n): MarkupRun | undefined => {\n  if (letters === BREAK_TOKEN) {\n    // A break carries nothing. `{br|x}` is a mistake, not a break with text.\n    return digits === '' && styleDigits === undefined && text === ''\n      ? { kind: 'break' }\n      : undefined;\n  }\n\n  if (letters === '' && digits === '' && styleDigits === undefined) {\n    return undefined;\n  }\n\n  // Empty text has no words to mark or link, and staying literal is what shows\n  // the author the mistake instead of swallowing the marker.\n  if (text === '') {\n    return undefined;\n  }\n\n  const marks = resolveMarks(letters);\n  if (marks === undefined) {\n    return undefined;\n  }\n\n  return {\n    kind: 'span',\n    text,\n    ...(digits === '' ? {} : { index: Number.parseInt(digits, 10) }),\n    ...(styleDigits === undefined ? {} : { styleIndex: Number.parseInt(styleDigits, 10) }),\n    marks,\n  };\n};\n","import type { InlineMarks, MarkupRun, MarkupSpanRun } from './parse-inline-markers';\n\n/**\n * Canonical letter order, so the same span always serializes the same way and\n * a source-mode author sees `{bi1|…}` rather than whichever order an editor\n * happened to apply the marks in.\n */\nconst MARK_LETTERS: ReadonlyArray<readonly [keyof InlineMarks, string]> = [\n  ['bold', 'b'],\n  ['italic', 'i'],\n  ['strike', 's'],\n];\n\nconst markLetters = (marks: InlineMarks): string =>\n  MARK_LETTERS.filter(([mark]) => marks[mark])\n    .map(([, letter]) => letter)\n    .join('');\n\n/**\n * A span back to its marker. Text that carries no marks, no link and no styling\n * is emitted bare: a marker with nothing to say would only be noise an author\n * has to read past, and it round-trips to the same runs either way.\n */\nconst formatSpan = (run: MarkupSpanRun): string => {\n  const letters = markLetters(run.marks);\n  const index = run.index === undefined ? '' : `${run.index}`;\n  const styleIndex = run.styleIndex === undefined ? '' : `.${run.styleIndex}`;\n\n  if (letters === '' && index === '' && styleIndex === '') {\n    return run.text;\n  }\n  return `{${letters}${index}${styleIndex}|${run.text}}`;\n};\n\n/** One run as authored text. */\nexport function formatInlineMarker(run: MarkupRun): string {\n  switch (run.kind) {\n    case 'text':\n      return run.text;\n    case 'break':\n      return '{br|}';\n    default:\n      return formatSpan(run);\n  }\n}\n\n/**\n * Runs back to the string they were parsed from.\n *\n * This is the writer half of the grammar, and it lives beside the parser so\n * that an editor never hand-assembles marker syntax. `parseInlineMarkers` and\n * this function are inverses for anything the parser produces, which is what\n * lets a rich editor round-trip through source mode without rewriting content\n * the author did not touch.\n */\nexport function serializeInlineMarkers(runs: readonly MarkupRun[]): string {\n  return runs.map(formatInlineMarker).join('');\n}\n","/**\n * One link entry as the composer stores it. Shared by every component that\n * links (text markers today, icons next), so nothing here knows about text.\n */\nexport type LinkItem = {\n  href?: string;\n  /** Author-facing name for identifying the entry in the composer. Never rendered. */\n  label?: string;\n  target?: string;\n  rel?: string;\n  title?: string;\n  ariaLabel?: string;\n  /** `true` = bare download attribute; string = suggested filename. */\n  download?: string | boolean;\n  hreflang?: string;\n  referrerPolicy?: string;\n};\n\n/**\n * Runtime guard for entries arriving through componentAttributes (untyped,\n * editor-authored data).\n *\n * Only object shape is checked. Field types are not: a single bad field must\n * cost that field, not the whole entry, so per-field validation belongs to\n * `resolveLinkAttributes` where each value is turned into an attribute.\n */\nexport function isLinkItem(value: unknown): value is LinkItem {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * True for a cleared slot. Clearing a link stores `{}` rather than removing\n * the array element, because marker indexes are 1-based positions: dropping an\n * element would silently repoint every later marker.\n */\nexport function isEmptyLinkItem(item: unknown): boolean {\n  if (!isLinkItem(item)) {\n    return true;\n  }\n  return Object.values(item).every(\n    (value) =>\n      value === undefined ||\n      value === null ||\n      (typeof value === 'string' && value.trim() === '')\n  );\n}\n\n/**\n * The entry a `{n|…}` marker points at, by 1-based position. Tolerant of bad\n * data on every axis — a non-array list, a fractional or out-of-range index,\n * a hole in the array — because none of it may throw during a render.\n */\nexport function getLinkItemAt(links: unknown, oneBasedIndex: number): LinkItem | undefined {\n  if (!Array.isArray(links)) {\n    return undefined;\n  }\n  if (!Number.isInteger(oneBasedIndex) || oneBasedIndex < 1) {\n    return undefined;\n  }\n  const entry = links[oneBasedIndex - 1];\n  return isLinkItem(entry) ? entry : undefined;\n}\n","/**\n * Schemes that can execute or smuggle a payload. Mirrors the denylist in\n * RuleCMS `text-safety/validate-text-content.ts`; the two must stay in step,\n * because that one warns at save and this one is the guarantee at render.\n */\nexport const REJECTED_HREF_SCHEMES: readonly string[] = [\n  'javascript',\n  // Netscape-era aliases for javascript:. No browser in use today runs them,\n  // but RuleCMS refuses them in prose, and an href accepting what the text\n  // field rejects is the kind of gap that outlives the reason for it.\n  'livescript',\n  'mocha',\n  'vbscript',\n  'data',\n  'file',\n  'blob',\n];\n\nconst SCHEME = /^([a-z][a-z0-9+.-]*):/;\n\n/**\n * Characters a browser ignores while reading a scheme. Stripped before the\n * scheme is matched so `java\\tscript:` and a leading NUL cannot hide one.\n */\nconst isIgnoredWhenReadingAScheme = (charCode: number): boolean =>\n  charCode <= 0x20 || charCode === 0x7f;\n\nconst schemeProbe = (value: string): string => {\n  let probe = '';\n  for (let index = 0; index < value.length; index += 1) {\n    if (!isIgnoredWhenReadingAScheme(value.charCodeAt(index))) {\n      probe += value[index];\n    }\n  }\n  return probe.toLowerCase();\n};\n\n/**\n * Whether a value is safe to put in an `href`.\n *\n * A denylist, not an allowlist. Customers link with git and ssh URLs, mobile\n * deep links, `mailto:`, `tel:`, `sms:`, custom app schemes, protocol-relative\n * `//host/path`, and bare `/path`, `#anchor`, `?q=1`, `./rel` — all of which\n * are accepted. A host is not required and neither is http/https. Only the\n * schemes that turn a link into code or an opaque payload are turned away.\n *\n * Rejection happens at render, never at save: the composer may use this for a\n * non-blocking warning, but stored content is not trusted content.\n */\nexport function isSafeHref(value: unknown): boolean {\n  if (typeof value !== 'string' || value.trim() === '') {\n    return false;\n  }\n\n  const match = SCHEME.exec(schemeProbe(value));\n  // No scheme at all: relative, fragment, query, or protocol-relative.\n  if (!match) {\n    return true;\n  }\n\n  return !REJECTED_HREF_SCHEMES.includes(match[1]);\n}\n","import type { AnchorHTMLAttributes } from 'react';\nimport { isLinkItem } from './link-item';\nimport type { LinkItem } from './link-item';\nimport { isSafeHref } from './is-safe-href';\n\n/**\n * The props an `<a>` receives, picked from React's own anchor attributes so\n * the object can be spread onto an element without a cast at the call site.\n * `href` is required: an anchor without one has no link role, is not\n * focusable, and is invisible to assistive technology while still looking\n * like a link.\n */\nexport type AnchorAttributes = Pick<\n  AnchorHTMLAttributes<HTMLAnchorElement>,\n  'target' | 'rel' | 'title' | 'aria-label' | 'download' | 'hrefLang' | 'referrerPolicy'\n> & { href: string };\n\nconst BLANK_TARGET = '_blank';\n\n/** `target=\"_blank\"` opens a window that can reach back into this one without these. */\nconst IMPLIED_REL_FOR_BLANK_TARGET = ['noopener', 'noreferrer'];\n\nconst cleanString = (value: unknown): string | undefined => {\n  if (typeof value !== 'string') {\n    return undefined;\n  }\n  const trimmed = value.trim();\n  return trimmed === '' ? undefined : trimmed;\n};\n\n/**\n * The author's tokens and their order are kept; only the missing implied ones\n * are appended. Deduping is case-insensitive because `rel` tokens are.\n */\nconst resolveRel = (authorRel: string | undefined, target: string | undefined): string | undefined => {\n  const tokens = authorRel ? authorRel.split(/\\s+/) : [];\n\n  if (target?.toLowerCase() !== BLANK_TARGET) {\n    return tokens.length > 0 ? tokens.join(' ') : undefined;\n  }\n\n  const present = new Set(tokens.map((token) => token.toLowerCase()));\n  const merged = [...tokens];\n  IMPLIED_REL_FOR_BLANK_TARGET.forEach((token) => {\n    if (!present.has(token)) {\n      merged.push(token);\n    }\n  });\n  return merged.join(' ');\n};\n\nconst resolveDownload = (download: LinkItem['download']): string | true | undefined => {\n  if (download === true) {\n    return true;\n  }\n  return cleanString(download);\n};\n\n/**\n * Turns a stored link entry into anchor props, or `undefined` when the entry\n * cannot produce a usable anchor — missing, cleared, blank `href`, or an\n * `href` the safety check rejects. Callers render the plain text in that case.\n *\n * Knows nothing about text, markers, or any one component: this is the piece\n * every linking component shares.\n */\nexport function resolveLinkAttributes(item: LinkItem | undefined): AnchorAttributes | undefined {\n  if (!isLinkItem(item)) {\n    return undefined;\n  }\n\n  const href = cleanString(item.href);\n  if (!isSafeHref(href)) {\n    return undefined;\n  }\n\n  const target = cleanString(item.target);\n  const rel = resolveRel(cleanString(item.rel), target);\n  const title = cleanString(item.title);\n  const ariaLabel = cleanString(item.ariaLabel);\n  const download = resolveDownload(item.download);\n  const hrefLang = cleanString(item.hreflang);\n  const referrerPolicy = cleanString(item.referrerPolicy);\n\n  return {\n    href: href as string,\n    ...(target !== undefined ? { target } : {}),\n    ...(rel !== undefined ? { rel } : {}),\n    ...(title !== undefined ? { title } : {}),\n    ...(ariaLabel !== undefined ? { 'aria-label': ariaLabel } : {}),\n    ...(download !== undefined ? { download } : {}),\n    ...(hrefLang !== undefined ? { hrefLang } : {}),\n    // Stored values are untyped strings; React narrows this prop to a union.\n    ...(referrerPolicy !== undefined\n      ? { referrerPolicy: referrerPolicy as AnchorAttributes['referrerPolicy'] }\n      : {}),\n  };\n}\n","/**\n * One styling entry as the composer stores it. Shared by every component that\n * renders inline markup, so nothing here knows about text.\n *\n * Two fields rather than one because they fail differently. Class names are\n * names of rules the customer's own CSS owns, and this library can neither see\n * them nor be sure they exist. Declarations are CSS this library has to\n * understand well enough to hand to a platform that wants an object rather\n * than a string.\n */\nexport type SpanStyleItem = {\n  /** Author-facing name for identifying the entry in the composer. Never rendered. */\n  label?: string;\n  /** Whitespace-separated class names, exactly as the author typed them. */\n  classNames?: string;\n  /** A CSS declaration list — `color: #c00; font-weight: 600` — without braces. */\n  declarations?: string;\n};\n\n/**\n * Runtime guard for entries arriving through componentAttributes (untyped,\n * editor-authored data).\n *\n * Only object shape is checked, for the same reason as `isLinkItem`: one bad\n * field must cost that field rather than the whole entry, so per-field\n * validation belongs to `resolveSpanStyleAttributes` where each value is turned\n * into something a platform can apply.\n */\nexport function isSpanStyleItem(value: unknown): value is SpanStyleItem {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * True for a cleared slot. Clearing an entry stores `{}` rather than removing\n * the array element, because marker indexes are 1-based positions: dropping an\n * element would silently repoint every later marker.\n *\n * A `label` alone is a cleared slot. It is the author's note to themselves\n * about which entry this is, and an entry that only has a name applies nothing.\n */\nexport function isEmptySpanStyleItem(item: unknown): boolean {\n  if (!isSpanStyleItem(item)) {\n    return true;\n  }\n  return [item.classNames, item.declarations].every(\n    (value) => typeof value !== 'string' || value.trim() === ''\n  );\n}\n\n/**\n * The entry a `{.n|…}` marker points at, by 1-based position. Tolerant of bad\n * data on every axis — a non-array list, a fractional or out-of-range index, a\n * hole in the array — because none of it may throw during a render.\n */\nexport function getSpanStyleItemAt(\n  spanStyles: unknown,\n  oneBasedIndex: number\n): SpanStyleItem | undefined {\n  if (!Array.isArray(spanStyles)) {\n    return undefined;\n  }\n  if (!Number.isInteger(oneBasedIndex) || oneBasedIndex < 1) {\n    return undefined;\n  }\n  const entry = spanStyles[oneBasedIndex - 1];\n  return isSpanStyleItem(entry) ? entry : undefined;\n}\n","/**\n * A class name is a *selector* into CSS the customer already ships. This\n * library cannot see those rules, so it cannot validate a name against\n * anything — the only useful question is whether the token could damage the\n * document it lands in, and the answer is almost always no: every renderer sets\n * `class` through a DOM property or a framework binding, never by pasting into\n * markup.\n *\n * So the filter is deliberately permissive. Utility frameworks make class names\n * that look like code — `md:hover:text-lg`, `w-1/2`, `text-[#c00]`,\n * `[&>svg]:size-4` — and a tidier-looking allowlist would reject those and be\n * read as a bug.\n */\n\n/** Whitespace separates tokens, so it cannot appear inside one. */\nconst TOKEN_SEPARATOR = /\\s+/;\n\n/**\n * Control characters, and the two brace characters. Braces are not dangerous in\n * a class attribute; they are rejected because a token containing one is nearly\n * always an inline marker the author pasted into the wrong field, and silently\n * accepting it would style nothing and explain nothing.\n */\n// eslint-disable-next-line no-control-regex -- control characters are what this matches\nconst REJECTED_IN_TOKEN = /[\\u0000-\\u001f\\u007f{}]/;\n\n/**\n * Bounds, so a pathological value cannot become a pathological `class`\n * attribute. Both are far past anything a person types by hand.\n */\nconst MAX_TOKENS = 50;\nconst MAX_TOKEN_LENGTH = 120;\n\nconst isUsableToken = (token: string): boolean =>\n  // Splitting a blank string yields one empty token, which is not a class name.\n  token !== '' && token.length <= MAX_TOKEN_LENGTH && !REJECTED_IN_TOKEN.test(token);\n\n/**\n * The `class` value for a styling entry, or `undefined` when it has none worth\n * setting.\n *\n * Duplicates collapse and order is the author's. One bad token costs that\n * token: the rest of a mostly-fine list still applies, which is the same\n * per-field tolerance `resolveLinkAttributes` gives a link entry.\n */\nexport function resolveClassNames(value: unknown): string | undefined {\n  if (typeof value !== 'string') {\n    return undefined;\n  }\n\n  const tokens = value.trim().split(TOKEN_SEPARATOR).filter(isUsableToken);\n  if (tokens.length === 0) {\n    return undefined;\n  }\n\n  return Array.from(new Set(tokens)).slice(0, MAX_TOKENS).join(' ');\n}\n","/**\n * One CSS declaration, normalized: a property name this module accepted and the\n * value it accepted for it, both trimmed and with nothing dropped in between.\n */\nexport type StyleDeclaration = { property: string; value: string };\n\n/**\n * Declarations are split on `;` without tracking quotes, so a value containing\n * a semicolon inside a string — `content: \"a;b\"` — is cut in half and both\n * halves are then rejected as malformed. That is an acceptable trade: the\n * properties worth setting on a run of words do not take string values, and a\n * quote-aware splitter is a second CSS parser to keep correct.\n */\nconst DECLARATION_SEPARATOR = ';';\n\n/** `--custom-property`, or an ordinary property with an optional vendor prefix. */\nconst PROPERTY_NAME = /^(--[a-zA-Z0-9-]+|-?[a-zA-Z][a-zA-Z0-9-]*)$/;\n\n/**\n * Properties that do something other than style text, whatever their value.\n *\n * `behavior` and `-moz-binding` attach script to an element through CSS in old\n * engines. They are long dead, and they are named here anyway: the cost is one\n * line, and the alternative is relying on every reader of this file knowing\n * that CSS was once executable.\n */\nconst REJECTED_PROPERTIES = new Set(['behavior', '-moz-binding']);\n\n/**\n * Constructs that make a declaration do more than describe appearance.\n *\n * `url(` is rejected rather than filtered because every reason to allow it is\n * better served elsewhere: a background image is a component, and a `url()` in\n * authored text is either a tracking pixel, a `data:` payload, or a\n * `javascript:` scheme an older engine will still run. A backslash is rejected\n * for the same reason in one step: CSS escapes let `\\75 rl(` spell `url(`, so\n * allowing escapes would mean decoding them before every other check.\n */\nconst REJECTED_IN_VALUE = [\n  'url(',\n  'expression(',\n  'javascript:',\n  'vbscript:',\n  '@',\n  '\\\\',\n  '<',\n  '>',\n  '/*',\n  '*/',\n];\n\n/** Control characters, and the braces that would end a declaration list. */\n// eslint-disable-next-line no-control-regex -- control characters are what this matches\nconst REJECTED_CHARACTERS = /[\\u0000-\\u001f\\u007f{}]/;\n\n/**\n * React drops `!important` from a style object and warns about it, while the\n * string-based ports would honour it — one authored value behaving differently\n * per platform. It comes off everywhere instead, and the class-names field is\n * the supported way to win a specificity fight.\n */\nconst IMPORTANT_FLAG = /\\s*!\\s*important\\s*$/i;\n\n/** Bounds well past anything typed by hand, so a pasted stylesheet cannot land here. */\nconst MAX_DECLARATIONS = 40;\nconst MAX_VALUE_LENGTH = 400;\n\nconst isUsableProperty = (property: string): boolean =>\n  PROPERTY_NAME.test(property) && !REJECTED_PROPERTIES.has(property.toLowerCase());\n\nconst isUsableValue = (value: string): boolean => {\n  if (value === '' || value.length > MAX_VALUE_LENGTH) {\n    return false;\n  }\n  if (REJECTED_CHARACTERS.test(value)) {\n    return false;\n  }\n  const lowered = value.toLowerCase();\n  return !REJECTED_IN_VALUE.some((rejected) => lowered.includes(rejected));\n};\n\n/**\n * A declaration to the pair it means, or `undefined` when it is malformed or\n * carries something this module will not apply.\n */\nconst parseDeclaration = (declaration: string): StyleDeclaration | undefined => {\n  const separator = declaration.indexOf(':');\n  if (separator === -1) {\n    return undefined;\n  }\n\n  // Only the first colon splits: `background: linear-gradient(…)` keeps the rest.\n  const property = declaration.slice(0, separator).trim();\n  const value = declaration.slice(separator + 1).replace(IMPORTANT_FLAG, '').trim();\n\n  if (!isUsableProperty(property) || !isUsableValue(value)) {\n    return undefined;\n  }\n  return { property, value };\n};\n\n/**\n * An authored CSS declaration list to the declarations that will be applied.\n *\n * The list is a denylist over values and an allowlist over property names,\n * which is the same shape as `text-safety/validate-text-content.ts` in the\n * composer and for the same reason: a person writing `color: #c00` should never\n * have to think about this function, while the handful of constructs that turn\n * a declaration into a request or a script are named and refused.\n *\n * A rejected declaration costs only itself. `color: red; behavior: url(x)`\n * still sets the colour, because dropping the whole entry over one bad\n * declaration would lose work the author can see is fine.\n */\nexport function parseStyleDeclarations(value: unknown): StyleDeclaration[] {\n  if (typeof value !== 'string') {\n    return [];\n  }\n\n  const parsed: StyleDeclaration[] = [];\n  const seen = new Set<string>();\n\n  for (const declaration of value.split(DECLARATION_SEPARATOR)) {\n    if (declaration.trim() === '') {\n      continue;\n    }\n    const pair = parseDeclaration(declaration);\n    if (pair === undefined) {\n      continue;\n    }\n    // Last one wins, as it would in a real declaration list.\n    if (seen.has(pair.property)) {\n      parsed[parsed.findIndex((existing) => existing.property === pair.property)] = pair;\n      continue;\n    }\n    seen.add(pair.property);\n    parsed.push(pair);\n    if (parsed.length === MAX_DECLARATIONS) {\n      break;\n    }\n  }\n\n  return parsed;\n}\n\n/**\n * Declarations back to a CSS string, for the places that want a `style`\n * attribute rather than an object — the composer's editing surface, and the\n * ports whose templates bind a string.\n */\nexport function formatStyleDeclarations(declarations: readonly StyleDeclaration[]): string {\n  return declarations.map(({ property, value }) => `${property}: ${value}`).join('; ');\n}\n","import type { CSSProperties } from 'react';\nimport { isSpanStyleItem } from './span-style-item';\nimport type { SpanStyleItem } from './span-style-item';\nimport { resolveClassNames } from './resolve-class-names';\nimport { parseStyleDeclarations } from './parse-style-declarations';\n\n/** The props a styled run's element receives, ready to spread. */\nexport type SpanStyleAttributes = { className?: string; style?: CSSProperties };\n\n/**\n * React wants `fontWeight`, authors write `font-weight`. Custom properties keep\n * their name — React passes `--brand` through untouched — and a vendor prefix\n * loses its leading dash and gains a capital, except `-ms-`, which React spells\n * lowercase.\n */\nconst toReactProperty = (property: string): string => {\n  if (property.startsWith('--')) {\n    return property;\n  }\n\n  const isVendorPrefixed = property.startsWith('-');\n  const camelCased = (isVendorPrefixed ? property.slice(1) : property).replace(\n    /-([a-z])/g,\n    (_, letter: string) => letter.toUpperCase()\n  );\n\n  if (!isVendorPrefixed || camelCased.startsWith('ms')) {\n    return camelCased;\n  }\n  return camelCased.charAt(0).toUpperCase() + camelCased.slice(1);\n};\n\n/**\n * Turns a stored styling entry into element props, or `undefined` when the\n * entry cannot produce any — missing, cleared, or holding nothing that survived\n * the checks in `resolveClassNames` and `parseStyleDeclarations`. Callers render\n * the plain text in that case, exactly as they do for a link that resolves to\n * nothing.\n *\n * Knows nothing about text, markers, or any one component: this is the piece\n * every component rendering inline markup shares.\n */\nexport function resolveSpanStyleAttributes(\n  item: SpanStyleItem | undefined\n): SpanStyleAttributes | undefined {\n  if (!isSpanStyleItem(item)) {\n    return undefined;\n  }\n\n  const className = resolveClassNames(item.classNames);\n  const declarations = parseStyleDeclarations(item.declarations);\n\n  if (className === undefined && declarations.length === 0) {\n    return undefined;\n  }\n\n  const style: Record<string, string> = {};\n  declarations.forEach(({ property, value }) => {\n    style[toReactProperty(property)] = value;\n  });\n\n  return {\n    ...(className !== undefined ? { className } : {}),\n    ...(declarations.length > 0 ? { style: style as CSSProperties } : {}),\n  };\n}\n","import { Fragment, createElement, type ReactNode } from 'react';\nimport { getLinkItemAt } from '../links/link-item';\nimport { resolveLinkAttributes } from '../links/resolve-link-attributes';\nimport type { AnchorAttributes } from '../links/resolve-link-attributes';\nimport { getSpanStyleItemAt } from '../span-styles/span-style-item';\nimport { resolveSpanStyleAttributes } from '../span-styles/resolve-span-style-attributes';\nimport type { SpanStyleAttributes } from '../span-styles/resolve-span-style-attributes';\nimport { parseInlineMarkers } from './parse-inline-markers';\nimport type { InlineMarks, MarkupRun, MarkupSpanRun } from './parse-inline-markers';\n\n/**\n * Which element each mark renders as, outermost first.\n *\n * `s` rather than `del`: struck CMS copy is text that no longer applies (an old\n * price), not an edit to the document.\n */\nconst MARK_TAGS: ReadonlyArray<readonly [keyof InlineMarks, 'strong' | 'em' | 's']> = [\n  ['bold', 'strong'],\n  ['italic', 'em'],\n  ['strike', 's'],\n];\n\nconst hasMark = (marks: InlineMarks): boolean => marks.bold || marks.italic || marks.strike;\n\n/**\n * The anchor stays the innermost element, so marks wrap it rather than the\n * other way round — `<strong><a>…</a></strong>`. Keeps the one rule from\n * `INNER_LINK_TAGS_ARCHITECTURE.md` true no matter what else a span carries.\n *\n * A styling entry becomes the outermost element, outside the marks: the marks\n * say what these words *are*, and the styling says how this run of words looks,\n * which is the container of the other two. It also means an author who sets a\n * colour on bold words does not have to know which element the colour lands on.\n */\nconst renderSpan = (\n  run: MarkupSpanRun,\n  anchor: AnchorAttributes | undefined,\n  spanStyle: SpanStyleAttributes | undefined\n): ReactNode => {\n  let node: ReactNode = anchor === undefined ? run.text : createElement('a', anchor, run.text);\n\n  for (let position = MARK_TAGS.length - 1; position >= 0; position -= 1) {\n    const [mark, tag] = MARK_TAGS[position];\n    if (run.marks[mark]) {\n      node = createElement(tag, null, node);\n    }\n  }\n\n  return spanStyle === undefined ? node : createElement('span', spanStyle, node);\n};\n\n/** What a run contributes when nothing about it needs an element. */\nconst plainTextOf = (run: MarkupRun): string => (run.kind === 'break' ? '' : run.text);\n\n/**\n * Renders authored text with its `{…|…}` markers resolved — marks as elements,\n * indexes against a link list, `.n` against a styling list, `{br|}` as a line\n * break.\n *\n * Every element is phrasing content and sits inside whatever tag the caller\n * wraps this in; nothing here ever becomes the caller's root element, so the\n * wrapper decisions in `MINIMAL_MARKUP_ARCHITECTURE.md` are unaffected.\n *\n * When no run needs an element the return value is a plain string: the same\n * React output as text with no markup support at all. A span whose link or\n * styling entry is missing, cleared, or rejected contributes its text, so a\n * broken reference degrades to prose rather than leaving a `{1|…}` shell,\n * emitting an `<a>` without an `href`, or wrapping words in a `<span>` that\n * carries nothing.\n */\nexport function renderInlineMarkup(\n  text: string,\n  links: unknown,\n  spanStyles?: unknown\n): ReactNode {\n  const runs = parseInlineMarkers(text);\n\n  const anchors = runs.map((run) =>\n    run.kind === 'span' && run.index !== undefined\n      ? resolveLinkAttributes(getLinkItemAt(links, run.index))\n      : undefined\n  );\n\n  const spanStyleAttributes = runs.map((run) =>\n    run.kind === 'span' && run.styleIndex !== undefined\n      ? resolveSpanStyleAttributes(getSpanStyleItemAt(spanStyles, run.styleIndex))\n      : undefined\n  );\n\n  const needsElement = runs.some(\n    (run, position) =>\n      run.kind === 'break' ||\n      (run.kind === 'span' &&\n        (anchors[position] !== undefined ||\n          spanStyleAttributes[position] !== undefined ||\n          hasMark(run.marks)))\n  );\n\n  if (!needsElement) {\n    return runs.map(plainTextOf).join('');\n  }\n\n  return runs.map((run, position) => {\n    if (run.kind === 'text') {\n      return run.text;\n    }\n    if (run.kind === 'break') {\n      return <br key={position} />;\n    }\n    return (\n      <Fragment key={position}>\n        {renderSpan(run, anchors[position], spanStyleAttributes[position])}\n      </Fragment>\n    );\n  });\n}\n","import { DefaultAttributeValue } from '@source-components-react/types';\n\nexport const DEFAULT_CONTENT_TEXT =\n  'This is a text block. Click to edit and add your own content.';\n\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    name: 'content-text-3-resolutions',\n    defaultValues: {\n      all: DEFAULT_CONTENT_TEXT,\n    },\n  },\n  {\n    name: 'enclosing-tag-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'links-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'inline-styles-3-resolutions',\n    defaultValues: {},\n  },\n];\n","/**\n * HTML tags the RText component is allowed to enclose its text content in,\n * ordered from most common to least common.\n *\n * Every entry is a standard HTML element that can directly contain text\n * without changing how that text behaves. Deliberately excluded:\n * - Interactive / behavior-changing tags: a, button, label, summary, option,\n *   textarea, select, details, dialog\n * - Void elements that cannot enclose text: br, wbr, hr, img, input\n * - Tags only valid inside a specific parent: li, dt, dd, td, th, caption,\n *   legend, figcaption, rt, rp, optgroup\n * - Raw-text / metadata tags: script, style, title, template\n * - Live-region / form-output tags that alter assistive-technology behavior:\n *   output\n */\nexport const ENCLOSING_TAGS = [\n  // Common block-level text containers\n  'p',\n  'div',\n  // Generic inline container\n  'span',\n  // Headings\n  'h1',\n  'h2',\n  'h3',\n  'h4',\n  'h5',\n  'h6',\n  // Common inline emphasis and importance\n  'strong',\n  'em',\n  'b',\n  'i',\n  'u',\n  // Common text presentation\n  'small',\n  'mark',\n  // Preformatted and code text\n  'pre',\n  'code',\n  // Quotations\n  'blockquote',\n  'q',\n  // Subscript and superscript\n  'sub',\n  'sup',\n  // Edits and strikethrough\n  's',\n  'del',\n  'ins',\n  // Citations, definitions and abbreviations\n  'cite',\n  'abbr',\n  'dfn',\n  // Computer I/O text\n  'kbd',\n  'samp',\n  'var',\n  // Machine-readable text\n  'time',\n  'data',\n  // Contact information\n  'address',\n  // Bidirectional text\n  'bdi',\n  'bdo',\n  // Sectioning and grouping containers\n  'figure',\n  'article',\n  'section',\n  'aside',\n  'header',\n  'footer',\n  'main',\n  'nav',\n] as const;\n\nexport type EnclosingTag = (typeof ENCLOSING_TAGS)[number];\n\n/**\n * The tags above whose default CSS display is block-level.\n *\n * Box styles — margin, padding, width, border — behave as authored on these\n * elements, so RText can carry the column's styles on the tag itself and\n * needs no wrapper div (see `resolve-no-container.ts`).\n */\nexport const BLOCK_ENCLOSING_TAGS: readonly EnclosingTag[] = [\n  'p',\n  'div',\n  'h1',\n  'h2',\n  'h3',\n  'h4',\n  'h5',\n  'h6',\n  'pre',\n  'blockquote',\n  'address',\n  'figure',\n  'article',\n  'section',\n  'aside',\n  'header',\n  'footer',\n  'main',\n  'nav',\n];\n\n/**\n * The tags above that default to `display: inline`, where vertical\n * margin/padding and width/height are ignored by the layout engine. Column\n * box styles must go on a wrapper element instead of on the tag.\n *\n * Kept as an explicit list rather than derived from BLOCK_ENCLOSING_TAGS so\n * adding a tag to ENCLOSING_TAGS forces a deliberate block-or-inline call;\n * the coverage test in `__tests__/enclosing-tags.test.ts` fails otherwise.\n */\nexport const INLINE_ENCLOSING_TAGS: readonly EnclosingTag[] = [\n  'span',\n  'strong',\n  'em',\n  'b',\n  'i',\n  'u',\n  'small',\n  'mark',\n  'code',\n  'q',\n  'sub',\n  'sup',\n  's',\n  'del',\n  'ins',\n  'cite',\n  'abbr',\n  'dfn',\n  'kbd',\n  'samp',\n  'var',\n  'time',\n  'data',\n  'bdi',\n  'bdo',\n];\n\n/**\n * Runtime guard for values arriving through componentAttributes (untyped\n * editor-authored data). Unknown values are rejected so a bad persisted value\n * can never render an unexpected element.\n */\nexport function isEnclosingTag(value: unknown): value is EnclosingTag {\n  return (\n    typeof value === 'string' &&\n    (ENCLOSING_TAGS as readonly string[]).includes(value)\n  );\n}\n\n/**\n * True when the tag renders as a block-level box. Unknown values count as\n * non-block: the safe answer, because it routes box styles to a wrapper\n * element where they are guaranteed to apply.\n */\nexport function isBlockEnclosingTag(value: unknown): boolean {\n  return (\n    isEnclosingTag(value) &&\n    (BLOCK_ENCLOSING_TAGS as readonly string[]).includes(value)\n  );\n}\n","import { isEnclosingTag } from './enclosing-tags';\nimport type { EnclosingTag } from './enclosing-tags';\n\n/** Editor-authored attribute holding the tag, flattened per device resolution. */\nexport const ENCLOSING_TAG_ATTRIBUTE = 'enclosing-tag-3-resolutions';\n\n/**\n * The single source of truth for which tag RText will render.\n *\n * Both the component and `resolveNoContainer` go through here: if they\n * disagreed, widget-react would wrap a block element in a redundant div or\n * hand box styles to an inline element that ignores them.\n *\n * The editor-authored attribute wins over the caller's prop. Attribute values\n * are untyped, so an unrecognised value falls back to the prop, then to no\n * enclosing tag at all.\n */\nexport function resolveEnclosingTag(\n  componentAttributes?: { [key: string]: any },\n  fallback?: EnclosingTag\n): EnclosingTag | undefined {\n  const userAssignedTag = componentAttributes?.[ENCLOSING_TAG_ATTRIBUTE];\n  return isEnclosingTag(userAssignedTag) ? userAssignedTag : fallback;\n}\n","import React from 'react';\nimport { renderInlineMarkup } from '@source-components-react/shared/inline-markup';\nimport { DEFAULT_CONTENT_TEXT } from './default-attribute-values';\nimport { resolveEnclosingTag } from './resolve-enclosing-tag';\nimport type { EnclosingTag } from './enclosing-tags';\n\nexport function RText({\n  enclosingTag,\n  componentAttributes,\n  styleProps,\n  className,\n}: Readonly<{\n  /**\n   * HTML tag to enclose the text in (e.g. 'p', 'h1', 'span'). Restricted to\n   * tags that hold text without changing its behavior. When omitted the text\n   * renders bare, exactly as before this attribute existed.\n   */\n  enclosingTag?: EnclosingTag;\n  componentAttributes?: { [key: string]: any };\n  /**\n   * Column styles from the widget pipeline. Applied to the enclosing tag when\n   * there is one; bare text has no element to carry them, in which case\n   * `resolveNoContainer` has already told widget-react to use a wrapper div.\n   */\n  styleProps?: { [key: string]: any };\n  /**\n   * Host-app classes for this column, when the renderer decided this card\n   * carries them. Applied to the enclosing tag; ignored for bare text (the\n   * renderer has already taken responsibility for the class).\n   */\n  className?: string;\n}>) {\n  const contentVal =\n    componentAttributes?.['content-text-3-resolutions'] ?? DEFAULT_CONTENT_TEXT;\n\n  // Marks (`{b|word}`), line breaks (`{br|}`), and the two index markers —\n  // `{n|…}` into the link list and `{.n|…}` into the styling list — both by\n  // 1-based position. Everything the markup emits is phrasing content inside\n  // the enclosing tag, never around it.\n  const content =\n    typeof contentVal === 'string'\n      ? renderInlineMarkup(\n          contentVal,\n          componentAttributes?.['links-3-resolutions'],\n          componentAttributes?.['inline-styles-3-resolutions']\n        )\n      : contentVal;\n\n  const tag = resolveEnclosingTag(componentAttributes, enclosingTag);\n\n  if (tag) {\n    return React.createElement(\n      tag,\n      className !== undefined ? { style: styleProps, className } : { style: styleProps },\n      content\n    );\n  }\n  return content;\n}\n","import type { NoContainerResolverContext } from '@source-components-react/types';\nimport { isBlockEnclosingTag } from './enclosing-tags';\nimport { resolveEnclosingTag } from './resolve-enclosing-tag';\n\n/**\n * RText needs a wrapper div for the column's box styles unless it is about to\n * render a block-level element that can hold those styles itself.\n *\n * - No enclosing tag: bare text with no element to style → wrapper needed.\n * - Inline tag (`span`, `em`, `code`, …): vertical margin/padding and width\n *   are ignored on inline boxes → wrapper needed.\n * - Block tag (`p`, `div`, `h1`, `section`, …): RText puts styleProps on the\n *   tag itself → no wrapper, one less element in the DOM.\n */\nexport const resolveNoContainer = ({\n  componentAttributes,\n}: NoContainerResolverContext): boolean =>\n  !isBlockEnclosingTag(resolveEnclosingTag(componentAttributes));\n","type ComponentIdentifier =\n  | 'r-text'\n  | 'something-else'\n  | 'cloudinary-advanced-image'\n  | 'r-video'\n  | 'r-icon'\n  | 'r-button'\n  | 'r-divider'\n  | 'r-embed'\n  | 'r-list'\n  | 'r-accordion'\n  | 'r-mount';\n\nexport const ComponentIdentifiers: Record<string, ComponentIdentifier> = {\n  rText: 'r-text',\n  somethingElse: 'something-else',\n  cloudinaryAdvancedImage: 'cloudinary-advanced-image',\n  rVideo: 'r-video',\n  rIcon: 'r-icon',\n  rButton: 'r-button',\n  rDivider: 'r-divider',\n  rEmbed: 'r-embed',\n  rList: 'r-list',\n  rAccordion: 'r-accordion',\n  rMount: 'r-mount',\n} as const;\n","import { RText } from './RText';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type { DisplayCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.rText,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({\n    componentAttributes,\n    styleProps,\n    className,\n  }: {\n    componentAttributes?: { [key: string]: any };\n    styleProps?: { [key: string]: any };\n    className?: string;\n  }) => (\n    <RText\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n    />\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","import type { NoContainerResolverContext } from '@source-components-react/types';\nimport { resolveEnclosingTag } from './resolve-enclosing-tag';\n\n/**\n * True when RText is about to render an element that can carry a className —\n * any enclosing tag, block or inline. Bare text has no element, so false.\n *\n * Shares `resolveEnclosingTag` with RText and `resolveNoContainer`: if this\n * disagreed with what the component renders, a class would either land on\n * nothing or force a redundant wrapper.\n */\nexport const resolveRendersOwnElement = ({\n  componentAttributes,\n}: NoContainerResolverContext): boolean => !!resolveEnclosingTag(componentAttributes);\n","import { AttributeEditModeConfig } from '@source-components-react/types';\n\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: 'content-text-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'enclosing-tag-3-resolutions',\n    heading: 'Enclosing Tag',\n    subHeading:\n      'HTML tag to enclose the text in (e.g. p, h1, span); see ENCLOSING_TAGS for the allowed values, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'links-3-resolutions',\n    heading: 'Links',\n    subHeading:\n      'Links this text can point at. To link part of the text, wrap those words in {1|linked text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'inline-styles-3-resolutions',\n    heading: 'Class Names & Styles',\n    subHeading:\n      'Class names and CSS this text can apply to part of itself, e.g. to give a few words their own colour. To apply an entry, wrap those words in {.1|styled text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',\n    defaultValues: {},\n  },\n];\n","// Builds Cloudinary delivery URLs directly so the component renders a complete\n// <img src/srcSet> during server rendering (no browser-only Cloudinary SDK).\n// `f_auto,q_auto` lets Cloudinary pick format/quality; `c_limit` scales down\n// but never upscales beyond the original asset.\n\n// A Cloudinary transformation component as an object, e.g.\n// { c: 'fill', g: 'auto', ar: '4:3' } serializes to \"c_fill,g_auto,ar_4:3\"\nexport type CloudinaryTransformations = { [key: string]: string | number };\n\n// Covers phone through large desktop displays, including 2x DPR on common\n// viewport widths. Upper bound keeps multi-thousand-pixel originals in check.\nexport const cloudinarySrcSetWidths = [480, 768, 1080, 1600, 2400];\n\n// Mid-size fallback for browsers/tools that ignore srcset.\nconst fallbackSrcWidth = 1080;\n\nconst defaultTransformations: CloudinaryTransformations = {\n  f: 'auto',\n  q: 'auto',\n  c: 'limit',\n};\n\nfunction serializeTransformations(transformations: CloudinaryTransformations): string {\n  return Object.entries(transformations)\n    .map(([key, value]) => `${key}_${value}`)\n    .join(',');\n}\n\n// Accepts the raw Cloudinary string form (\"c_fill,g_auto,ar_4:3\") and parses\n// it into an object so it merges over the defaults the same way object input\n// does (e.g. a user c_fill cleanly replaces the default c_limit)\nfunction parseTransformationString(value: string): CloudinaryTransformations {\n  const result: CloudinaryTransformations = {};\n  value.split(',').forEach((entry) => {\n    const trimmed = entry.trim();\n    if (!trimmed) {\n      return;\n    }\n    const separatorIndex = trimmed.indexOf('_');\n    if (separatorIndex <= 0) {\n      return;\n    }\n    result[trimmed.slice(0, separatorIndex)] = trimmed.slice(separatorIndex + 1);\n  });\n  return result;\n}\n\nfunction buildCloudinaryImageUrl(\n  cloudName: string,\n  publicId: string,\n  width: number,\n  transformations?: CloudinaryTransformations | string\n): string {\n  const userTransformations =\n    typeof transformations === 'string'\n      ? parseTransformationString(transformations)\n      : transformations;\n\n  // User keys override defaults (object spread = \"split the object onto the\n  // configuration\"). `w` is always component-managed so srcset width variants\n  // stay correct; combined with user ar/c/g values Cloudinary produces\n  // consistent crops at every width.\n  const merged: CloudinaryTransformations = {\n    ...defaultTransformations,\n    ...userTransformations,\n    w: width,\n  };\n\n  // encodeURI preserves the slashes that are part of Cloudinary public IDs\n  // (e.g. \"images/<uuid>/<id>\") while escaping unsafe characters\n  return encodeURI(\n    `https://res.cloudinary.com/${cloudName}/image/upload/${serializeTransformations(merged)}/${publicId}`\n  );\n}\n\nexport function buildCloudinarySrc(\n  cloudName: string,\n  publicId: string,\n  transformations?: CloudinaryTransformations | string\n): string {\n  return buildCloudinaryImageUrl(cloudName, publicId, fallbackSrcWidth, transformations);\n}\n\nexport function buildCloudinarySrcSet(\n  cloudName: string,\n  publicId: string,\n  transformations?: CloudinaryTransformations | string\n): string {\n  return cloudinarySrcSetWidths\n    .map((width) => `${buildCloudinaryImageUrl(cloudName, publicId, width, transformations)} ${width}w`)\n    .join(', ');\n}\n","import { buildCloudinarySrc, buildCloudinarySrcSet } from './build-cloudinary-image-urls';\nimport type { CloudinaryTransformations } from './build-cloudinary-image-urls';\n\nexport function CloudinaryAdvancedImage({\n  cloudName = 'rulecms',\n  publicId = 'main-sample',\n  alt = 'RuleCMS Component',\n  transformations,\n  componentAttributes,\n  styleProps = {},\n  className,\n}: Readonly<{\n  cloudName?: string;\n  publicId?: string;\n  alt?: string;\n  // Extra Cloudinary transformations merged over the defaults (f_auto,q_auto,c_limit).\n  // Object form { c: 'fill', g: 'auto' } or raw string form 'c_fill,g_auto'.\n  transformations?: CloudinaryTransformations | string;\n  componentAttributes?: { [key: string]: any };\n  styleProps?: { [key: string]: any };\n  /**\n   * Host-app classes for this column, when the renderer decided this card\n   * carries them. Concatenated after any pass-through-image-attributes-className.\n   */\n  className?: string;\n}>) {\n  // Get values from component attributes if provided\n  // Priority: media-selector-3-resolutions > cloudinary-public-id > default publicId prop\n  const userCloudName = componentAttributes?.['cloudinary-cloud-name'];\n  const mediaSelectorPublicId = componentAttributes?.['media-selector-3-resolutions'];\n  const userPublicId = componentAttributes?.['cloudinary-public-id'];\n\n  const finalCloudName = userCloudName || cloudName;\n  const finalPublicId = mediaSelectorPublicId || userPublicId || publicId;\n\n  // Editor-authored transformations (per-device, flattened by the widget\n  // pipeline) take precedence over the prop from the calling component\n  const componentTransformations = componentAttributes?.['cloudinary-transformations-3-resolutions'];\n  const finalTransformations = componentTransformations || transformations;\n\n  // Extract pass-through props for the img element\n  // Convention:\n  // - 'pass-through-image-attributes-style-' prefix for style properties\n  // - 'pass-through-image-attributes-' prefix for other HTML attributes\n  // alt priority: pass-through-image-attributes-alt (editor-authored, applied\n  // in the loop below) > alt prop (calling component) > 'RuleCMS Component' default\n  const imageAttributes: { [key: string]: any } = {\n    alt,\n    // Safe default; pass-through attributes can override. loading=\"lazy\" stays\n    // opt-in via pass-through because lazy-loading an LCP image hurts paint.\n    decoding: 'async',\n    // Generic default so srcset has a baseline; editors/consumers should\n    // override via 'pass-through-image-attributes-sizes' when the rendered\n    // width is known.\n    sizes: '100vw',\n  };\n\n  const imageStyles: { [key: string]: any } = {\n    height: 'auto',\n  };\n\n  if (componentAttributes) {\n    Object.keys(componentAttributes).forEach((keyWithDimensionSuffix) => {\n      const value = componentAttributes[keyWithDimensionSuffix];\n\n      // Unset attributes arrive as undefined entries (the device-type\n      // flattening keeps the key); skip them so defaults are not clobbered.\n      // Empty string is kept — alt=\"\" is meaningful for decorative images.\n      if (value === undefined || value === null) {\n        return;\n      }\n\n      // Remove the -3-resolutions suffix if it exists\n      const key = keyWithDimensionSuffix.endsWith('-3-resolutions')\n        ? keyWithDimensionSuffix.slice(0, -'-3-resolutions'.length)\n        : keyWithDimensionSuffix;\n\n      // Handle style attributes\n      if (key.startsWith('pass-through-image-attributes-style-')) {\n        const styleProp = key.slice('pass-through-image-attributes-style-'.length)\n        imageStyles[styleProp] = value;\n      }\n\n      // Handle other HTML attributes (className, alt, id, loading, sizes, width, height, etc.)\n      else if (key.startsWith('pass-through-image-attributes-')) {\n        const attributeName = key.slice('pass-through-image-attributes-'.length);\n        imageAttributes[attributeName] = value;\n      }\n    });\n  }\n\n  // Column className comes last so host utilities can override pass-through\n  // classes from the editor. Omit the attribute entirely when neither is set.\n  const { className: passThroughClassName, ...restImageAttributes } = imageAttributes;\n  const mergedClassName = [passThroughClassName, className].filter(Boolean).join(' ') || undefined;\n\n  // Plain <img> with Cloudinary delivery URLs built directly: renders complete\n  // markup (src + responsive srcset) on the server, so widget images appear in\n  // server-generated HTML and each device downloads one right-sized asset\n  return (\n    <img\n      src={buildCloudinarySrc(finalCloudName, finalPublicId, finalTransformations)}\n      srcSet={buildCloudinarySrcSet(finalCloudName, finalPublicId, finalTransformations)}\n      {...restImageAttributes}\n      {...(mergedClassName !== undefined ? { className: mergedClassName } : {})}\n      style={{ ...imageStyles, ...styleProps }}\n    />\n  );\n}\n","/**\n * The component spreads styleProps onto its `<img>`, a replaced element that\n * honors min/max sizing, margin and padding directly — a wrapper div would\n * only add a redundant element. There is no configuration that changes this,\n * so the answer is the same for every render.\n */\nexport const resolveNoContainer = (): boolean => false;\n","import { CloudinaryAdvancedImage } from './CloudinaryAdvancedImage';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type { DisplayCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.cloudinaryAdvancedImage,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({\n    componentAttributes,\n    styleProps,\n    className,\n  }: {\n    componentAttributes?: { [key: string]: any };\n    styleProps?: { [key: string]: any };\n    className?: string;\n  }) => (\n    <CloudinaryAdvancedImage\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n    />\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","/**\n * Always renders an `<img>`, which can carry a className. There is no\n * configuration that changes this.\n */\nexport const resolveRendersOwnElement = (): boolean => true;\n","import { DefaultAttributeValue } from '@source-components-react/types';\n\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    name: 'cloudinary-cloud-name',\n    defaultValues: {},\n  },\n  {\n    name: 'cloudinary-public-id',\n    defaultValues: {},\n  },\n  {\n    name: 'media-selector-3-resolutions',\n    defaultValues: {\n      all: 'main-sample',\n    },\n  },\n  {\n    name: 'pass-through-image-attributes-alt-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-width-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-height-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-sizes-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-loading-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-fetchpriority-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'cloudinary-transformations-3-resolutions',\n    defaultValues: {},\n  },\n];\n","import { AttributeEditModeConfig } from '@source-components-react/types';\n\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: 'cloudinary-cloud-name',\n    heading: 'Cloudinary Cloud Name',\n    subHeading: 'Set the Cloudinary cloud name for 3 different device resolutions',\n    defaultValues: {},\n    // Filled by Media Selector / team defaults — not something marketers edit by hand.\n    hiddenToUser: true,\n  },\n  {\n    name: 'cloudinary-public-id',\n    heading: 'Image Public ID',\n    subHeading: 'Set the Cloudinary image public ID for 3 different device resolutions',\n    defaultValues: {},\n    // Filled by Media Selector — not something marketers edit by hand.\n    hiddenToUser: true,\n  },\n  {\n    name: 'media-selector-3-resolutions',\n    heading: 'Media Selector',\n    subHeading: 'Select media for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-alt-3-resolutions',\n    heading: 'Image Alt Text',\n    subHeading: 'Set the image alt text for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-width-3-resolutions',\n    heading: 'Image Intrinsic Width',\n    subHeading: 'Set the intrinsic pixel width (with height, reserves layout space before the image loads) for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-height-3-resolutions',\n    heading: 'Image Intrinsic Height',\n    subHeading: 'Set the intrinsic pixel height (with width, reserves layout space before the image loads) for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-sizes-3-resolutions',\n    heading: 'Image Sizes',\n    subHeading: 'Set the sizes attribute (e.g. \"(max-width: 600px) 100vw, 600px\") so the browser downloads the right srcset variant, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-loading-3-resolutions',\n    heading: 'Image Loading',\n    subHeading: 'Set to \"lazy\" for below-the-fold images (never for the main hero image), for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-image-attributes-fetchpriority-3-resolutions',\n    heading: 'Image Fetch Priority',\n    subHeading: 'Set to \"high\" for the most important image on the page (e.g. the hero), for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'cloudinary-transformations-3-resolutions',\n    heading: 'Cloudinary Transformations',\n    subHeading: 'Set extra Cloudinary transformations (e.g. \"c_fill,g_auto,ar_4:3\"); merged over the defaults f_auto,q_auto,c_limit, for 3 different device resolutions',\n    defaultValues: {},\n  },\n];\n","// Builds media-host delivery URLs directly so the component renders a complete\n// <video src/poster> during server rendering (no browser-only SDK).\n//\n// Delivery choices for web video:\n// - `.mp4` extension: every uploaded source format (webm, mov, …) is\n//   transcoded to progressive MP4, which plays in every browser.\n// - `vc_auto,q_auto`: the host picks the best codec and quality for the\n//   requested container.\n// - `c_limit,w_1920`: scales oversized originals down to a web-appropriate\n//   width but never upscales.\n\n// A transformation component as an object, e.g.\n// { c: 'fill', ar: '16:9' } serializes to \"c_fill,ar_16:9\"\nexport type VideoTransformations = { [key: string]: string | number };\n\nconst defaultVideoTransformations: VideoTransformations = {\n  q: 'auto',\n  vc: 'auto',\n  c: 'limit',\n  w: 1920,\n};\n\n// Poster is a single still frame: take the first frame (so_0), same\n// downscale cap as the video itself. Format comes from the .jpg extension.\nconst defaultPosterTransformations: VideoTransformations = {\n  so: 0,\n  q: 'auto',\n  c: 'limit',\n  w: 1920,\n};\n\n// Custom posters are uploaded images, so they get the image pipeline's\n// automatic format/quality.\nconst defaultCustomPosterTransformations: VideoTransformations = {\n  f: 'auto',\n  q: 'auto',\n  c: 'limit',\n  w: 1920,\n};\n\nfunction serializeTransformations(transformations: VideoTransformations): string {\n  return Object.entries(transformations)\n    .map(([key, value]) => (value === '' ? key : `${key}_${value}`))\n    .join(',');\n}\n\n// Accepts the raw string form (\"c_fill,ar_16:9\") and parses it into an object\n// so it merges over the defaults the same way object input does.\nfunction parseTransformationString(value: string): VideoTransformations {\n  const result: VideoTransformations = {};\n  value.split(',').forEach((entry) => {\n    const trimmed = entry.trim();\n    if (!trimmed) {\n      return;\n    }\n    const separatorIndex = trimmed.indexOf('_');\n    if (separatorIndex <= 0) {\n      // Flag-style component with no value (e.g. \"so_0\" handled above; keep\n      // unknown flags as bare keys so they serialize unchanged).\n      result[trimmed] = '';\n      return;\n    }\n    result[trimmed.slice(0, separatorIndex)] = trimmed.slice(separatorIndex + 1);\n  });\n  return result;\n}\n\nfunction mergeTransformations(\n  defaults: VideoTransformations,\n  transformations?: VideoTransformations | string\n): VideoTransformations {\n  const userTransformations =\n    typeof transformations === 'string'\n      ? parseTransformationString(transformations)\n      : transformations;\n  return { ...defaults, ...userTransformations };\n}\n\nexport function buildVideoSrc(\n  cloudName: string,\n  publicId: string,\n  transformations?: VideoTransformations | string\n): string {\n  const merged = mergeTransformations(defaultVideoTransformations, transformations);\n  // encodeURI preserves the slashes that are part of public IDs\n  // (e.g. \"videos/<uuid>/<id>\") while escaping unsafe characters\n  return encodeURI(\n    `https://res.cloudinary.com/${cloudName}/video/upload/${serializeTransformations(merged)}/${publicId}.mp4`\n  );\n}\n\n// First frame of the video itself — the zero-configuration poster.\nexport function buildVideoPosterSrc(cloudName: string, publicId: string): string {\n  return encodeURI(\n    `https://res.cloudinary.com/${cloudName}/video/upload/${serializeTransformations(defaultPosterTransformations)}/${publicId}.jpg`\n  );\n}\n\n// A separately uploaded image chosen as the poster.\nexport function buildCustomPosterSrc(cloudName: string, posterPublicId: string): string {\n  return encodeURI(\n    `https://res.cloudinary.com/${cloudName}/image/upload/${serializeTransformations(defaultCustomPosterTransformations)}/${posterPublicId}`\n  );\n}\n","import {\n  buildVideoSrc,\n  buildVideoPosterSrc,\n  buildCustomPosterSrc,\n} from './build-video-delivery-urls';\nimport type { VideoTransformations } from './build-video-delivery-urls';\n\n// Boolean <video> attributes arrive from the editor as the strings\n// 'true' / 'false' (select editors), or as real booleans from calling\n// components. They must become real booleans on the element.\nconst BOOLEAN_VIDEO_ATTRIBUTES = new Set(['controls', 'autoplay', 'loop', 'muted', 'playsinline']);\n\n// React prop names for attributes whose DOM names are all-lowercase.\nconst REACT_PROP_NAMES: { [key: string]: string } = {\n  autoplay: 'autoPlay',\n  playsinline: 'playsInline',\n};\n\nconst coerceBoolean = (value: any): boolean => value === true || value === 'true';\n\nexport function RVideo({\n  cloudName = 'rulecms',\n  publicId = 'main-sample-video',\n  transformations,\n  componentAttributes,\n  styleProps = {},\n  className,\n}: Readonly<{\n  cloudName?: string;\n  publicId?: string;\n  // Extra delivery transformations merged over the defaults (q_auto,vc_auto,c_limit,w_1920).\n  // Object form { c: 'fill', ar: '16:9' } or raw string form 'c_fill,ar_16:9'.\n  transformations?: VideoTransformations | string;\n  componentAttributes?: { [key: string]: any };\n  styleProps?: { [key: string]: any };\n  /**\n   * Host-app classes for this column, when the renderer decided this card\n   * carries them. Concatenated after any pass-through-video-attributes-className.\n   */\n  className?: string;\n}>) {\n  // Priority: video-selector-3-resolutions > video-public-id > default publicId prop\n  const userCloudName = componentAttributes?.['video-host-cloud-name'];\n  const selectorPublicId = componentAttributes?.['video-selector-3-resolutions'];\n  const userPublicId = componentAttributes?.['video-public-id'];\n\n  const finalCloudName = userCloudName || cloudName;\n  const finalPublicId = selectorPublicId || userPublicId || publicId;\n\n  // Editor-authored transformations (per-device, flattened by the widget\n  // pipeline) take precedence over the prop from the calling component\n  const componentTransformations = componentAttributes?.['video-transformations-3-resolutions'];\n  const finalTransformations = componentTransformations || transformations;\n\n  // Optional custom poster (an uploaded image's public ID); falls back to the\n  // video's own first frame so there is always a poster.\n  const posterPublicId = componentAttributes?.['video-poster-selector-3-resolutions'];\n\n  // Extract pass-through props for the video element\n  // Convention:\n  // - 'pass-through-video-attributes-style-' prefix for style properties\n  // - 'pass-through-video-attributes-' prefix for other HTML attributes\n  const videoAttributes: { [key: string]: any } = {\n    // Classic content-video defaults: visible controls, inline playback on\n    // mobile (no forced fullscreen), and metadata-only preload so dropping a\n    // video on a page does not download the whole file.\n    controls: true,\n    playsInline: true,\n    preload: 'metadata',\n    poster: posterPublicId\n      ? buildCustomPosterSrc(finalCloudName, posterPublicId)\n      : buildVideoPosterSrc(finalCloudName, finalPublicId),\n  };\n\n  const videoStyles: { [key: string]: any } = {\n    // A replaced element with a large intrinsic size should shrink to its\n    // container, not overflow it; height follows to keep the aspect ratio.\n    maxWidth: '100%',\n    height: 'auto',\n  };\n\n  if (componentAttributes) {\n    Object.keys(componentAttributes).forEach((keyWithDimensionSuffix) => {\n      const value = componentAttributes[keyWithDimensionSuffix];\n\n      // Unset attributes arrive as undefined entries (the device-type\n      // flattening keeps the key); skip them so defaults are not clobbered.\n      if (value === undefined || value === null) {\n        return;\n      }\n\n      // Remove the -3-resolutions suffix if it exists\n      const key = keyWithDimensionSuffix.endsWith('-3-resolutions')\n        ? keyWithDimensionSuffix.slice(0, -'-3-resolutions'.length)\n        : keyWithDimensionSuffix;\n\n      // Handle style attributes\n      if (key.startsWith('pass-through-video-attributes-style-')) {\n        const styleProp = key.slice('pass-through-video-attributes-style-'.length);\n        videoStyles[styleProp] = value;\n      }\n\n      // Handle other HTML attributes (controls, autoplay, poster, width, etc.)\n      else if (key.startsWith('pass-through-video-attributes-')) {\n        const attributeName = key.slice('pass-through-video-attributes-'.length);\n\n        if (BOOLEAN_VIDEO_ATTRIBUTES.has(attributeName)) {\n          const reactName = REACT_PROP_NAMES[attributeName] || attributeName;\n          if (coerceBoolean(value)) {\n            videoAttributes[reactName] = true;\n          } else {\n            // 'false' must remove the attribute (booleans are presence-based),\n            // including the built-in controls/playsInline defaults.\n            delete videoAttributes[reactName];\n          }\n          return;\n        }\n\n        videoAttributes[REACT_PROP_NAMES[attributeName] || attributeName] = value;\n      }\n    });\n  }\n\n  // Column className comes last so host utilities can override pass-through\n  // classes from the editor. Omit the attribute entirely when neither is set.\n  const { className: passThroughClassName, ...restVideoAttributes } = videoAttributes;\n  const mergedClassName = [passThroughClassName, className].filter(Boolean).join(' ') || undefined;\n\n  // Plain <video> with delivery URLs built directly: renders complete markup\n  // (src + poster) on the server. Progressive MP4 keeps the element simple\n  // and universally playable.\n  return (\n    <video\n      src={buildVideoSrc(finalCloudName, finalPublicId, finalTransformations)}\n      {...restVideoAttributes}\n      {...(mergedClassName !== undefined ? { className: mergedClassName } : {})}\n      style={{ ...videoStyles, ...styleProps }}\n    />\n  );\n}\n","/**\n * The component spreads styleProps onto its `<video>`, a replaced element that\n * honors min/max sizing, margin and padding directly — a wrapper div would\n * only add a redundant element. There is no configuration that changes this,\n * so the answer is the same for every render.\n */\nexport const resolveNoContainer = (): boolean => false;\n","import { RVideo } from './RVideo';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type { DisplayCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.rVideo,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({\n    componentAttributes,\n    styleProps,\n    className,\n  }: {\n    componentAttributes?: { [key: string]: any };\n    styleProps?: { [key: string]: any };\n    className?: string;\n  }) => (\n    <RVideo\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n    />\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","/**\n * Always renders a `<video>`, which can carry a className. There is no\n * configuration that changes this.\n */\nexport const resolveRendersOwnElement = (): boolean => true;\n","import { DefaultAttributeValue } from '@source-components-react/types';\n\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    name: 'video-host-cloud-name',\n    defaultValues: {},\n  },\n  {\n    name: 'video-public-id',\n    defaultValues: {},\n  },\n  {\n    name: 'video-selector-3-resolutions',\n    defaultValues: {\n      all: 'main-sample-video',\n    },\n  },\n  {\n    name: 'video-poster-selector-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-controls-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-autoplay-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-loop-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-muted-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-playsinline-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-preload-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-width-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-height-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'video-transformations-3-resolutions',\n    defaultValues: {},\n  },\n];\n","import { AttributeEditModeConfig } from '@source-components-react/types';\n\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: 'video-host-cloud-name',\n    heading: 'Video Host Cloud Name',\n    subHeading: 'Set the media host cloud name for 3 different device resolutions',\n    defaultValues: {},\n    // Filled by Video Selector / team defaults — not something marketers edit by hand.\n    hiddenToUser: true,\n  },\n  {\n    name: 'video-public-id',\n    heading: 'Video Public ID',\n    subHeading: 'Set the video public ID for 3 different device resolutions',\n    defaultValues: {},\n    // Filled by Video Selector — not something marketers edit by hand.\n    hiddenToUser: true,\n  },\n  {\n    name: 'video-selector-3-resolutions',\n    heading: 'Video Selector',\n    subHeading: 'Select a video for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'video-poster-selector-3-resolutions',\n    heading: 'Poster Image',\n    subHeading: 'Select an uploaded image shown before playback; defaults to the video\\'s first frame, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-controls-3-resolutions',\n    heading: 'Video Controls',\n    subHeading: 'Show the browser playback controls (default \"true\"; set \"false\" for background/ambient video), for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-autoplay-3-resolutions',\n    heading: 'Video Autoplay',\n    subHeading: 'Start playback automatically (browsers require muted \"true\" for autoplay to work), for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-loop-3-resolutions',\n    heading: 'Video Loop',\n    subHeading: 'Restart the video automatically when it ends, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-muted-3-resolutions',\n    heading: 'Video Muted',\n    subHeading: 'Start with the audio muted (required by browsers for autoplay), for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-playsinline-3-resolutions',\n    heading: 'Video Plays Inline',\n    subHeading: 'Play within the page on mobile instead of forcing fullscreen (default \"true\"), for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-preload-3-resolutions',\n    heading: 'Video Preload',\n    subHeading: 'How much to download before playback: \"none\", \"metadata\" (default) or \"auto\", for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-width-3-resolutions',\n    heading: 'Video Intrinsic Width',\n    subHeading: 'Set the intrinsic pixel width (with height, reserves layout space before the video loads) for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'pass-through-video-attributes-height-3-resolutions',\n    heading: 'Video Intrinsic Height',\n    subHeading: 'Set the intrinsic pixel height (with width, reserves layout space before the video loads) for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'video-transformations-3-resolutions',\n    heading: 'Video Transformations',\n    subHeading: 'Set extra delivery transformations (e.g. \"c_fill,ar_16:9\"); merged over the defaults q_auto,vc_auto,c_limit,w_1920, for 3 different device resolutions',\n    defaultValues: {},\n  },\n];\n","import { createElement } from 'react';\nimport type { ReactElement } from 'react';\nimport type { IconNode } from '@source-components-react/types';\n\n/**\n * Turns persisted Lucide geometry into SVG children.\n *\n * Shared so `r-icon` and any component that embeds an icon draw from one\n * implementation — the icon architecture's rule that there is exactly one\n * icon path, whoever is rendering it. The caller owns the `<svg>` element,\n * because a standalone icon and an icon inside a button want different\n * sizing and accessibility defaults.\n */\nexport const renderIconChildren = (iconNode: IconNode): ReactElement[] =>\n  iconNode.map(([tag, attributes], index) =>\n    createElement(tag, { ...attributes, key: attributes.key ?? `${tag}-${index}` })\n  );\n","import type { IconAttributeValue, IconNode } from '@source-components-react/types';\n\n/**\n * The placeholder a freshly dropped icon shows before the author picks one:\n * Lucide's `shapes`, inlined as a literal.\n *\n * It is a literal rather than a registry lookup on purpose. Nothing on the\n * render path may import `lucide-static` — the default library is registered\n * eagerly by every consumer, so its bundle reaches every customer page.\n * `__tests__/default-attribute-values.test.ts` checks this literal still\n * matches the registry, so a drift shows up as a test failure rather than a\n * placeholder that quietly stops matching the real icon.\n */\nexport const PLACEHOLDER_ICON_NAME = 'shapes';\n\nexport const PLACEHOLDER_ICON_NODE: IconNode = [\n  [\n    'path',\n    { d: 'M8.3 10a.7.7 0 0 1-.626-1.079L11.4 3a.7.7 0 0 1 1.198-.043L16.3 8.9a.7.7 0 0 1-.572 1.1Z' },\n  ],\n  ['rect', { x: '3', y: '14', width: '7', height: '7', rx: '1' }],\n  ['circle', { cx: '17.5', cy: '17.5', r: '3.5' }],\n];\n\nexport const PLACEHOLDER_ICON: IconAttributeValue = {\n  name: PLACEHOLDER_ICON_NAME,\n  iconNode: PLACEHOLDER_ICON_NODE,\n};\n\nconst isIconNode = (value: unknown): value is IconNode =>\n  Array.isArray(value) &&\n  value.length > 0 &&\n  value.every(\n    (entry) =>\n      Array.isArray(entry) &&\n      entry.length >= 2 &&\n      typeof entry[0] === 'string' &&\n      entry[1] != null &&\n      typeof entry[1] === 'object' &&\n      !Array.isArray(entry[1])\n  );\n\n/**\n * Read geometry out of a persisted icon attribute.\n *\n * The composer writes `{ name, iconNode }` when the author picks an icon, so\n * every surface renders from the value alone and never needs the registry.\n * Anything else — a bare name string from a hand-edited config, a half-written\n * value — yields null and renders nothing, which is the specified behaviour\n * for an unresolvable icon on a published page.\n */\nexport const resolveIconAttributeValue = (value: unknown): IconNode | null => {\n  if (!value || typeof value !== 'object' || Array.isArray(value)) {\n    return null;\n  }\n\n  const { iconNode } = value as { iconNode?: unknown };\n  return isIconNode(iconNode) ? iconNode : null;\n};\n","import { createElement, type CSSProperties, type SVGProps } from 'react';\nimport { renderIconChildren } from '@source-components-react/shared/icons';\nimport { PLACEHOLDER_ICON_NODE, resolveIconAttributeValue } from './icon-attribute-value';\n\nconst DEFAULT_SIZE = 24;\n\nconst A11Y_ATTR_KEYS = [\n  'aria-label',\n  'aria-labelledby',\n  'aria-describedby',\n  'role',\n  'title',\n] as const;\n\n/**\n * One `<svg>` — nothing else.\n *\n * Geometry comes from the attribute the composer wrote, so resolution is\n * synchronous and SSR emits the icon in the initial HTML. Nothing here reads\n * the icon registry (see `icon-attribute-value.ts`).\n *\n * Decorative by default (`aria-hidden`) unless the caller supplies a genuine\n * accessibility prop, matching Lucide's own Icon behaviour.\n */\nexport function RIcon({\n  componentAttributes,\n  styleProps = {},\n  className,\n}: Readonly<{\n  componentAttributes?: { [key: string]: any };\n  styleProps?: { [key: string]: any };\n  className?: string;\n}>) {\n  // An unset attribute shows the placeholder, matching how every other\n  // component in this library renders sensibly with no configuration. A value\n  // that is set but unusable is a data error and renders nothing, per the\n  // unresolvable-icon rule for published pages.\n  const value = componentAttributes?.['icon-name-3-resolutions'];\n  const iconNode = value == null ? PLACEHOLDER_ICON_NODE : resolveIconAttributeValue(value);\n\n  if (!iconNode) {\n    return null;\n  }\n\n  const a11yProps = pickA11yProps(componentAttributes);\n  const hasA11yProp = Object.keys(a11yProps).length > 0;\n\n  const svgProps: SVGProps<SVGSVGElement> = {\n    xmlns: 'http://www.w3.org/2000/svg',\n    width: DEFAULT_SIZE,\n    height: DEFAULT_SIZE,\n    viewBox: '0 0 24 24',\n    fill: 'none',\n    stroke: 'currentColor',\n    strokeWidth: 2,\n    strokeLinecap: 'round',\n    strokeLinejoin: 'round',\n    ...(className !== undefined ? { className } : {}),\n    style: styleProps as CSSProperties,\n    ...a11yProps,\n    ...(hasA11yProp ? {} : { 'aria-hidden': true }),\n  };\n\n  return createElement('svg', svgProps, ...renderIconChildren(iconNode));\n}\n\nconst pickA11yProps = (componentAttributes?: {\n  [key: string]: any;\n}): Record<string, string> => {\n  if (!componentAttributes) return {};\n\n  const a11yProps: Record<string, string> = {};\n  for (const key of A11Y_ATTR_KEYS) {\n    const value = componentAttributes[key];\n    if (typeof value === 'string' && value.length > 0) {\n      a11yProps[key] = value;\n    }\n  }\n  return a11yProps;\n};\n","import { RIcon } from './RIcon';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type { DisplayCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.rIcon,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({\n    componentAttributes,\n    styleProps,\n    className,\n  }: {\n    componentAttributes?: { [key: string]: any };\n    styleProps?: { [key: string]: any };\n    className?: string;\n  }) => (\n    <RIcon\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n    />\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","/**\n * Always renders an `<svg>`, a replaced element that honours sizing, margin\n * and colour (via currentColor) directly, so a wrapper div would only add a\n * redundant element. No configuration changes that.\n */\nexport const resolveNoContainer = (): boolean => false;\n","/**\n * The `<svg>` carries the className. An unresolvable attribute renders no\n * element at all, but that is a data error rather than a different root type,\n * so the configured happy path is what this answers for.\n */\nexport const resolveRendersOwnElement = (): boolean => true;\n","import type { DefaultAttributeValue } from '@source-components-react/types';\nimport { PLACEHOLDER_ICON } from './icon-attribute-value';\n\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    // Holds `{ name, iconNode }`, not a bare name: the composer stamps the\n    // geometry when the author picks the icon so the render path never needs\n    // the icon registry.\n    name: 'icon-name-3-resolutions',\n    defaultValues: {\n      all: PLACEHOLDER_ICON,\n    },\n  },\n];\n","import type { AttributeEditModeConfig } from '@source-components-react/types';\nimport { PLACEHOLDER_ICON } from './icon-attribute-value';\n\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: 'icon-name-3-resolutions',\n    heading: 'Icon',\n    subHeading:\n      'Search by name or by what the icon depicts (e.g. \"launch\" finds the rocket), for 3 different device resolutions',\n    defaultValues: {\n      all: PLACEHOLDER_ICON,\n    },\n  },\n];\n","import { parseInlineMarkers } from '../inline-markup/parse-inline-markers';\n\nexport type InlineTextRun = { kind: 'text'; text: string };\nexport type InlineLinkRun = { kind: 'link'; index: number; text: string };\nexport type InlineRun = InlineTextRun | InlineLinkRun;\n\n/**\n * @deprecated Use `parseInlineMarkers` from `shared/inline-markup`.\n *\n * The link-only view of the grammar, kept because it is public API. Markers now\n * also carry marks (`{b|word}`) and line breaks (`{br|}`), which this shape\n * cannot express, so anything that is not a linked span is flattened into text:\n * marked spans contribute their words without the marks, and a break\n * contributes a newline. That is exactly what a caller stripping markers out of\n * a string wants — `resolveAccordionSummary` is the one in this package — and\n * it is wrong for anything that means to render them.\n *\n * Adjacent text is merged, so text with no linked spans is still one run.\n */\nexport function parseInlineLinkMarkers(value: string): InlineRun[] {\n  const runs: InlineRun[] = [];\n  let pendingText = '';\n\n  const flushPendingText = () => {\n    if (pendingText !== '') {\n      runs.push({ kind: 'text', text: pendingText });\n      pendingText = '';\n    }\n  };\n\n  for (const run of parseInlineMarkers(value)) {\n    if (run.kind === 'span' && run.index !== undefined) {\n      flushPendingText();\n      runs.push({ kind: 'link', index: run.index, text: run.text });\n      continue;\n    }\n    pendingText += run.kind === 'break' ? '\\n' : run.text;\n  }\n\n  flushPendingText();\n\n  return runs;\n}\n","/**\n * The preset vocabulary for `r-button`.\n *\n * Three independent axes rather than one flat list of named buttons: five\n * variants times four tones times five sizes is a hundred looks from three\n * short tables, and an author picks each axis without us having to have\n * anticipated the combination.\n *\n * Every preset name here is persisted customer data, exactly like an icon\n * name. A name that has shipped is never deleted or repurposed — add a new\n * one and alias the old one, or published pages change appearance on upgrade.\n */\n\n/** How the button is filled and bordered. */\nexport const BUTTON_VARIANTS = ['solid', 'soft', 'outline', 'ghost', 'link'] as const;\n\n/** Which colour family the variant draws from. */\nexport const BUTTON_TONES = ['brand', 'neutral', 'danger', 'success'] as const;\n\n/** Type scale, padding and control height, smallest to largest. */\nexport const BUTTON_SIZES = ['xs', 'sm', 'md', 'lg', 'xl'] as const;\n\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\nexport type ButtonTone = (typeof BUTTON_TONES)[number];\nexport type ButtonSize = (typeof BUTTON_SIZES)[number];\n\nexport type ButtonPreset = {\n  variant: ButtonVariant;\n  tone: ButtonTone;\n  size: ButtonSize;\n};\n\n/**\n * What an unconfigured button looks like. Dropping the component on a canvas\n * and touching nothing must already produce something shippable.\n */\nexport const DEFAULT_BUTTON_PRESET: ButtonPreset = {\n  variant: 'solid',\n  tone: 'brand',\n  size: 'md',\n};\n\nexport const isButtonVariant = (value: unknown): value is ButtonVariant =>\n  BUTTON_VARIANTS.includes(value as ButtonVariant);\n\nexport const isButtonTone = (value: unknown): value is ButtonTone =>\n  BUTTON_TONES.includes(value as ButtonTone);\n\nexport const isButtonSize = (value: unknown): value is ButtonSize =>\n  BUTTON_SIZES.includes(value as ButtonSize);\n\n/**\n * The colour slots a tone fills.\n *\n * `hover` / `active` / `softHover` are consumed by the library stylesheet\n * (`button-library-stylesheet.ts`) via the per-instance fill slots. Changing\n * a tone here is still a palette change, not a delivery change.\n */\nexport type TonePalette = {\n  /** Solid fill, and the text colour for outline / ghost / link. */\n  base: string;\n  hover: string;\n  active: string;\n  /** Text placed on top of `base`. */\n  contrast: string;\n  /** Outline border, kept separate so neutral can be lighter than its text. */\n  border: string;\n  /** Tinted fill for the soft variant. */\n  soft: string;\n  softHover: string;\n  /** Text placed on top of `soft`. */\n  softContrast: string;\n};\n\n/**\n * Defaults only. Every one of these reaches the page as the fallback half of a\n * `var(--rulecms-button-…, default)`, so a customer rebrands every button on\n * their site by defining the custom property once, with no per-button edits\n * and no composer access. See `button-css-variables.ts`.\n */\nexport const TONE_PALETTES: Record<ButtonTone, TonePalette> = {\n  brand: {\n    base: '#2563eb',\n    hover: '#1d4ed8',\n    active: '#1e40af',\n    contrast: '#ffffff',\n    border: '#2563eb',\n    soft: '#eff6ff',\n    softHover: '#dbeafe',\n    softContrast: '#1d4ed8',\n  },\n  neutral: {\n    base: '#334155',\n    hover: '#1e293b',\n    active: '#0f172a',\n    contrast: '#ffffff',\n    // Deliberately lighter than `base`: a secondary button whose border matches\n    // its dark label text reads as heavy as a primary one.\n    border: '#cbd5e1',\n    soft: '#f1f5f9',\n    softHover: '#e2e8f0',\n    softContrast: '#334155',\n  },\n  danger: {\n    base: '#dc2626',\n    hover: '#b91c1c',\n    active: '#991b1b',\n    contrast: '#ffffff',\n    border: '#dc2626',\n    soft: '#fef2f2',\n    softHover: '#fee2e2',\n    softContrast: '#b91c1c',\n  },\n  success: {\n    base: '#16a34a',\n    hover: '#15803d',\n    active: '#166534',\n    contrast: '#ffffff',\n    border: '#16a34a',\n    soft: '#f0fdf4',\n    softHover: '#dcfce7',\n    softContrast: '#15803d',\n  },\n};\n\nexport type SizeScale = {\n  fontSize: string;\n  /** Floor for the control height. WCAG 2.2 AA wants at least 24px. */\n  minHeight: string;\n  paddingInline: string;\n  paddingBlock: string;\n  borderRadius: string;\n  /** Space between an icon and the label. */\n  gap: string;\n};\n\nexport const SIZE_SCALES: Record<ButtonSize, SizeScale> = {\n  xs: {\n    fontSize: '12px',\n    minHeight: '28px',\n    paddingInline: '10px',\n    paddingBlock: '4px',\n    borderRadius: '4px',\n    gap: '4px',\n  },\n  sm: {\n    fontSize: '14px',\n    minHeight: '34px',\n    paddingInline: '12px',\n    paddingBlock: '6px',\n    borderRadius: '6px',\n    gap: '6px',\n  },\n  md: {\n    fontSize: '16px',\n    minHeight: '40px',\n    paddingInline: '16px',\n    paddingBlock: '8px',\n    borderRadius: '8px',\n    gap: '8px',\n  },\n  lg: {\n    fontSize: '18px',\n    minHeight: '48px',\n    paddingInline: '20px',\n    paddingBlock: '10px',\n    borderRadius: '10px',\n    gap: '8px',\n  },\n  xl: {\n    fontSize: '20px',\n    minHeight: '56px',\n    paddingInline: '24px',\n    paddingBlock: '12px',\n    borderRadius: '12px',\n    gap: '10px',\n  },\n};\n","import type { ButtonTone } from './button-presets';\n\n/**\n * Every colour a preset emits goes out as `var(--rulecms-button-…, default)`.\n *\n * This is the whole white-label story for buttons. A customer who sets\n *\n *   :root { --rulecms-button-brand: #c8102e; }\n *\n * once in their own stylesheet rebrands every RuleCMS button on the site,\n * without touching a widget, without composer access, and without us shipping\n * a theme provider or a design system to their page. Authors who want one\n * button to differ still override it per column, as before.\n */\nexport const BUTTON_CSS_VARIABLE_PREFIX = '--rulecms-button';\n\n/**\n * Per-instance slots the library stylesheet reads. The preset writes these on\n * the element; a column `backgroundColor` rewrite and a future widget-level\n * token both land on the same names.\n */\nexport const BUTTON_FILL_VARIABLE = `${BUTTON_CSS_VARIABLE_PREFIX}-fill`;\nexport const BUTTON_FILL_HOVER_VARIABLE = `${BUTTON_CSS_VARIABLE_PREFIX}-fill-hover`;\nexport const BUTTON_FILL_ACTIVE_VARIABLE = `${BUTTON_CSS_VARIABLE_PREFIX}-fill-active`;\nexport const BUTTON_INK_VARIABLE = `${BUTTON_CSS_VARIABLE_PREFIX}-ink`;\nexport const BUTTON_INK_HOVER_VARIABLE = `${BUTTON_CSS_VARIABLE_PREFIX}-ink-hover`;\nexport const BUTTON_BORDER_VARIABLE = `${BUTTON_CSS_VARIABLE_PREFIX}-border`;\nexport const BUTTON_RING_VARIABLE = `${BUTTON_CSS_VARIABLE_PREFIX}-ring`;\n\n/**\n * The suffixes on a tone's custom properties. `''` is the tone's main colour,\n * e.g. `--rulecms-button-brand`.\n */\nexport type ToneVariableSlot =\n  | ''\n  | 'hover'\n  | 'active'\n  | 'contrast'\n  | 'border'\n  | 'soft'\n  | 'soft-hover'\n  | 'soft-contrast';\n\nexport const toneCssVariable = (tone: ButtonTone, slot: ToneVariableSlot = ''): string =>\n  slot === ''\n    ? `${BUTTON_CSS_VARIABLE_PREFIX}-${tone}`\n    : `${BUTTON_CSS_VARIABLE_PREFIX}-${tone}-${slot}`;\n\n/**\n * A `var()` reference carrying our default as the fallback, so the button is\n * fully styled whether or not the host defines anything.\n */\nexport const cssVarWithFallback = (variableName: string, fallback: string): string =>\n  `var(${variableName}, ${fallback})`;\n\n/** Shorthand for the common case: one tone slot with its palette default. */\nexport const toneColor = (\n  tone: ButtonTone,\n  slot: ToneVariableSlot,\n  fallback: string\n): string => cssVarWithFallback(toneCssVariable(tone, slot), fallback);\n","import type { CSSProperties } from 'react';\nimport { SIZE_SCALES, TONE_PALETTES } from './button-presets';\nimport type { ButtonPreset, ButtonTone } from './button-presets';\nimport {\n  BUTTON_BORDER_VARIABLE,\n  BUTTON_FILL_ACTIVE_VARIABLE,\n  BUTTON_FILL_HOVER_VARIABLE,\n  BUTTON_FILL_VARIABLE,\n  BUTTON_INK_HOVER_VARIABLE,\n  BUTTON_INK_VARIABLE,\n  BUTTON_RING_VARIABLE,\n  toneColor,\n} from './button-css-variables';\n\nconst asVars = (vars: Record<string, string>): CSSProperties => vars as CSSProperties;\n\n/**\n * The rules that stop a native `<button>` looking like a native `<button>`.\n *\n * Two of these do most of the work. `appearance: none` removes the platform\n * chrome iOS Safari still paints on form controls, and `fontFamily: inherit`\n * opts out of the browser's refusal to inherit typography into form controls —\n * which is the single biggest reason an unstyled button looks foreign on a\n * designed page. Inheriting means the button picks up the customer's brand\n * typeface with no configuration at all.\n */\nconst structuralStyles: CSSProperties = {\n  appearance: 'none',\n  WebkitAppearance: 'none',\n  boxSizing: 'border-box',\n  display: 'inline-flex',\n  alignItems: 'center',\n  justifyContent: 'center',\n  textAlign: 'center',\n  textDecoration: 'none',\n  cursor: 'pointer',\n  userSelect: 'none',\n  WebkitUserSelect: 'none',\n  verticalAlign: 'middle',\n  borderStyle: 'solid',\n  fontFamily: 'inherit',\n  fontWeight: 600,\n  lineHeight: 1.25,\n  // Eases the library stylesheet's :hover / :active colour change.\n  transition: 'background-color 150ms ease, border-color 150ms ease, color 150ms ease',\n};\n\nconst sizeStyles = (preset: ButtonPreset): CSSProperties => {\n  const scale = SIZE_SCALES[preset.size];\n  return {\n    fontSize: scale.fontSize,\n    minHeight: scale.minHeight,\n    // Logical properties so a right-to-left page mirrors correctly.\n    paddingInline: scale.paddingInline,\n    paddingBlock: scale.paddingBlock,\n    borderRadius: scale.borderRadius,\n    gap: scale.gap,\n  };\n};\n\n/**\n * Every variant keeps a 1px border, transparent where it is not wanted, so\n * switching preset never changes the button's box and never nudges the layout\n * around it.\n */\nconst variantStyles = (preset: ButtonPreset): CSSProperties => {\n  const palette = TONE_PALETTES[preset.tone];\n  const tone: ButtonTone = preset.tone;\n\n  switch (preset.variant) {\n    case 'solid':\n      return {\n        borderWidth: '1px',\n        backgroundColor: toneColor(tone, '', palette.base),\n        borderColor: toneColor(tone, '', palette.base),\n        color: toneColor(tone, 'contrast', palette.contrast),\n      };\n    case 'soft':\n      return {\n        borderWidth: '1px',\n        backgroundColor: toneColor(tone, 'soft', palette.soft),\n        borderColor: 'transparent',\n        color: toneColor(tone, 'soft-contrast', palette.softContrast),\n      };\n    case 'outline':\n      return {\n        borderWidth: '1px',\n        backgroundColor: 'transparent',\n        borderColor: toneColor(tone, 'border', palette.border),\n        color: toneColor(tone, '', palette.base),\n      };\n    case 'ghost':\n      return {\n        borderWidth: '1px',\n        backgroundColor: 'transparent',\n        borderColor: 'transparent',\n        color: toneColor(tone, '', palette.base),\n      };\n    case 'link':\n    default:\n      // Reads as prose, not as a control: the size scale's box is stripped\n      // back off so it can sit in a sentence without a padded hit area.\n      return {\n        borderWidth: 0,\n        backgroundColor: 'transparent',\n        borderColor: 'transparent',\n        color: toneColor(tone, '', palette.base),\n        minHeight: 0,\n        paddingInline: 0,\n        paddingBlock: 0,\n        borderRadius: 0,\n        textDecoration: 'underline',\n        textUnderlineOffset: '0.2em',\n      };\n  }\n};\n\n/**\n * Per-instance colour slots the library stylesheet reads. Tone tokens stay\n * in the `var(--rulecms-button-brand, fallback)` form so a host `:root`\n * rebrand still wins.\n */\nconst variantCssVariables = (preset: ButtonPreset): CSSProperties => {\n  const palette = TONE_PALETTES[preset.tone];\n  const tone: ButtonTone = preset.tone;\n  const ink = toneColor(tone, '', palette.base);\n  const inkHover = toneColor(tone, 'hover', palette.hover);\n  const contrast = toneColor(tone, 'contrast', palette.contrast);\n\n  switch (preset.variant) {\n    case 'solid':\n      return asVars({\n        [BUTTON_FILL_VARIABLE]: toneColor(tone, '', palette.base),\n        [BUTTON_FILL_HOVER_VARIABLE]: toneColor(tone, 'hover', palette.hover),\n        [BUTTON_FILL_ACTIVE_VARIABLE]: toneColor(tone, 'active', palette.active),\n        [BUTTON_INK_VARIABLE]: contrast,\n        [BUTTON_INK_HOVER_VARIABLE]: contrast,\n        [BUTTON_BORDER_VARIABLE]: toneColor(tone, '', palette.base),\n        [BUTTON_RING_VARIABLE]: ink,\n      });\n    case 'soft':\n      return asVars({\n        [BUTTON_FILL_VARIABLE]: toneColor(tone, 'soft', palette.soft),\n        [BUTTON_FILL_HOVER_VARIABLE]: toneColor(tone, 'soft-hover', palette.softHover),\n        [BUTTON_FILL_ACTIVE_VARIABLE]: `color-mix(in srgb, ${toneColor(tone, 'soft-hover', palette.softHover)} 88%, black)`,\n        [BUTTON_INK_VARIABLE]: toneColor(tone, 'soft-contrast', palette.softContrast),\n        [BUTTON_INK_HOVER_VARIABLE]: toneColor(tone, 'soft-contrast', palette.softContrast),\n        [BUTTON_BORDER_VARIABLE]: 'transparent',\n        [BUTTON_RING_VARIABLE]: ink,\n      });\n    case 'outline':\n      return asVars({\n        [BUTTON_FILL_VARIABLE]: 'transparent',\n        [BUTTON_FILL_HOVER_VARIABLE]: toneColor(tone, 'soft', palette.soft),\n        [BUTTON_FILL_ACTIVE_VARIABLE]: toneColor(tone, 'soft-hover', palette.softHover),\n        [BUTTON_INK_VARIABLE]: ink,\n        [BUTTON_INK_HOVER_VARIABLE]: inkHover,\n        [BUTTON_BORDER_VARIABLE]: toneColor(tone, 'border', palette.border),\n        [BUTTON_RING_VARIABLE]: ink,\n      });\n    case 'ghost':\n      return asVars({\n        [BUTTON_FILL_VARIABLE]: 'transparent',\n        [BUTTON_FILL_HOVER_VARIABLE]: toneColor(tone, 'soft', palette.soft),\n        [BUTTON_FILL_ACTIVE_VARIABLE]: toneColor(tone, 'soft-hover', palette.softHover),\n        [BUTTON_INK_VARIABLE]: ink,\n        [BUTTON_INK_HOVER_VARIABLE]: inkHover,\n        [BUTTON_BORDER_VARIABLE]: 'transparent',\n        [BUTTON_RING_VARIABLE]: ink,\n      });\n    case 'link':\n    default:\n      return asVars({\n        [BUTTON_FILL_VARIABLE]: 'transparent',\n        [BUTTON_FILL_HOVER_VARIABLE]: 'transparent',\n        [BUTTON_FILL_ACTIVE_VARIABLE]: 'transparent',\n        [BUTTON_INK_VARIABLE]: ink,\n        [BUTTON_INK_HOVER_VARIABLE]: inkHover,\n        [BUTTON_BORDER_VARIABLE]: 'transparent',\n        [BUTTON_RING_VARIABLE]: ink,\n      });\n  }\n};\n\nconst COLOR_STYLE_KEYS = ['backgroundColor', 'color', 'borderColor'] as const;\n\n/**\n * The preset's contribution to the button's inline style, including resting\n * colours as real CSS properties. The composer picker uses this so a swatch\n * looks right without the library stylesheet.\n *\n * Callers spread column `styleProps` *after* this, which is what makes the\n * preset a set of defaults rather than a set of rules: anything the author\n * touched in Column Styles lands later in the object and wins.\n */\nexport function buildButtonPresetStyles(preset: ButtonPreset): CSSProperties {\n  return {\n    ...structuralStyles,\n    ...sizeStyles(preset),\n    ...variantStyles(preset),\n    ...variantCssVariables(preset),\n  };\n}\n\n/**\n * What `RButton` puts on the element. Same as the picker styles, minus the\n * colour properties the library stylesheet owns — those stay on the element\n * only as custom properties so `:hover` can change them.\n */\nexport function buildButtonRenderStyles(preset: ButtonPreset): CSSProperties {\n  const styles = { ...buildButtonPresetStyles(preset) };\n  for (const key of COLOR_STYLE_KEYS) {\n    delete styles[key];\n  }\n  return styles;\n}\n\n/**\n * Column styles still win on everything except `backgroundColor`, which is\n * remapped onto the fill slots so hover/active follow the authored colour\n * instead of the preset's blue-family hover.\n */\nexport function applyButtonColumnStyles(\n  styleProps?: CSSProperties | { [key: string]: unknown }\n): CSSProperties {\n  if (!styleProps) {\n    return {};\n  }\n  const { backgroundColor, ...rest } = styleProps as CSSProperties & {\n    backgroundColor?: string;\n  };\n  if (backgroundColor === undefined || backgroundColor === '') {\n    return rest;\n  }\n  return {\n    ...rest,\n    ...asVars({\n      [BUTTON_FILL_VARIABLE]: backgroundColor,\n      [BUTTON_FILL_HOVER_VARIABLE]: `color-mix(in srgb, ${backgroundColor} 82%, black)`,\n      [BUTTON_FILL_ACTIVE_VARIABLE]: `color-mix(in srgb, ${backgroundColor} 70%, black)`,\n    }),\n  };\n}\n\n/** Applied on top when the button is disabled, before column styles. */\nexport const disabledStyles: CSSProperties = {\n  opacity: 0.5,\n  cursor: 'not-allowed',\n};\n","import type { MouseEvent } from 'react';\nimport type { WidgetInstanceProps } from '@source-components-react/types';\n\n/** The only key `r-button` reads out of the host's per-instance props. */\nexport const BUTTON_CLICK_HANDLER_PROP = 'onClick';\n\nexport type ButtonClickHandler = (\n  event: MouseEvent<HTMLButtonElement | HTMLAnchorElement>\n) => void;\n\n/**\n * The host's click handler for this instance, or `undefined` when there is\n * nothing to attach.\n *\n * Two reasons to answer `undefined` even when the host supplied something:\n *\n * - **Disabled wins.** A disabled plain `<button>` is inert natively, but a\n *   disabled *linked* button renders `<a role=\"link\" aria-disabled=\"true\">`,\n *   which has no native inertness at all — a handler left on it would fire on\n *   every click and make \"disabled\" a lie.\n * - **The bag is untyped.** It crosses a package boundary as\n *   `{ [key: string]: any }`, so a host that passes a string here should get a\n *   button that does nothing rather than a React crash on the customer's page.\n */\nexport const resolveButtonClickHandler = ({\n  widgetInstanceProps,\n  disabled,\n}: {\n  widgetInstanceProps?: WidgetInstanceProps;\n  disabled: boolean;\n}): ButtonClickHandler | undefined => {\n  if (disabled) {\n    return undefined;\n  }\n  const handler = widgetInstanceProps?.[BUTTON_CLICK_HANDLER_PROP];\n  return typeof handler === 'function' ? (handler as ButtonClickHandler) : undefined;\n};\n","import {\n  DEFAULT_BUTTON_PRESET,\n  isButtonSize,\n  isButtonTone,\n  isButtonVariant,\n} from './button-presets';\nimport type { ButtonPreset } from './button-presets';\n\n/**\n * Turns whatever the composer stored into a complete preset.\n *\n * Tolerant on every axis independently: a value saved by a newer composer, a\n * half-written object, or an outright wrong type costs that one axis and\n * nothing else. A published page must never fail to render a button because\n * one field went stale.\n */\nexport function resolveButtonPreset(value: unknown): ButtonPreset {\n  if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n    return DEFAULT_BUTTON_PRESET;\n  }\n\n  const candidate = value as Partial<Record<keyof ButtonPreset, unknown>>;\n\n  return {\n    variant: isButtonVariant(candidate.variant)\n      ? candidate.variant\n      : DEFAULT_BUTTON_PRESET.variant,\n    tone: isButtonTone(candidate.tone) ? candidate.tone : DEFAULT_BUTTON_PRESET.tone,\n    size: isButtonSize(candidate.size) ? candidate.size : DEFAULT_BUTTON_PRESET.size,\n  };\n}\n","/**\n * The attribute names `r-button` reads.\n *\n * Every one is button-specific, including the label and the icon, which are\n * conceptually the same thing `r-text` and `r-icon` already offer. Sharing\n * their attribute names would have saved two lines of editor wiring and cost\n * the documentation contract: RuleCMS publishes one page per component-scope\n * attribute and requires exactly one owning component, so a shared name has\n * no single page to link to from the drawer.\n *\n * Reuse happens a level down instead. The composer resolves an attribute to\n * an *editor* by name, and several names already share one editor, so these\n * point at the existing text field and the existing Lucide picker without\n * either component having to give up ownership of its own settings.\n *\n * Nothing here duplicates a column style attribute. Colour, border, radius,\n * spacing and width already have editors that write into the same bag, and a\n * second way to set the same CSS property would only create a question about\n * which one wins.\n */\nexport const BUTTON_PRESET_ATTRIBUTE = 'button-preset-3-resolutions';\nexport const BUTTON_LABEL_ATTRIBUTE = 'button-label-3-resolutions';\nexport const BUTTON_LINK_ATTRIBUTE = 'button-link-3-resolutions';\nexport const BUTTON_DISABLED_ATTRIBUTE = 'button-disabled-3-resolutions';\nexport const BUTTON_ICON_ATTRIBUTE = 'button-icon-3-resolutions';\nexport const BUTTON_ICON_POSITION_ATTRIBUTE = 'button-icon-position-3-resolutions';\nexport const BUTTON_ARIA_LABEL_ATTRIBUTE = 'aria-label';\n\n/** What a freshly dropped button says before the author types anything. */\nexport const DEFAULT_BUTTON_LABEL = 'Button';\n\nexport const ICON_POSITIONS = ['leading', 'trailing'] as const;\nexport type IconPosition = (typeof ICON_POSITIONS)[number];\n\nexport const DEFAULT_ICON_POSITION: IconPosition = 'leading';\n\ntype Attributes = { [key: string]: any } | undefined;\n\n/**\n * An unset label falls back to the placeholder, but a label the author\n * deliberately cleared stays cleared — that is how an icon-only button is\n * made, so an empty string cannot be treated as \"nothing set\".\n */\nexport const resolveButtonLabel = (componentAttributes: Attributes): string => {\n  const value = componentAttributes?.[BUTTON_LABEL_ATTRIBUTE];\n  if (value === undefined || value === null) {\n    return DEFAULT_BUTTON_LABEL;\n  }\n  return typeof value === 'string' ? value : String(value);\n};\n\n/**\n * Select editors in the composer store `'true'` / `'false'` as strings, while\n * a hand-written or migrated config may carry a real boolean. Both mean the\n * same thing here.\n */\nexport const resolveButtonDisabled = (componentAttributes: Attributes): boolean => {\n  const value = componentAttributes?.[BUTTON_DISABLED_ATTRIBUTE];\n  if (typeof value === 'boolean') {\n    return value;\n  }\n  return typeof value === 'string' && value.trim().toLowerCase() === 'true';\n};\n\nexport const resolveIconPosition = (componentAttributes: Attributes): IconPosition => {\n  const value = componentAttributes?.[BUTTON_ICON_POSITION_ATTRIBUTE];\n  return value === 'trailing' ? 'trailing' : DEFAULT_ICON_POSITION;\n};\n\nexport const resolveButtonAriaLabel = (componentAttributes: Attributes): string | undefined => {\n  const value = componentAttributes?.[BUTTON_ARIA_LABEL_ATTRIBUTE];\n  if (typeof value !== 'string') {\n    return undefined;\n  }\n  const trimmed = value.trim();\n  return trimmed === '' ? undefined : trimmed;\n};\n","import { createElement } from 'react';\nimport type { CSSProperties, ReactNode } from 'react';\nimport { resolveLinkAttributes } from '@source-components-react/shared/links';\nimport { renderIconChildren } from '@source-components-react/shared/icons';\nimport type { IconNode, WidgetInstanceProps } from '@source-components-react/types';\nimport { resolveIconAttributeValue } from '../r-icon/icon-attribute-value';\nimport {\n  applyButtonColumnStyles,\n  buildButtonRenderStyles,\n  disabledStyles,\n} from './button-preset-styles';\nimport { resolveButtonClickHandler } from './button-instance-props';\nimport { resolveButtonPreset } from './resolve-button-preset';\nimport {\n  BUTTON_ICON_ATTRIBUTE,\n  BUTTON_LINK_ATTRIBUTE,\n  BUTTON_PRESET_ATTRIBUTE,\n  resolveButtonAriaLabel,\n  resolveButtonDisabled,\n  resolveButtonLabel,\n  resolveIconPosition,\n} from './button-attributes';\n\n/**\n * One interactive element — `<a>` when the button links somewhere, otherwise\n * `<button>` — and never a wrapper around it.\n *\n * Appearance is a preset by default and column styles on top: the preset\n * styles are spread first so anything the author set in Column Styles lands\n * later in the object and wins. Colour properties the library stylesheet\n * owns (`background-color`, `color`, `border-color`) travel as custom\n * properties instead, so `:hover` can change them. A column background\n * rewrite remaps onto those slots. That is why this component declares no\n * style attributes of its own.\n *\n * Nothing here needs the browser. The element, its label and any icon\n * geometry are all in the server's HTML, with no resolution after hydration.\n * A host's `onClick` is the one exception, and it can only be: an event\n * listener does not exist until the widget hydrates, which is why the disabled\n * state is still expressed in markup rather than by cancelling clicks.\n */\nexport function RButton({\n  componentAttributes,\n  styleProps,\n  className,\n  widgetInstanceProps,\n}: Readonly<{\n  componentAttributes?: { [key: string]: any };\n  /** Column styles from the widget pipeline. Always applied to the button itself. */\n  styleProps?: { [key: string]: any };\n  /** Host-app classes for this column. The single root element carries them. */\n  className?: string;\n  /**\n   * Props the embedding app addressed at this instance. `onClick` is the only\n   * key read; see `button-instance-props.ts`.\n   */\n  widgetInstanceProps?: WidgetInstanceProps;\n}>) {\n  const preset = resolveButtonPreset(componentAttributes?.[BUTTON_PRESET_ATTRIBUTE]);\n  const disabled = resolveButtonDisabled(componentAttributes);\n  const onClick = resolveButtonClickHandler({ widgetInstanceProps, disabled });\n\n  const style: CSSProperties = {\n    ...buildButtonRenderStyles(preset),\n    ...(disabled ? disabledStyles : {}),\n    ...applyButtonColumnStyles(styleProps),\n  };\n\n  const content = renderContent(componentAttributes);\n  const ariaLabel = resolveButtonAriaLabel(componentAttributes);\n  const link = resolveLinkAttributes(componentAttributes?.[BUTTON_LINK_ATTRIBUTE]);\n\n  const sharedProps = {\n    style,\n    'data-rulecms-button': '',\n    'data-rulecms-button-variant': preset.variant,\n    'data-rulecms-button-tone': preset.tone,\n    ...(className !== undefined ? { className } : {}),\n    ...(ariaLabel !== undefined ? { 'aria-label': ariaLabel } : {}),\n    ...(onClick !== undefined ? { onClick } : {}),\n  };\n\n  if (link) {\n    // A disabled link keeps its role and its look but loses its href, which is\n    // what makes it unreachable by pointer and by keyboard. The alternative —\n    // an href plus a click handler that cancels it — would need JavaScript on\n    // the page to be correct, and a widget must be right before it hydrates.\n    if (disabled) {\n      return (\n        <a {...sharedProps} role=\"link\" aria-disabled=\"true\">\n          {content}\n        </a>\n      );\n    }\n    // A host handler on a link runs and then the browser still navigates. The\n    // link is what the author configured and what the markup promises, so\n    // swallowing it here would make the anchor lie about where it goes; a host\n    // that wants the click instead of the navigation calls preventDefault.\n    return (\n      <a {...link} {...sharedProps}>\n        {content}\n      </a>\n    );\n  }\n\n  // `type=\"button\"` is not a default worth losing: an untyped button dropped\n  // inside a host page's existing <form> submits it on click.\n  return (\n    <button type=\"button\" disabled={disabled} {...sharedProps}>\n      {content}\n    </button>\n  );\n}\n\nconst renderContent = (componentAttributes?: { [key: string]: any }): ReactNode => {\n  const label = resolveButtonLabel(componentAttributes);\n  const iconNode = resolveIconAttributeValue(componentAttributes?.[BUTTON_ICON_ATTRIBUTE]);\n\n  if (!iconNode) {\n    return label;\n  }\n\n  const icon = renderButtonIcon(iconNode);\n  if (resolveIconPosition(componentAttributes) === 'trailing') {\n    return (\n      <>\n        {label}\n        {icon}\n      </>\n    );\n  }\n  return (\n    <>\n      {icon}\n      {label}\n    </>\n  );\n};\n\n/**\n * Sized in `em` so it tracks the button's font size across every preset and\n * any font size the author sets, and hidden from assistive technology because\n * the control is already named by its label or `aria-label`.\n */\nconst renderButtonIcon = (iconNode: IconNode) =>\n  createElement(\n    'svg',\n    {\n      xmlns: 'http://www.w3.org/2000/svg',\n      width: '1em',\n      height: '1em',\n      viewBox: '0 0 24 24',\n      fill: 'none',\n      stroke: 'currentColor',\n      strokeWidth: 2,\n      strokeLinecap: 'round',\n      strokeLinejoin: 'round',\n      style: { flexShrink: 0 },\n      'aria-hidden': true,\n    },\n    ...renderIconChildren(iconNode)\n  );\n","import { RButton } from './RButton';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type {\n  DisplayCardEntry,\n  WidgetInstanceProps,\n} from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.rButton,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({\n    componentAttributes,\n    styleProps,\n    className,\n    widgetInstanceProps,\n  }: {\n    componentAttributes?: { [key: string]: any };\n    styleProps?: { [key: string]: any };\n    className?: string;\n    widgetInstanceProps?: WidgetInstanceProps;\n  }) => (\n    <RButton\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n      widgetInstanceProps={widgetInstanceProps}\n    />\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","/**\n * Always renders a `<button>` or an `<a>`, both of which take padding, border,\n * background and sizing directly. A wrapper div would add an element that does\n * nothing, and would put the column's background behind the button rather than\n * on it. No configuration changes that.\n */\nexport const resolveNoContainer = (): boolean => false;\n","/**\n * There is always a root element to carry the class: the button is the\n * component, so no configuration leaves it without one.\n */\nexport const resolveRendersOwnElement = (): boolean => true;\n","import type { DefaultAttributeValue } from '@source-components-react/types';\nimport { DEFAULT_BUTTON_PRESET } from './button-presets';\nimport {\n  BUTTON_LABEL_ATTRIBUTE,\n  BUTTON_PRESET_ATTRIBUTE,\n  DEFAULT_BUTTON_LABEL,\n} from './button-attributes';\n\n/**\n * Seeds the button's own semantics only.\n *\n * No style attribute may be seeded here, and the omission is load-bearing\n * rather than an oversight. Defaults are merged into the column's attribute\n * bag before styles are computed, so a seeded `border-radius-3-resolutions`\n * would be present on every button and would override the preset's radius\n * forever — the preset would appear to have no effect on anything a default\n * happened to cover. Leaving the bag empty is what lets \"unset means preset\"\n * be true.\n */\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    name: BUTTON_PRESET_ATTRIBUTE,\n    defaultValues: {\n      all: DEFAULT_BUTTON_PRESET,\n    },\n  },\n  {\n    name: BUTTON_LABEL_ATTRIBUTE,\n    defaultValues: {\n      all: DEFAULT_BUTTON_LABEL,\n    },\n  },\n];\n","import type { AttributeEditModeConfig } from '@source-components-react/types';\nimport { DEFAULT_BUTTON_PRESET } from './button-presets';\nimport {\n  BUTTON_DISABLED_ATTRIBUTE,\n  BUTTON_ICON_ATTRIBUTE,\n  BUTTON_ICON_POSITION_ATTRIBUTE,\n  BUTTON_LABEL_ATTRIBUTE,\n  BUTTON_LINK_ATTRIBUTE,\n  BUTTON_PRESET_ATTRIBUTE,\n  DEFAULT_ICON_POSITION,\n} from './button-attributes';\n\n/**\n * Every setting the button offers, in the order the Modify drawer shows them:\n * how it looks, what it says, where it goes, then the two refinements most\n * buttons never need.\n *\n * Order is not cosmetic. The drawer renders these top to bottom and the\n * documentation registry asserts its pages match that sequence, so moving an\n * entry here moves it in both places at once.\n */\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: BUTTON_PRESET_ATTRIBUTE,\n    heading: 'Button Style',\n    subHeading:\n      'Variant, tone and size. Sets how the button looks when Column Styles leave it alone — anything set there wins, for 3 different device resolutions',\n    defaultValues: { all: DEFAULT_BUTTON_PRESET },\n  },\n  {\n    name: BUTTON_LABEL_ATTRIBUTE,\n    heading: 'Label',\n    subHeading: 'Text shown on the button. Clear it for an icon-only button',\n    defaultValues: {},\n  },\n  {\n    name: BUTTON_LINK_ATTRIBUTE,\n    heading: 'Link',\n    subHeading:\n      'Where the button goes. Setting it renders an anchor styled as a button; leaving it empty renders a plain button, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: BUTTON_ICON_ATTRIBUTE,\n    heading: 'Icon',\n    subHeading:\n      'Optional icon shown beside the label. Search by name or by what the icon depicts (e.g. \"launch\" finds the rocket), for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: BUTTON_ICON_POSITION_ATTRIBUTE,\n    heading: 'Icon Position',\n    subHeading: 'Whether the icon sits before or after the label',\n    defaultValues: { all: DEFAULT_ICON_POSITION },\n  },\n  {\n    name: BUTTON_DISABLED_ATTRIBUTE,\n    heading: 'Disabled',\n    subHeading: 'Renders the button dimmed and non-interactive',\n    defaultValues: {},\n  },\n];\n","/**\n * The attribute names `r-divider` reads.\n *\n * There are two, and the list is short on purpose. A divider is a line, and a\n * line has four things about it worth setting: how thick it is, whether it is\n * broken up, what colour it is, and how far across the column it runs. Only\n * the first two are here.\n *\n * Colour and width are missing because they already exist. The line is drawn\n * in `currentColor`, so Text Color on the column sets it the same way it sets\n * the colour of an icon, and the element is the column's own box, so Width on\n * the column shortens it. Offering a second control for either would only\n * raise the question of which one wins — the same reasoning that keeps colour\n * and border off `r-button`.\n *\n * Both remaining attributes are closed sets rather than free text, which is\n * what lets them go through the composer's shared select and lets the render\n * path treat anything unrecognised as unset. A stored value reaches a CSS\n * property here, so an allowlist is the boundary that keeps it from being\n * whatever a request happened to write.\n */\nexport const DIVIDER_STYLE_ATTRIBUTE = 'divider-style-3-resolutions';\nexport const DIVIDER_THICKNESS_ATTRIBUTE = 'divider-thickness-3-resolutions';\n\nexport const DIVIDER_STYLES = ['solid', 'dashed', 'dotted'] as const;\nexport type DividerStyle = (typeof DIVIDER_STYLES)[number];\nexport const DEFAULT_DIVIDER_STYLE: DividerStyle = 'solid';\n\n/**\n * Weights rather than a free measurement. Anything under a pixel disappears on\n * a low-density screen and anything much over eight reads as a band rather\n * than a rule, so the useful range is small enough to offer whole.\n */\nexport const DIVIDER_THICKNESSES = ['1px', '2px', '3px', '4px', '8px'] as const;\nexport type DividerThickness = (typeof DIVIDER_THICKNESSES)[number];\nexport const DEFAULT_DIVIDER_THICKNESS: DividerThickness = '1px';\n\ntype Attributes = { [key: string]: any } | undefined;\n\nexport const resolveDividerStyle = (componentAttributes: Attributes): DividerStyle => {\n  const value = componentAttributes?.[DIVIDER_STYLE_ATTRIBUTE];\n  return DIVIDER_STYLES.includes(value) ? value : DEFAULT_DIVIDER_STYLE;\n};\n\nexport const resolveDividerThickness = (componentAttributes: Attributes): DividerThickness => {\n  const value = componentAttributes?.[DIVIDER_THICKNESS_ATTRIBUTE];\n  return DIVIDER_THICKNESSES.includes(value) ? value : DEFAULT_DIVIDER_THICKNESS;\n};\n","import type { CSSProperties } from 'react';\nimport { resolveDividerStyle, resolveDividerThickness } from './divider-attributes';\n\n/**\n * The rules that stop a host page's CSS reset having an opinion about the line.\n *\n * Every popular reset has already decided what an `<hr>` looks like, and they\n * disagree: Bootstrap draws one at a quarter opacity, older versions paint it\n * as a background bar with no border at all, browsers with no reset at all\n * still paint the inset 3D groove from the days of Netscape. A widget is\n * dropped into a page whose stylesheet it has never seen, so the only line\n * that renders the same everywhere is one whose box is stated outright.\n *\n * These are inline styles, so they win against anything the host declares —\n * and column styles are spread over them afterwards, so they lose to anything\n * the author sets.\n */\nconst hostResetStyles: CSSProperties = {\n  boxSizing: 'border-box',\n  // The line is the top border. The element itself has no content, and the\n  // other three sides are what the browser's own inset groove is made of.\n  height: 0,\n  borderRightStyle: 'none',\n  borderBottomStyle: 'none',\n  borderLeftStyle: 'none',\n  backgroundColor: 'transparent',\n  opacity: 1,\n  // Spacing around a divider belongs to the row's gap and the column's margin,\n  // the same as it does for every other component. Zero here is a decision,\n  // not an omission: the alternative is spacing that depends on which reset\n  // the host page happens to load.\n  marginBlock: 0,\n  // Only visible once the author narrows the column: an inset rule centres\n  // rather than hugging the left edge, which is what a shortened divider is\n  // almost always for.\n  marginInline: 'auto',\n};\n\n/**\n * The divider's contribution to its own inline style.\n *\n * Written entirely in per-side longhands. Column styles arrive as the\n * all-sides `borderWidth` / `borderStyle` / `borderColor` — different keys,\n * which is what matters: spread later they are appended after these rather\n * than replacing them in place, so an author who sets Border on the column\n * gets a border and not a silent no-op.\n */\nexport function buildDividerStyles(componentAttributes?: {\n  [key: string]: any;\n}): CSSProperties {\n  return {\n    ...hostResetStyles,\n    borderTopStyle: resolveDividerStyle(componentAttributes),\n    borderTopWidth: resolveDividerThickness(componentAttributes),\n    // Inherited, so Text Color on the column colours the line — and a column\n    // that says nothing gets a rule in the same ink as the words around it.\n    borderTopColor: 'currentColor',\n  };\n}\n","import type { CSSProperties } from 'react';\nimport { buildDividerStyles } from './divider-styles';\n\n/**\n * One `<hr>` — nothing else.\n *\n * The element is the whole accessibility story. `<hr>` carries the implicit\n * `separator` role, so a screen reader announces the break between sections\n * without an ARIA attribute anywhere, and a search engine reads the same\n * structure. A styled `<div>` would look identical and say nothing.\n *\n * Appearance is the component's own defaults with column styles on top: the\n * divider's styles are spread first so anything the author set in Column\n * Styles lands later in the object and wins.\n *\n * Nothing here needs the browser. There is no state, no measurement and no\n * resolution after hydration — the line is complete in the server's HTML.\n */\nexport function RDivider({\n  componentAttributes,\n  styleProps,\n  className,\n}: Readonly<{\n  componentAttributes?: { [key: string]: any };\n  /** Column styles from the widget pipeline. Always applied to the rule itself. */\n  styleProps?: { [key: string]: any };\n  /** Host-app classes for this column. The single root element carries them. */\n  className?: string;\n}>) {\n  const style: CSSProperties = {\n    ...buildDividerStyles(componentAttributes),\n    ...styleProps,\n  };\n\n  return <hr style={style} {...(className !== undefined ? { className } : {})} />;\n}\n","/**\n * Always renders an `<hr>`, a block-level element that holds margin, padding,\n * width and border on itself, so a wrapper div would only add a redundant\n * element. No configuration changes that.\n */\nexport const resolveNoContainer = (): boolean => false;\n","import { RDivider } from './RDivider';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type { DisplayCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.rDivider,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({ componentAttributes, styleProps, className }) => (\n    <RDivider\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n    />\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","/**\n * The `<hr>` carries the className. There is always exactly one element and it\n * is always the root, so there is nothing for this to depend on.\n */\nexport const resolveRendersOwnElement = (): boolean => true;\n","import type { DefaultAttributeValue } from '@source-components-react/types';\nimport {\n  DEFAULT_DIVIDER_STYLE,\n  DEFAULT_DIVIDER_THICKNESS,\n  DIVIDER_STYLE_ATTRIBUTE,\n  DIVIDER_THICKNESS_ATTRIBUTE,\n} from './divider-attributes';\n\n/**\n * Seeds the divider's own two settings so the drawer opens on the values a\n * fresh divider is already drawn with, rather than on two empty pickers.\n *\n * Seeding them is safe here in a way it would not be for `r-button`: these\n * names are the divider's own, so nothing in Column Styles writes to them and\n * a seeded value can never shadow a style the author set.\n */\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    name: DIVIDER_STYLE_ATTRIBUTE,\n    defaultValues: {\n      all: DEFAULT_DIVIDER_STYLE,\n    },\n  },\n  {\n    name: DIVIDER_THICKNESS_ATTRIBUTE,\n    defaultValues: {\n      all: DEFAULT_DIVIDER_THICKNESS,\n    },\n  },\n];\n","import type { AttributeEditModeConfig } from '@source-components-react/types';\nimport {\n  DEFAULT_DIVIDER_STYLE,\n  DEFAULT_DIVIDER_THICKNESS,\n  DIVIDER_STYLE_ATTRIBUTE,\n  DIVIDER_THICKNESS_ATTRIBUTE,\n} from './divider-attributes';\n\n/**\n * Both settings the divider offers, in the order the Modify drawer shows them.\n *\n * Order is not cosmetic. The drawer renders these top to bottom and the\n * documentation registry asserts its pages match that sequence, so moving an\n * entry here moves it in both places at once.\n */\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: DIVIDER_STYLE_ATTRIBUTE,\n    heading: 'Line Style',\n    subHeading:\n      'Whether the rule is unbroken or broken up. Colour comes from Text Color on the column, for 3 different device resolutions',\n    defaultValues: { all: DEFAULT_DIVIDER_STYLE },\n  },\n  {\n    name: DIVIDER_THICKNESS_ATTRIBUTE,\n    heading: 'Thickness',\n    subHeading: 'How heavy the rule is, from a hairline to a band',\n    defaultValues: { all: DEFAULT_DIVIDER_THICKNESS },\n  },\n];\n","/**\n * The attribute names `r-embed` reads.\n *\n * Four, and the shortest list that still produces a usable embed: what to\n * frame, what to call it, what shape it is, and whether it loads before the\n * visitor scrolls to it.\n *\n * There is no provider attribute. The provider is derived from the URL, so\n * pasting is the whole interaction and an author cannot pick YouTube and then\n * paste a Vimeo link. There is no width, colour, border or radius either —\n * those are column settings, and a second route to the same CSS property only\n * raises the question of which one is in force.\n *\n * Nothing here can influence the frame's privileges. `sandbox`, `allow` and\n * `referrerpolicy` are fixed per provider in `embed-providers.ts` and are not\n * attributes at all, which is also why this component deliberately has no\n * `pass-through-embed-attributes-*` convention.\n */\nexport const EMBED_URL_ATTRIBUTE = 'embed-url-3-resolutions';\nexport const EMBED_TITLE_ATTRIBUTE = 'embed-title-3-resolutions';\nexport const EMBED_ASPECT_RATIO_ATTRIBUTE = 'embed-aspect-ratio-3-resolutions';\nexport const EMBED_LOADING_ATTRIBUTE = 'embed-loading-3-resolutions';\n\n/**\n * Shapes rather than dimensions. An iframe has no intrinsic size to preserve,\n * so something has to state the proportion or the box collapses to the\n * browser's 300×150 default — and a ratio does it without the author having to\n * work out a height for every screen width.\n *\n * `none` is the escape hatch for content that has no natural shape, a booking\n * form being the obvious one. It is the only value that hands sizing back to\n * the column's Height setting.\n */\nexport const EMBED_ASPECT_RATIOS = ['16:9', '4:3', '1:1', '9:16', '21:9', 'none'] as const;\nexport type EmbedAspectRatio = (typeof EMBED_ASPECT_RATIOS)[number];\n\n/**\n * What an unshaped embed is tall by default. Applied by the component rather\n * than seeded onto the column, so the column's Height still overrides it and\n * clearing that returns the embed to a height that works — the same\n * \"unset means the component's default\" arrangement `r-button` uses for its\n * preset. Sized for a Calendly month view, which is the content that needs it.\n */\nexport const EMBED_UNSHAPED_HEIGHT = '700px';\n\nexport const EMBED_LOADING_VALUES = ['lazy', 'eager'] as const;\nexport type EmbedLoading = (typeof EMBED_LOADING_VALUES)[number];\n\n/**\n * Lazy by default. An embed is a whole third-party document — for YouTube,\n * comfortably more than the rest of the page around it — and most of them sit\n * below the fold. `eager` exists for the ones that do not.\n */\nexport const DEFAULT_EMBED_LOADING: EmbedLoading = 'lazy';\n\ntype Attributes = { [key: string]: any } | undefined;\n\n/**\n * The aspect ratio, or `undefined` when the author has not chosen one and the\n * provider's own default should apply. Anything unrecognised is treated as\n * unset rather than passed through: this value reaches a CSS property.\n */\nexport const resolveEmbedAspectRatio = (\n  componentAttributes: Attributes\n): EmbedAspectRatio | undefined => {\n  const value = componentAttributes?.[EMBED_ASPECT_RATIO_ATTRIBUTE];\n  return EMBED_ASPECT_RATIOS.includes(value) ? value : undefined;\n};\n\nexport const resolveEmbedLoading = (componentAttributes: Attributes): EmbedLoading => {\n  const value = componentAttributes?.[EMBED_LOADING_ATTRIBUTE];\n  return value === 'eager' ? 'eager' : DEFAULT_EMBED_LOADING;\n};\n\n/**\n * An iframe's accessible name. A screen reader reaching an untitled frame\n * announces it as \"frame\" and nothing else, so this never returns empty — an\n * author who clears the field gets the provider's name back rather than a\n * silent frame.\n */\nexport const resolveEmbedTitle = (\n  componentAttributes: Attributes,\n  fallback: string\n): string => {\n  const value = componentAttributes?.[EMBED_TITLE_ATTRIBUTE];\n  if (typeof value !== 'string') {\n    return fallback;\n  }\n  const trimmed = value.trim();\n  return trimmed === '' ? fallback : trimmed;\n};\n\n/** CSS `aspect-ratio` wants `16 / 9`; the attribute stores the familiar `16:9`. */\nexport const toCssAspectRatio = (ratio: EmbedAspectRatio): string | undefined =>\n  ratio === 'none' ? undefined : ratio.replace(':', ' / ');\n","/**\n * The providers `r-embed` will frame, and the only thing standing between an\n * author's paste box and an iframe on a customer's page.\n *\n * This is an allowlist, not a denylist — the opposite of `is-safe-href`, and\n * deliberately so. A link navigates away; an iframe runs a third party's\n * document inside the page the widget was dropped on, with whatever\n * permissions the embedding page grants it. There is no set of dangerous\n * schemes to enumerate here, because everything that is not a provider we have\n * looked at is dangerous.\n *\n * Two consequences worth stating outright:\n *\n * **The `src` is rebuilt, never passed through.** A provider takes the URL the\n * author pasted, extracts the one or two identifiers it recognises, and\n * composes a fresh URL from a hardcoded origin. Nothing an author types\n * reaches the `src` except an id that matched a narrow pattern. A watch page,\n * a share link and an already-embeddable URL all converge on the same output.\n *\n * **The frame's privileges are the provider's, not the author's.** `sandbox`\n * and `allow` are properties of the entry below, not attributes an author can\n * set, which is also why this component has no pass-through convention. A\n * `pass-through-embed-attributes-sandbox` key would hand away every guarantee\n * on this page for the sake of consistency with `r-video`.\n */\nimport type { EmbedAspectRatio } from './embed-attributes';\n\nexport type EmbedProviderId = 'youtube' | 'vimeo' | 'google-maps' | 'calendly';\n\nexport type EmbedProvider = {\n  id: EmbedProviderId;\n  /** Provider name for the composer and for the docs. */\n  label: string;\n  /**\n   * Hosts an author may paste, already normalised (lowercase, no `www.`).\n   * Matching one of these only earns the URL a look at `toEmbedUrl`.\n   */\n  inputHosts: readonly string[];\n  /**\n   * Builds the URL that actually goes in the `src`, or returns `null` when the\n   * URL is on the right host but is not something this provider can frame.\n   * The origin it returns is hardcoded here, never taken from the input.\n   */\n  toEmbedUrl: (url: URL) => string | null;\n  /** Tokens for the iframe's `sandbox`. Fixed per provider; never author-set. */\n  sandbox: readonly string[];\n  /** Permissions-policy tokens for the iframe's `allow`. */\n  allow: readonly string[];\n  allowFullScreen: boolean;\n  /** The shape this provider's content wants before an author says otherwise. */\n  defaultAspectRatio: EmbedAspectRatio;\n  /** The iframe's accessible name when the author has not written one. */\n  defaultTitle: string;\n};\n\n/**\n * `allow-scripts` with `allow-same-origin` looks like it cancels the sandbox,\n * and for a same-origin frame it would. Here the framed document is\n * cross-origin, so \"same origin\" means youtube.com's own origin, not ours: the\n * frame gets its own storage and cookies and still cannot reach into the\n * embedding page. Dropping `allow-same-origin` does not harden anything — it\n * gives the frame an opaque origin, which breaks every one of these providers\n * outright.\n */\nconst FRAMED_APP_SANDBOX = ['allow-scripts', 'allow-same-origin'] as const;\n\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{11}$/;\nconst VIMEO_ID = /^[0-9]+$/;\nconst VIMEO_PRIVACY_HASH = /^[A-Za-z0-9]+$/;\nconst CALENDLY_SEGMENT = /^[A-Za-z0-9_-]+$/;\nconst GOOGLE_MAPS_PB = /^[A-Za-z0-9!*'().~_-]+$/;\n\n/**\n * Calendly paths that are the scheduling app rather than someone's booking\n * page. Framing one gets a login screen, so they fail closed here instead.\n */\nconst CALENDLY_RESERVED_SEGMENTS = new Set([\n  'app',\n  'event_types',\n  'login',\n  'signup',\n  'pages',\n  'api',\n  'help',\n  'blog',\n  'pricing',\n]);\n\n/**\n * Calendly's own appearance and prefill options, which authors legitimately\n * copy along with the booking URL. Anything else in the query is dropped\n * rather than forwarded — the point of rebuilding the URL is that only things\n * on a list get through.\n */\nconst CALENDLY_ALLOWED_PARAMS = [\n  'hide_gdpr_banner',\n  'hide_event_type_details',\n  'hide_landing_page_details',\n  'primary_color',\n  'background_color',\n  'text_color',\n  'month',\n  'date',\n] as const;\n\n/** Lowercase and drop a leading `www.`, so host matching is not a trick question. */\nexport const normaliseHost = (hostname: string): string =>\n  hostname.toLowerCase().replace(/^www\\./, '');\n\nconst pathSegments = (url: URL): string[] =>\n  url.pathname.split('/').filter((segment) => segment !== '');\n\n/**\n * Every YouTube URL an author is likely to have in the clipboard — the watch\n * page, the share link, a Short, a live stream, and an embed URL someone\n * copied out of another site's markup — reduced to the video id.\n */\nconst youTubeVideoId = (url: URL): string | null => {\n  const segments = pathSegments(url);\n  const host = normaliseHost(url.hostname);\n\n  if (host === 'youtu.be') {\n    return segments[0] ?? null;\n  }\n\n  if (segments[0] === 'watch') {\n    return url.searchParams.get('v');\n  }\n\n  if (segments.length === 2 && ['embed', 'shorts', 'live', 'v'].includes(segments[0])) {\n    return segments[1];\n  }\n\n  return null;\n};\n\nexport const EMBED_PROVIDERS: readonly EmbedProvider[] = [\n  {\n    id: 'youtube',\n    label: 'YouTube',\n    inputHosts: ['youtube.com', 'm.youtube.com', 'youtu.be', 'youtube-nocookie.com'],\n    toEmbedUrl: (url) => {\n      const videoId = youTubeVideoId(url);\n      if (!videoId || !YOUTUBE_ID.test(videoId)) {\n        return null;\n      }\n      // youtube-nocookie.com is the same player without the tracking cookies\n      // it would otherwise set on a visitor who never pressed play. Nothing\n      // about the embed needs them, and a widget that quietly adds third-party\n      // cookies to a customer's site is a consent problem we would be handing\n      // them.\n      return `https://www.youtube-nocookie.com/embed/${videoId}`;\n    },\n    sandbox: [...FRAMED_APP_SANDBOX, 'allow-presentation', 'allow-popups', 'allow-popups-to-escape-sandbox'],\n    allow: ['accelerometer', 'clipboard-write', 'encrypted-media', 'gyroscope', 'picture-in-picture'],\n    allowFullScreen: true,\n    defaultAspectRatio: '16:9',\n    defaultTitle: 'YouTube video player',\n  },\n  {\n    id: 'vimeo',\n    label: 'Vimeo',\n    inputHosts: ['vimeo.com', 'player.vimeo.com'],\n    toEmbedUrl: (url) => {\n      const segments = pathSegments(url);\n      // player.vimeo.com/video/123, vimeo.com/123, vimeo.com/channels/staffpicks/123\n      const withoutPrefix = segments[0] === 'video' ? segments.slice(1) : segments;\n      const numeric = withoutPrefix.filter((segment) => VIMEO_ID.test(segment));\n      const videoId = numeric[0];\n      if (!videoId) {\n        return null;\n      }\n\n      // An unlisted video carries a hash, either as a trailing path segment\n      // (vimeo.com/123/abc123) or as ?h=. Without it the player refuses to\n      // load, so it is the one piece of the input besides the id that is\n      // carried across.\n      const trailing = withoutPrefix[withoutPrefix.indexOf(videoId) + 1];\n      const hash = url.searchParams.get('h') ?? trailing;\n      const privacyHash = hash && VIMEO_PRIVACY_HASH.test(hash) ? hash : null;\n\n      return privacyHash\n        ? `https://player.vimeo.com/video/${videoId}?h=${privacyHash}`\n        : `https://player.vimeo.com/video/${videoId}`;\n    },\n    sandbox: [...FRAMED_APP_SANDBOX, 'allow-presentation', 'allow-popups', 'allow-popups-to-escape-sandbox'],\n    allow: ['autoplay', 'clipboard-write', 'encrypted-media', 'picture-in-picture'],\n    allowFullScreen: true,\n    defaultAspectRatio: '16:9',\n    defaultTitle: 'Vimeo video player',\n  },\n  {\n    id: 'google-maps',\n    label: 'Google Maps',\n    inputHosts: ['google.com', 'maps.google.com'],\n    toEmbedUrl: (url) => {\n      // Only the URL from \"Share → Embed a map\" is accepted, because it is the\n      // only one that can be framed at all: Google serves an ordinary maps\n      // link with X-Frame-Options and the visitor gets a blank box. Failing\n      // here means the author is told to copy the other URL, rather than\n      // shipping a page with a hole in it.\n      if (pathSegments(url).join('/') !== 'maps/embed') {\n        return null;\n      }\n      const pb = url.searchParams.get('pb');\n      // `pb` is Google's own opaque camera-and-place blob. It is carried\n      // across verbatim rather than re-encoded, because it is the one input\n      // this file cannot regenerate from an id — so instead it is held to a\n      // charset with no way out of a query string. Everything that could end\n      // one, or start an attribute, is absent from this list.\n      if (!pb || !GOOGLE_MAPS_PB.test(pb)) {\n        return null;\n      }\n      return `https://www.google.com/maps/embed?pb=${pb}`;\n    },\n    sandbox: [...FRAMED_APP_SANDBOX, 'allow-popups', 'allow-popups-to-escape-sandbox'],\n    allow: [],\n    allowFullScreen: true,\n    defaultAspectRatio: '4:3',\n    defaultTitle: 'Map',\n  },\n  {\n    id: 'calendly',\n    label: 'Calendly',\n    inputHosts: ['calendly.com'],\n    toEmbedUrl: (url) => {\n      const segments = pathSegments(url);\n      if (segments.length === 0 || segments.length > 2) {\n        return null;\n      }\n      if (!segments.every((segment) => CALENDLY_SEGMENT.test(segment))) {\n        return null;\n      }\n      if (CALENDLY_RESERVED_SEGMENTS.has(segments[0].toLowerCase())) {\n        return null;\n      }\n\n      const options = new URLSearchParams();\n      for (const parameter of CALENDLY_ALLOWED_PARAMS) {\n        const value = url.searchParams.get(parameter);\n        if (value !== null) {\n          options.set(parameter, value);\n        }\n      }\n\n      const query = options.toString();\n      const path = segments.join('/');\n      return query ? `https://calendly.com/${path}?${query}` : `https://calendly.com/${path}`;\n    },\n    sandbox: [...FRAMED_APP_SANDBOX, 'allow-forms', 'allow-popups', 'allow-popups-to-escape-sandbox'],\n    allow: [],\n    allowFullScreen: false,\n    // A booking widget is a form of unpredictable length, not a picture with a\n    // shape. It gets a height instead of a ratio; see `embed-attributes.ts`.\n    defaultAspectRatio: 'none',\n    defaultTitle: 'Booking calendar',\n  },\n];\n\nexport type ResolvedEmbedUrl = {\n  provider: EmbedProvider;\n  /** The rebuilt URL. Never the author's string. */\n  src: string;\n};\n\n/**\n * The single place an embed URL is checked against the allowlist.\n *\n * Every caller — the component, both container resolvers, and the composer's\n * URL editor through the package's public exports — comes through here rather\n * than reading `EMBED_PROVIDERS` and matching for itself. That is what keeps\n * a per-app allowlist a change to one function's argument later on, instead of\n * a hunt for call sites: see\n * `rulecms/__docs__/__TODO__/__COMPONENTS__/TODO_PLATFORM_EMBED_ALLOWLIST_PER_APP.md`.\n *\n * Returns `null` for anything it cannot vouch for, which is the fail-closed\n * behaviour the component depends on. It never throws: a malformed URL in a\n * stored widget must not take down the page it was published to.\n */\nexport function resolveEmbedUrl(\n  value: unknown,\n  providers: readonly EmbedProvider[] = EMBED_PROVIDERS\n): ResolvedEmbedUrl | null {\n  if (typeof value !== 'string' || value.trim() === '') {\n    return null;\n  }\n\n  let url: URL;\n  try {\n    url = new URL(value.trim());\n  } catch {\n    return null;\n  }\n\n  // https only. Every provider here serves it, and requiring it disposes of\n  // javascript:, data:, blob: and file: as a class rather than one at a time.\n  if (url.protocol !== 'https:') {\n    return null;\n  }\n\n  // `https://youtube.com@evil.example/` parses with `evil.example` as the host\n  // and the rest as credentials. The host check below is not fooled by it, but\n  // a URL carrying credentials at all is not one an author pasted from a\n  // provider, so it is refused outright.\n  if (url.username !== '' || url.password !== '') {\n    return null;\n  }\n\n  const host = normaliseHost(url.hostname);\n  const provider = providers.find((candidate) => candidate.inputHosts.includes(host));\n  if (!provider) {\n    return null;\n  }\n\n  const src = provider.toEmbedUrl(url);\n  return src ? { provider, src } : null;\n}\n\n/** Whether a URL would render. The composer's editor uses this for its hint. */\nexport const isAllowedEmbedUrl = (\n  value: unknown,\n  providers: readonly EmbedProvider[] = EMBED_PROVIDERS\n): boolean => resolveEmbedUrl(value, providers) !== null;\n","import type { CSSProperties } from 'react';\nimport {\n  EMBED_UNSHAPED_HEIGHT,\n  EMBED_URL_ATTRIBUTE,\n  resolveEmbedAspectRatio,\n  resolveEmbedLoading,\n  resolveEmbedTitle,\n  toCssAspectRatio,\n} from './embed-attributes';\nimport type { EmbedAspectRatio, EmbedLoading } from './embed-attributes';\nimport { resolveEmbedUrl } from './embed-providers';\nimport type { EmbedProvider } from './embed-providers';\n\n/**\n * One render's worth of embed: everything `REmbed` needs, and nothing that\n * needs deciding twice.\n */\nexport type ResolvedEmbed = {\n  provider: EmbedProvider;\n  /** The rebuilt provider URL — see `embed-providers.ts`. */\n  src: string;\n  title: string;\n  aspectRatio: EmbedAspectRatio;\n  loading: EmbedLoading;\n};\n\n/**\n * The single resolution path, shared by `REmbed` and by both container\n * resolvers, in the way `r-text` shares `resolveEnclosingTag`.\n *\n * Sharing it is not tidiness. `resolveNoContainer` and\n * `resolveRendersOwnElement` both answer questions about an element that only\n * exists when a URL passed the allowlist, so if they decided that\n * independently of the component, a rejected URL would have the renderer\n * either handing column styles to an element that was never rendered or\n * wrapping nothing in a styled div. Neither would look wrong in the drawer, in\n * Redux, or in the stored JSON.\n *\n * `null` means nothing will be rendered: no URL, or one the allowlist refused.\n */\nexport function resolveEmbed(componentAttributes?: { [key: string]: any }): ResolvedEmbed | null {\n  const resolved = resolveEmbedUrl(componentAttributes?.[EMBED_URL_ATTRIBUTE]);\n  if (!resolved) {\n    return null;\n  }\n\n  const { provider, src } = resolved;\n\n  return {\n    provider,\n    src,\n    title: resolveEmbedTitle(componentAttributes, provider.defaultTitle),\n    // An author's choice wins; otherwise the provider's own shape, which is\n    // what makes a pasted map 4:3 and a pasted video 16:9 without anyone\n    // having to know that is what they wanted.\n    aspectRatio: resolveEmbedAspectRatio(componentAttributes) ?? provider.defaultAspectRatio,\n    loading: resolveEmbedLoading(componentAttributes),\n  };\n}\n\n/**\n * The frame's own appearance, before column styles are laid over it.\n *\n * `display: block` removes the descender gap an inline replaced element leaves\n * under itself, and the zero border removes the 2px inset ridge every browser\n * still draws around an iframe by default.\n */\nexport const buildEmbedStyles = (aspectRatio: EmbedAspectRatio): CSSProperties => ({\n  display: 'block',\n  width: '100%',\n  border: 0,\n  ...(aspectRatio === 'none'\n    ? { height: EMBED_UNSHAPED_HEIGHT }\n    : { aspectRatio: toCssAspectRatio(aspectRatio) }),\n});\n","import { buildEmbedStyles, resolveEmbed } from './resolve-embed';\n\n/**\n * One `<iframe>`, or nothing at all.\n *\n * Everything an author can set arrives as text and everything this component\n * emits is chosen from a table, and the gap between those two sentences is the\n * component. `resolveEmbed` either recognises the URL as a provider on the\n * allowlist — in which case the `src` is rebuilt from that provider's own\n * hardcoded origin — or it does not, in which case this renders `null`.\n *\n * **Rejection renders nothing, on every surface.** There is no error box and\n * no fallback link, and the composer gets the same empty result the published\n * page would: an author who pastes something unsupported sees at once that it\n * did not take, rather than finding out from a customer. The Modify drawer's\n * URL editor is where they are told why, because it can say so while they are\n * still looking at the field.\n *\n * Nothing here needs the browser. The `src` is in the server's HTML, so the\n * frame starts loading from the markup rather than after hydration.\n */\nexport function REmbed({\n  componentAttributes,\n  styleProps,\n  className,\n}: Readonly<{\n  componentAttributes?: { [key: string]: any };\n  /** Column styles. Applied to the iframe, which is the only element here. */\n  styleProps?: { [key: string]: any };\n  /** Host-app classes for this column. */\n  className?: string;\n}>) {\n  const embed = resolveEmbed(componentAttributes);\n\n  if (!embed) {\n    return null;\n  }\n\n  const { provider } = embed;\n\n  return (\n    <iframe\n      src={embed.src}\n      // Never empty: an untitled frame is announced as \"frame\" and nothing\n      // else, which is worse than a generic name.\n      title={embed.title}\n      style={{ ...buildEmbedStyles(embed.aspectRatio), ...styleProps }}\n      // Fixed per provider, and listed here rather than spread from an\n      // attribute bag so that no author-settable value can widen them.\n      sandbox={provider.sandbox.join(' ')}\n      {...(provider.allow.length > 0 ? { allow: provider.allow.join('; ') } : {})}\n      {...(provider.allowFullScreen ? { allowFullScreen: true } : {})}\n      // Enough for the providers that gate embedding on the referring domain,\n      // and no path or query — which would otherwise leak the customer's URLs\n      // to a third party on every page view.\n      referrerPolicy=\"strict-origin-when-cross-origin\"\n      loading={embed.loading}\n      {...(className !== undefined ? { className } : {})}\n    />\n  );\n}\n","import type { NoContainerResolverContext } from '@source-components-react/types';\nimport { resolveEmbed } from './resolve-embed';\n\n/**\n * `false` whenever an iframe is rendered: it is a replaced element that holds\n * margin, padding, width and height on itself, so a wrapper div would only add\n * an element that does nothing.\n *\n * `true` when the URL is missing or the allowlist refused it, because then\n * there is no element at all — the same answer `r-text` gives for bare text.\n * Deciding this from `resolveEmbed`, rather than from the fact that this\n * component \"is an iframe component\", is what keeps the two in step.\n */\nexport const resolveNoContainer = ({ componentAttributes }: NoContainerResolverContext): boolean =>\n  resolveEmbed(componentAttributes) === null;\n","import { REmbed } from './REmbed';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type { DisplayCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.rEmbed,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({\n    componentAttributes,\n    styleProps,\n    className,\n  }: {\n    componentAttributes?: { [key: string]: any };\n    styleProps?: { [key: string]: any };\n    className?: string;\n  }) => (\n    <REmbed\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n    />\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","import type { NoContainerResolverContext } from '@source-components-react/types';\nimport { resolveEmbed } from './resolve-embed';\n\n/**\n * The iframe carries the className — when there is one. A rejected or unset\n * URL renders nothing, and a class handed to a component that renders nothing\n * disappears without trace, so this answers from the same resolution the\n * component uses.\n */\nexport const resolveRendersOwnElement = ({\n  componentAttributes,\n}: NoContainerResolverContext): boolean => resolveEmbed(componentAttributes) !== null;\n","import { DefaultAttributeValue } from '@source-components-react/types';\n\n/**\n * Nothing is seeded, including the URL.\n *\n * A freshly dropped embed renders nothing until an author pastes something,\n * which is the honest result: the alternative is shipping a placeholder video\n * that a customer's page would publish if anyone forgot to replace it. The\n * palette card and the empty drawer say what to do instead.\n *\n * Aspect ratio is left unset on purpose too, so it resolves from the provider\n * — a map is 4:3 and a video is 16:9 without either being written down here.\n */\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    name: 'embed-url-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'embed-title-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'embed-aspect-ratio-3-resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'embed-loading-3-resolutions',\n    defaultValues: {},\n  },\n];\n","import { AttributeEditModeConfig } from '@source-components-react/types';\n\n/**\n * Drawer order, which is also the order the documentation pages are listed in:\n * the URL first because nothing renders without it, then the three settings\n * that only matter once something does.\n */\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: 'embed-url-3-resolutions',\n    heading: 'Embed URL',\n    subHeading:\n      'Paste the address of a YouTube or Vimeo video, a Google Maps \"Embed a map\" link, or a Calendly booking page, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'embed-title-3-resolutions',\n    heading: 'Embed Title',\n    subHeading:\n      'Describe what is being embedded, for screen readers; defaults to the provider name, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'embed-aspect-ratio-3-resolutions',\n    heading: 'Embed Aspect Ratio',\n    subHeading:\n      'The shape of the embed — \"16:9\" for video, \"4:3\" for maps, or \"none\" to set the height on the column instead, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: 'embed-loading-3-resolutions',\n    heading: 'Embed Loading',\n    subHeading:\n      'Whether the embed loads with the page (\"eager\") or waits until the visitor scrolls near it (\"lazy\", the default), for 3 different device resolutions',\n    defaultValues: {},\n  },\n];\n","/**\n * The attribute names `r-list` reads.\n *\n * Every one is list-specific, including the links, which are conceptually the\n * same numbered list `r-text` already offers. Sharing that attribute name\n * would have saved one line of editor wiring and cost the documentation\n * contract: RuleCMS publishes one page per component-scope attribute and\n * requires exactly one owning component, so a shared name has no single page\n * to link to from the drawer. Reuse happens a level down instead — the\n * composer resolves an attribute to an *editor* by name, and `list-links`\n * points at the editor `links-3-resolutions` already resolves to.\n *\n * Nothing here duplicates a column style attribute. Spacing, colour and\n * typography come from Column Styles on the root element and inherit into\n * the items; the attributes below are only what is specific to a list —\n * which element it is, what the markers look like, and where numbering\n * starts. Kind and marker are closed sets rather than free text, which is\n * what lets them go through the composer's shared select and lets the render\n * path treat anything unrecognised as unset.\n */\nexport const LIST_ITEMS_ATTRIBUTE = 'list-items-3-resolutions';\nexport const LIST_KIND_ATTRIBUTE = 'list-kind-3-resolutions';\nexport const LIST_MARKER_ATTRIBUTE = 'list-marker-3-resolutions';\nexport const LIST_START_ATTRIBUTE = 'list-start-3-resolutions';\nexport const LIST_REVERSED_ATTRIBUTE = 'list-reversed-3-resolutions';\nexport const LIST_LINKS_ATTRIBUTE = 'list-links-3-resolutions';\nexport const LIST_INLINE_STYLES_ATTRIBUTE = 'list-inline-styles-3-resolutions';\n\n/** What a freshly dropped list shows before the author types anything. */\nexport const DEFAULT_LIST_ITEMS = 'First item\\nSecond item\\nThird item';\n\nexport const LIST_KINDS = ['unordered', 'ordered'] as const;\nexport type ListKind = (typeof LIST_KINDS)[number];\nexport const DEFAULT_LIST_KIND: ListKind = 'unordered';\n\n/**\n * `list-style-type` values worth offering. Every entry is valid CSS on both\n * `<ul>` and `<ol>`, so the marker survives the author switching kind rather\n * than resetting. Unset means the browser default — disc for unordered,\n * decimal for ordered — which is why there is no default constant here: not\n * setting the property at all is the default.\n */\nexport const LIST_MARKERS = [\n  'disc',\n  'circle',\n  'square',\n  'decimal',\n  'lower-alpha',\n  'upper-alpha',\n  'lower-roman',\n  'upper-roman',\n  'none',\n] as const;\nexport type ListMarker = (typeof LIST_MARKERS)[number];\n\ntype Attributes = { [key: string]: any } | undefined;\n\nexport const resolveListKind = (componentAttributes: Attributes): ListKind => {\n  const value = componentAttributes?.[LIST_KIND_ATTRIBUTE];\n  return LIST_KINDS.includes(value) ? value : DEFAULT_LIST_KIND;\n};\n\n/**\n * A stored value reaches a CSS property here, so the allowlist is the\n * boundary that keeps it from being whatever a request happened to write.\n * Unrecognised values mean \"unset\" and the browser default applies.\n */\nexport const resolveListMarker = (componentAttributes: Attributes): ListMarker | undefined => {\n  const value = componentAttributes?.[LIST_MARKER_ATTRIBUTE];\n  return LIST_MARKERS.includes(value) ? value : undefined;\n};\n\n/**\n * One item per line of the authored text, blank lines dropped. An unset\n * attribute falls back to the placeholder items, but text the author\n * deliberately cleared stays cleared — an empty list element still carries\n * the column's styles and stays selectable in the composer.\n */\nexport const resolveListItems = (componentAttributes: Attributes): string[] => {\n  const value = componentAttributes?.[LIST_ITEMS_ATTRIBUTE];\n  const text =\n    value === undefined || value === null\n      ? DEFAULT_LIST_ITEMS\n      : typeof value === 'string'\n        ? value\n        : String(value);\n  return text\n    .split(/\\r?\\n/)\n    .map((line) => line.trim())\n    .filter((line) => line !== '');\n};\n\n/**\n * HTML's `start` takes any integer, including negative ones. The editor\n * stores a number, but a hand-written or migrated config may carry a numeric\n * string; both mean the same thing here. Anything else is unset, and only an\n * ordered list ever reads it.\n */\nexport const resolveListStart = (componentAttributes: Attributes): number | undefined => {\n  const value = componentAttributes?.[LIST_START_ATTRIBUTE];\n  if (typeof value === 'number' && Number.isInteger(value)) {\n    return value;\n  }\n  if (typeof value === 'string' && value.trim() !== '') {\n    const parsed = Number(value.trim());\n    if (Number.isInteger(parsed)) {\n      return parsed;\n    }\n  }\n  return undefined;\n};\n\n/**\n * Select editors in the composer store `'true'` / `'false'` as strings, while\n * a hand-written or migrated config may carry a real boolean. Both mean the\n * same thing here. Only an ordered list ever reads it.\n */\nexport const resolveListReversed = (componentAttributes: Attributes): boolean => {\n  const value = componentAttributes?.[LIST_REVERSED_ATTRIBUTE];\n  if (typeof value === 'boolean') {\n    return value;\n  }\n  return typeof value === 'string' && value.trim().toLowerCase() === 'true';\n};\n","import type { CSSProperties } from 'react';\nimport { renderInlineMarkup } from '@source-components-react/shared/inline-markup';\nimport {\n  LIST_INLINE_STYLES_ATTRIBUTE,\n  LIST_LINKS_ATTRIBUTE,\n  resolveListItems,\n  resolveListKind,\n  resolveListMarker,\n  resolveListReversed,\n  resolveListStart,\n} from './list-attributes';\n\n/**\n * One `<ul>` or `<ol>` with an `<li>` per authored line — and never a wrapper\n * around it.\n *\n * The element is the whole accessibility story. A real list element carries\n * the implicit `list` role, announces \"list of N items\" in a screen reader\n * and reads as structure to a search engine; styled divs would look the same\n * and say nothing. Flat lists only — nesting multiplies both the content\n * model and the editor for a structure most content does not need.\n *\n * Appearance is the component's marker choice with column styles on top: the\n * marker is spread first so anything the author set in Column Styles lands\n * later in the object and wins. Everything else — spacing, colour,\n * typography — is column styles on the root, inherited by the items, which\n * is why this component declares no other style attributes of its own.\n *\n * `{n|linked text}` markers inside any item resolve against one shared link\n * list, numbered by entry position exactly as in `r-text`. Anchors land\n * inside the `<li>`, never around it, and the same items accept the mark and\n * line-break markers `r-text` does.\n *\n * Nothing here needs the browser. The element, its items and its numbering\n * are all in the server's HTML, with no resolution after hydration.\n */\nexport function RList({\n  componentAttributes,\n  styleProps,\n  className,\n}: Readonly<{\n  componentAttributes?: { [key: string]: any };\n  /** Column styles from the widget pipeline. Always applied to the list element itself. */\n  styleProps?: { [key: string]: any };\n  /** Host-app classes for this column. The single root element carries them. */\n  className?: string;\n}>) {\n  const marker = resolveListMarker(componentAttributes);\n  const links = componentAttributes?.[LIST_LINKS_ATTRIBUTE];\n  const inlineStyles = componentAttributes?.[LIST_INLINE_STYLES_ATTRIBUTE];\n\n  const style: CSSProperties = {\n    ...(marker !== undefined ? { listStyleType: marker } : {}),\n    ...styleProps,\n  };\n\n  const items = resolveListItems(componentAttributes).map((item, position) => (\n    <li key={position}>{renderInlineMarkup(item, links, inlineStyles)}</li>\n  ));\n\n  const sharedProps = {\n    style,\n    ...(className !== undefined ? { className } : {}),\n  };\n\n  if (resolveListKind(componentAttributes) === 'ordered') {\n    const start = resolveListStart(componentAttributes);\n    const reversed = resolveListReversed(componentAttributes);\n    return (\n      <ol\n        {...sharedProps}\n        {...(start !== undefined ? { start } : {})}\n        {...(reversed ? { reversed: true } : {})}\n      >\n        {items}\n      </ol>\n    );\n  }\n\n  return <ul {...sharedProps}>{items}</ul>;\n}\n","/**\n * RList always renders a `<ul>` or `<ol>` — both block-level elements that\n * hold margin, padding, width and border as authored — and puts styleProps on\n * that element itself. No configuration changes the answer, so no wrapper\n * div, ever.\n */\nexport const resolveNoContainer = (): boolean => false;\n","import { RList } from './RList';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type { DisplayCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.rList,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({ componentAttributes, styleProps, className }) => (\n    <RList\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n    />\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","/**\n * RList's root is always a real element — `<ul>` or `<ol>` — so it can always\n * carry the column's className, and the component applies the one it\n * receives.\n */\nexport const resolveRendersOwnElement = (): boolean => true;\n","import { DefaultAttributeValue } from '@source-components-react/types';\nimport {\n  DEFAULT_LIST_ITEMS,\n  DEFAULT_LIST_KIND,\n  LIST_INLINE_STYLES_ATTRIBUTE,\n  LIST_ITEMS_ATTRIBUTE,\n  LIST_KIND_ATTRIBUTE,\n  LIST_LINKS_ATTRIBUTE,\n  LIST_MARKER_ATTRIBUTE,\n  LIST_REVERSED_ATTRIBUTE,\n  LIST_START_ATTRIBUTE,\n} from './list-attributes';\n\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    name: LIST_ITEMS_ATTRIBUTE,\n    defaultValues: {\n      all: DEFAULT_LIST_ITEMS,\n    },\n  },\n  {\n    name: LIST_KIND_ATTRIBUTE,\n    defaultValues: {\n      all: DEFAULT_LIST_KIND,\n    },\n  },\n  // Marker, start and reversed default to unset on purpose: the browser\n  // already renders disc for unordered and 1, 2, 3 for ordered, and an\n  // attribute that is not set is one the render path does not emit.\n  {\n    name: LIST_MARKER_ATTRIBUTE,\n    defaultValues: {},\n  },\n  {\n    name: LIST_START_ATTRIBUTE,\n    defaultValues: {},\n  },\n  {\n    name: LIST_REVERSED_ATTRIBUTE,\n    defaultValues: {},\n  },\n  {\n    name: LIST_LINKS_ATTRIBUTE,\n    defaultValues: {},\n  },\n  {\n    name: LIST_INLINE_STYLES_ATTRIBUTE,\n    defaultValues: {},\n  },\n];\n","import type { AttributeEditModeConfig } from '@source-components-react/types';\nimport {\n  DEFAULT_LIST_KIND,\n  LIST_INLINE_STYLES_ATTRIBUTE,\n  LIST_ITEMS_ATTRIBUTE,\n  LIST_KIND_ATTRIBUTE,\n  LIST_LINKS_ATTRIBUTE,\n  LIST_MARKER_ATTRIBUTE,\n  LIST_REVERSED_ATTRIBUTE,\n  LIST_START_ATTRIBUTE,\n} from './list-attributes';\n\n/**\n * Every setting the list offers, in the order the Modify drawer shows them.\n *\n * Order is not cosmetic. The drawer renders these top to bottom and the\n * documentation registry asserts its pages match that sequence, so moving an\n * entry here moves it in both places at once.\n */\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: LIST_ITEMS_ATTRIBUTE,\n    heading: 'List Items',\n    subHeading:\n      'The items, one per line — each line becomes its own entry in the list. Blank lines are skipped, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: LIST_KIND_ATTRIBUTE,\n    heading: 'List Kind',\n    subHeading:\n      'Bulleted (unordered) or numbered (ordered). Numbered lists can also set where counting starts and whether it counts down, for 3 different device resolutions',\n    defaultValues: { all: DEFAULT_LIST_KIND },\n  },\n  {\n    name: LIST_MARKER_ATTRIBUTE,\n    heading: 'Marker Style',\n    subHeading:\n      'What sits in front of each item: a disc, circle or square for bullets; numbers, letters or Roman numerals for numbering; or none to hide the markers. Unset uses the browser default for the list kind, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: LIST_START_ATTRIBUTE,\n    heading: 'Numbering Start',\n    subHeading:\n      'The number the first item counts from. Only a numbered list reads this; leave unset to count from 1, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: LIST_REVERSED_ATTRIBUTE,\n    heading: 'Reversed Numbering',\n    subHeading:\n      'Whether a numbered list counts down instead of up, e.g. a top-10 countdown. Only a numbered list reads this, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: LIST_LINKS_ATTRIBUTE,\n    heading: 'Links',\n    subHeading:\n      'Links the items can point at. To link part of an item, wrap those words in {1|linked text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: LIST_INLINE_STYLES_ATTRIBUTE,\n    heading: 'Class Names & Styles',\n    subHeading:\n      'Class names and CSS the items can apply to part of themselves, e.g. to give a few words their own colour. To apply an entry, wrap those words in {.1|styled text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',\n    defaultValues: {},\n  },\n];\n","import { parseInlineLinkMarkers } from '@source-components-react/shared/links';\n\n/**\n * The attribute names `r-accordion` reads.\n *\n * The component is a disclosure and nothing more specific than that: a summary\n * a reader clicks and the detail it expands to. An FAQ is one thing to build\n * out of it, a shipping policy or a \"show more\" under a truncated bio are\n * others, and the settings are named after the two halves of the element —\n * `<summary>` and the detail inside `<details>` — rather than after any one of\n * those uses.\n *\n * One accordion is one disclosure, rather than a component holding N\n * collapsible sections. A repeating-group attribute would need an editor this\n * library does not have, and several sections in one component leave the\n * nested-content slot with no single detail to belong to (see\n * `rulecms/__docs__/__ARCHITECTURE__/ACCORDION_COMPONENT_ARCHITECTURE.md`).\n * Several of these in a column is the multi-section case, which is also how an\n * author reorders one.\n *\n * Every name is accordion-specific, including the links, which are the same\n * numbered list `r-text` already offers. Sharing that attribute name would\n * have saved a line of editor wiring and cost the documentation contract:\n * RuleCMS publishes one page per component-scope attribute and requires\n * exactly one owning component. Reuse happens a level down, where the composer\n * resolves an attribute to an *editor* by name — the same arrangement `r-list`\n * uses.\n *\n * Nothing here duplicates a column style attribute. Spacing, colour and\n * typography come from Column Styles on the `<details>` element and inherit\n * into the summary and the detail; the attributes below are only what is\n * specific to a disclosure — what it says closed, what it says open, whether\n * it starts open, and whether the summary also belongs in the page outline.\n */\nexport const ACCORDION_SUMMARY_ATTRIBUTE = 'accordion-summary-3-resolutions';\nexport const ACCORDION_DETAIL_ATTRIBUTE = 'accordion-detail-3-resolutions';\nexport const ACCORDION_LINKS_ATTRIBUTE = 'accordion-links-3-resolutions';\nexport const ACCORDION_INLINE_STYLES_ATTRIBUTE = 'accordion-inline-styles-3-resolutions';\nexport const ACCORDION_OPEN_ATTRIBUTE = 'accordion-open-3-resolutions';\nexport const ACCORDION_HEADING_LEVEL_ATTRIBUTE = 'accordion-heading-level-3-resolutions';\n\n/** What a freshly dropped accordion shows before the author types anything. */\nexport const DEFAULT_ACCORDION_SUMMARY = 'Summary';\nexport const DEFAULT_ACCORDION_DETAIL =\n  'The detail this summary expands to. Each line becomes its own paragraph.';\n\n/**\n * Heading levels the summary can be given, plus `none` for the common case.\n *\n * `h1` is deliberately absent: the page already has one, and ten disclosures\n * down the page would otherwise offer ten ways to compete with it. Levels\n * start at `h2` because a summary sits under whatever heading introduces the\n * section it is in.\n *\n * `none` is a real option rather than an unset value so the picker always has\n * a definite selection — there is no way to clear a select back to nothing —\n * and it is the default because a disclosure is not automatically part of the\n * document outline.\n */\nexport const ACCORDION_HEADING_LEVELS = ['none', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;\nexport type AccordionHeadingLevel = (typeof ACCORDION_HEADING_LEVELS)[number];\nexport const DEFAULT_ACCORDION_HEADING_LEVEL: AccordionHeadingLevel = 'none';\n\ntype Attributes = { [key: string]: any } | undefined;\n\n/**\n * The summary, on one line. An unset attribute falls back to the placeholder,\n * but text the author deliberately cleared stays cleared: an empty `<summary>`\n * still draws its chevron and still toggles, and the composer needs the element\n * to stay selectable on the canvas. Same distinction `r-list` draws for its\n * items and `r-button` for its label.\n *\n * Authored newlines collapse to single spaces. The editor behind this\n * attribute is the shared multi-line text field, and `<summary>` is a single\n * line of prose — HTML would collapse the whitespace anyway, so the resolved\n * value says so rather than leaving it to the browser.\n *\n * `{n|…}` markers contribute their inner text and nothing else. The summary\n * takes no links — inside a `<summary>` a click both follows the anchor and\n * toggles the disclosure — but the editor behind it is the same field that\n * offers the marker grammar for the detail, so an author will type one here.\n * Degrading it to prose is what the rest of the library does with a marker it\n * cannot resolve; leaving the braces on screen is the one outcome nobody wants.\n */\nexport const resolveAccordionSummary = (componentAttributes: Attributes): string => {\n  const value = componentAttributes?.[ACCORDION_SUMMARY_ATTRIBUTE];\n  const text =\n    value === undefined || value === null\n      ? DEFAULT_ACCORDION_SUMMARY\n      : typeof value === 'string'\n        ? value\n        : String(value);\n  return parseInlineLinkMarkers(text)\n    .map((run) => run.text)\n    .join('')\n    .replace(/\\s+/g, ' ')\n    .trim();\n};\n\n/**\n * The detail, as one paragraph per authored line with blank lines dropped —\n * the same grammar `r-list` uses for its items, so an author who has met one\n * has met both. An unset attribute falls back to the placeholder; text the\n * author deliberately cleared stays cleared and the disclosure opens onto\n * whatever else is in it, or onto nothing.\n */\nexport const resolveAccordionDetail = (componentAttributes: Attributes): string[] => {\n  const value = componentAttributes?.[ACCORDION_DETAIL_ATTRIBUTE];\n  const text =\n    value === undefined || value === null\n      ? DEFAULT_ACCORDION_DETAIL\n      : typeof value === 'string'\n        ? value\n        : String(value);\n  return text\n    .split(/\\r?\\n/)\n    .map((line) => line.trim())\n    .filter((line) => line !== '');\n};\n\n/**\n * Whether the disclosure renders already open. Select editors in the composer\n * store `'true'` / `'false'` as strings, while a hand-written or migrated\n * config may carry a real boolean; both mean the same thing here.\n *\n * This is an attribute rather than runtime state on purpose: `open` has to be\n * right in the server's HTML, because a disclosure a search engine or a\n * no-JavaScript reader should see expanded cannot wait for hydration.\n */\nexport const resolveAccordionOpen = (componentAttributes: Attributes): boolean => {\n  const value = componentAttributes?.[ACCORDION_OPEN_ATTRIBUTE];\n  if (typeof value === 'boolean') {\n    return value;\n  }\n  return typeof value === 'string' && value.trim().toLowerCase() === 'true';\n};\n\n/**\n * The heading tag to wrap the summary in, or `undefined` for no heading.\n *\n * A stored value reaches an element name here, so the allowlist is the\n * boundary that keeps it from being whatever a request happened to write.\n * `none` and anything unrecognised both mean \"no heading\", which is also the\n * safe answer: a plain `<summary>` is a valid disclosure, while an invented\n * tag inside one is not.\n */\nexport const resolveAccordionHeadingLevel = (\n  componentAttributes: Attributes\n): Exclude<AccordionHeadingLevel, 'none'> | undefined => {\n  const value = componentAttributes?.[ACCORDION_HEADING_LEVEL_ATTRIBUTE];\n  if (value === 'none' || !(ACCORDION_HEADING_LEVELS as readonly unknown[]).includes(value)) {\n    return undefined;\n  }\n  return value;\n};\n","import { createElement } from 'react';\nimport type { CSSProperties, ReactNode } from 'react';\nimport { renderInlineMarkup } from '@source-components-react/shared/inline-markup';\nimport {\n  ACCORDION_INLINE_STYLES_ATTRIBUTE,\n  ACCORDION_LINKS_ATTRIBUTE,\n  resolveAccordionDetail,\n  resolveAccordionHeadingLevel,\n  resolveAccordionOpen,\n  resolveAccordionSummary,\n} from './accordion-attributes';\n\n/**\n * One `<details>` / `<summary>` disclosure — a summary that expands to its\n * detail — and never a wrapper around it.\n *\n * The element is the whole interaction. Native disclosure gives expand and\n * collapse with no JavaScript at all: it is keyboard operable, it announces\n * its expanded state to a screen reader, the collapsed detail is still in the\n * server's HTML for a search engine to read, and a browser with scripts\n * blocked behaves identically. A div with a click handler would look the same\n * and have none of that, and it would need client state this library does not\n * have — every component here is a pure function.\n *\n * `open` is therefore an attribute rather than state. A disclosure the author\n * wants expanded has to be expanded in the first paint, before hydration,\n * which a `useState` initial value cannot promise.\n *\n * The detail is one paragraph per authored line, the grammar `r-list` uses for\n * its items, with `{n|linked text}` markers resolved against one shared link\n * list. Anchors land inside the paragraph, never around it. The summary takes\n * no links: inside a `<summary>` a click both follows the anchor and toggles\n * the disclosure, so an author cannot get either one reliably.\n *\n * `children` is the nested-content slot: whatever the render path passes lands\n * inside the disclosure, after the authored detail. Composing a subtree into it\n * is platform work that has not shipped yet — see\n * `rulecms/__docs__/__ARCHITECTURE__/ACCORDION_COMPONENT_ARCHITECTURE.md` — so\n * today it is only ever the authored text.\n *\n * Appearance is column styles plus one library stylesheet. Column styles go on\n * the `<details>` element and inherit into the summary and the detail. The\n * sheet hides the UA triangle, puts a chevron on the right, and animates the\n * panel and the chevron on `[open]`. The summary is a full-width flex row so\n * the chevron stays on the right in both states — the same class of hardcoded\n * layout as an embed's aspect ratio.\n */\nconst SUMMARY_LAYOUT_STYLE: CSSProperties = {\n  display: 'flex',\n  alignItems: 'center',\n  width: '100%',\n  boxSizing: 'border-box',\n  cursor: 'pointer',\n  listStyle: 'none',\n};\n\nconst SUMMARY_HEADING_STYLE: CSSProperties = {\n  // A heading is `display: block` with its own margins. As a flex item it\n  // already shares the summary row with the chevron; zeroing the UA margin\n  // keeps the words on that line. `flex: 1` lets a long heading wrap instead\n  // of shoving the chevron off the row.\n  flex: 1,\n  minWidth: 0,\n  margin: 0,\n};\n\nconst CHEVRON_STYLE: CSSProperties = {\n  width: 20,\n  height: 20,\n  flexShrink: 0,\n  marginLeft: 'auto',\n};\n\n/**\n * Same geometry and stroke as Lucide `chevron-down` (24×24, stroke 2), sized\n * to 20px so it matches the ATL FAQ item. Rotation lives on the stylesheet.\n */\nfunction AccordionChevron() {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width={24}\n      height={24}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      style={CHEVRON_STYLE}\n      data-rulecms-accordion-chevron=\"\"\n      aria-hidden=\"true\"\n    >\n      <path d=\"m6 9 6 6 6-6\" />\n    </svg>\n  );\n}\n\nexport function RAccordion({\n  componentAttributes,\n  styleProps,\n  className,\n  children,\n}: Readonly<{\n  componentAttributes?: { [key: string]: any };\n  /** Column styles from the widget pipeline. Always applied to the `<details>` element itself. */\n  styleProps?: { [key: string]: any };\n  /** Host-app classes for this column. The single root element carries them. */\n  className?: string;\n  /**\n   * Content to render inside the disclosure, after the authored detail. The\n   * slot a Composer-authored subtree will occupy; absent on every render path\n   * that exists today.\n   */\n  children?: ReactNode;\n}>) {\n  const style: CSSProperties | undefined = styleProps;\n  const summary = resolveAccordionSummary(componentAttributes);\n  const headingLevel = resolveAccordionHeadingLevel(componentAttributes);\n  const links = componentAttributes?.[ACCORDION_LINKS_ATTRIBUTE];\n  const inlineStyles = componentAttributes?.[ACCORDION_INLINE_STYLES_ATTRIBUTE];\n\n  return (\n    <details\n      data-rulecms-accordion=\"\"\n      style={style}\n      {...(className !== undefined ? { className } : {})}\n      {...(resolveAccordionOpen(componentAttributes) ? { open: true } : {})}\n    >\n      <summary style={SUMMARY_LAYOUT_STYLE}>\n        {headingLevel ? createElement(headingLevel, { style: SUMMARY_HEADING_STYLE }, summary) : summary}\n        <AccordionChevron />\n      </summary>\n      {resolveAccordionDetail(componentAttributes).map((paragraph, position) => (\n        <p key={position}>{renderInlineMarkup(paragraph, links, inlineStyles)}</p>\n      ))}\n      {children}\n    </details>\n  );\n}\n","/**\n * RAccordion always renders a `<details>` — a block-level element that holds\n * margin, padding, width and border as authored — and puts styleProps on that\n * element itself. No configuration changes the answer, so no wrapper div,\n * ever.\n */\nexport const resolveNoContainer = (): boolean => false;\n","import { RAccordion } from './RAccordion';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type { DisplayCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.rAccordion,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({ componentAttributes, styleProps, className, children }) => (\n    <RAccordion\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n    >\n      {children}\n    </RAccordion>\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","/**\n * RAccordion's root is always a real element — `<details>` — so it can always\n * carry the column's className, and the component applies the one it receives.\n */\nexport const resolveRendersOwnElement = (): boolean => true;\n","import { DefaultAttributeValue } from '@source-components-react/types';\nimport {\n  ACCORDION_DETAIL_ATTRIBUTE,\n  ACCORDION_HEADING_LEVEL_ATTRIBUTE,\n  ACCORDION_INLINE_STYLES_ATTRIBUTE,\n  ACCORDION_LINKS_ATTRIBUTE,\n  ACCORDION_OPEN_ATTRIBUTE,\n  ACCORDION_SUMMARY_ATTRIBUTE,\n  DEFAULT_ACCORDION_DETAIL,\n  DEFAULT_ACCORDION_HEADING_LEVEL,\n  DEFAULT_ACCORDION_SUMMARY,\n} from './accordion-attributes';\n\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    name: ACCORDION_SUMMARY_ATTRIBUTE,\n    defaultValues: {\n      all: DEFAULT_ACCORDION_SUMMARY,\n    },\n  },\n  {\n    name: ACCORDION_DETAIL_ATTRIBUTE,\n    defaultValues: {\n      all: DEFAULT_ACCORDION_DETAIL,\n    },\n  },\n  // Closed by default, and unset rather than 'false': a disclosure the author\n  // has not thought about should start collapsed, which is what an absent\n  // `open` attribute already means in HTML.\n  {\n    name: ACCORDION_OPEN_ATTRIBUTE,\n    defaultValues: {},\n  },\n  {\n    name: ACCORDION_HEADING_LEVEL_ATTRIBUTE,\n    defaultValues: {\n      all: DEFAULT_ACCORDION_HEADING_LEVEL,\n    },\n  },\n  {\n    name: ACCORDION_LINKS_ATTRIBUTE,\n    defaultValues: {},\n  },\n  {\n    name: ACCORDION_INLINE_STYLES_ATTRIBUTE,\n    defaultValues: {},\n  },\n];\n","import type { AttributeEditModeConfig } from '@source-components-react/types';\nimport {\n  ACCORDION_DETAIL_ATTRIBUTE,\n  ACCORDION_HEADING_LEVEL_ATTRIBUTE,\n  ACCORDION_INLINE_STYLES_ATTRIBUTE,\n  ACCORDION_LINKS_ATTRIBUTE,\n  ACCORDION_OPEN_ATTRIBUTE,\n  ACCORDION_SUMMARY_ATTRIBUTE,\n  DEFAULT_ACCORDION_HEADING_LEVEL,\n} from './accordion-attributes';\n\n/**\n * Every setting the accordion offers, in the order the Modify drawer shows\n * them.\n *\n * Headings name the two halves of the element — Summary and Detail — not one\n * use of it. An author building an FAQ reads \"Summary\" as the question without\n * being told to; an author building a \"show more\" would have had to translate\n * \"Question\" into something it is not.\n *\n * Order is not cosmetic. The drawer renders these top to bottom and the\n * documentation registry asserts its pages match that sequence, so moving an\n * entry here moves it in both places at once.\n */\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: ACCORDION_SUMMARY_ATTRIBUTE,\n    heading: 'Summary',\n    subHeading:\n      'The line a reader sees closed and clicks to expand. One line — extra lines collapse into it, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: ACCORDION_DETAIL_ATTRIBUTE,\n    heading: 'Detail',\n    subHeading:\n      'What the summary expands to, one paragraph per line. Blank lines are skipped, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: ACCORDION_OPEN_ATTRIBUTE,\n    heading: 'Open by Default',\n    subHeading:\n      'Whether the detail is already expanded when the page loads. A reader can still collapse it, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: ACCORDION_HEADING_LEVEL_ATTRIBUTE,\n    heading: 'Summary Heading Level',\n    subHeading:\n      'Whether the summary also counts as a heading in the page outline, and at which level. Leave it at none unless these summaries are the structure of the page, for 3 different device resolutions',\n    defaultValues: { all: DEFAULT_ACCORDION_HEADING_LEVEL },\n  },\n  {\n    name: ACCORDION_LINKS_ATTRIBUTE,\n    heading: 'Links',\n    subHeading:\n      'Links the detail can point at. To link part of the detail, wrap those words in {1|linked text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: ACCORDION_INLINE_STYLES_ATTRIBUTE,\n    heading: 'Class Names & Styles',\n    subHeading:\n      'Class names and CSS the detail can apply to part of itself, e.g. to give a few words their own colour. To apply an entry, wrap those words in {.1|styled text}, where the number is the position of the entry in this list counting from 1; clearing an entry leaves its position in place so the other numbers keep pointing where they did, for 3 different device resolutions',\n    defaultValues: {},\n  },\n];\n","import type { ComponentType } from 'react';\n\n/**\n * The attribute names `r-mount` reads.\n *\n * Two, and that is the whole of it: which host component to render, and the\n * props the author filled in for it. The host application supplies the actual\n * React component through `RuleCMSWidget`'s `mounts` map. This library never\n * imports customer code.\n *\n * There is no hide attribute. Hide lives on the column, like every other\n * component. There is no children slot — authoring a subtree into a card is\n * unshipped platform work, and a mount is a leaf that *is* the component.\n */\n\nexport const MOUNT_NAME_ATTRIBUTE = 'mount-name-3-resolutions';\nexport const MOUNT_PROPS_ATTRIBUTE = 'mount-props-3-resolutions';\n\nexport const MOUNT_PROP_TYPES = ['string', 'number', 'boolean', 'json'] as const;\nexport type MountPropType = (typeof MOUNT_PROP_TYPES)[number];\n\nexport type MountPropRow = {\n  key: string;\n  type: MountPropType;\n  value: string;\n};\n\nexport type MountsMap = {\n  [name: string]: ComponentType<any>;\n};\n\n/**\n * A name the host can register. Starts with a letter so it cannot collide\n * with a number-looking key or a CSS custom property, then letters, digits,\n * hyphens and underscores only. The same grammar the composer field accepts.\n */\nexport const MOUNT_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;\n\nconst FORBIDDEN_PROP_KEYS = new Set([\n  '__proto__',\n  'constructor',\n  'prototype',\n  'key',\n  'ref',\n  'children',\n  'dangerouslySetInnerHTML',\n]);\n\nconst HTML_EVENT_KEYS = new Set([\n  'onclick',\n  'onsubmit',\n  'onload',\n  'onerror',\n  'onmouseover',\n  'onmouseenter',\n  'onfocus',\n  'onblur',\n  'onchange',\n  'oninput',\n  'onkeydown',\n  'onkeyup',\n]);\n\nexport const isMountPropType = (value: unknown): value is MountPropType =>\n  typeof value === 'string' && (MOUNT_PROP_TYPES as readonly string[]).includes(value);\n\nexport const isAllowedMountName = (value: unknown): value is string =>\n  typeof value === 'string' && MOUNT_NAME_PATTERN.test(value);\n\n/**\n * Keys a marketer-authored row must never become. `on*` would turn a typed\n * string into an event handler; `dangerouslySetInnerHTML` would inject HTML;\n * the rest are reserved by React or by the prototype chain.\n */\nexport const isForbiddenMountPropKey = (key: string): boolean => {\n  if (FORBIDDEN_PROP_KEYS.has(key) || HTML_EVENT_KEYS.has(key.toLowerCase())) {\n    return true;\n  }\n  return /^on[A-Z]/.test(key);\n};\n\nexport const resolveMountName = (\n  componentAttributes?: { [key: string]: unknown }\n): string => {\n  const value = componentAttributes?.[MOUNT_NAME_ATTRIBUTE];\n  return isAllowedMountName(value) ? value : '';\n};\n\nconst isMountPropRow = (value: unknown): value is MountPropRow => {\n  if (!value || typeof value !== 'object' || Array.isArray(value)) {\n    return false;\n  }\n  const row = value as { key?: unknown; type?: unknown; value?: unknown };\n  return (\n    typeof row.key === 'string' &&\n    isMountPropType(row.type) &&\n    typeof row.value === 'string'\n  );\n};\n\nconst coerceMountPropValue = (row: MountPropRow): unknown => {\n  switch (row.type) {\n    case 'string':\n      return row.value;\n    case 'number': {\n      const parsed = Number(row.value);\n      return Number.isFinite(parsed) ? parsed : undefined;\n    }\n    case 'boolean':\n      if (row.value === 'true') {\n        return true;\n      }\n      if (row.value === 'false') {\n        return false;\n      }\n      return undefined;\n    case 'json':\n      try {\n        return JSON.parse(row.value);\n      } catch {\n        return undefined;\n      }\n    default:\n      return undefined;\n  }\n};\n\n/**\n * Turns the authored rows into a props object for the host component.\n * Unknown shapes, forbidden keys, and values that fail their declared type\n * are dropped rather than guessed.\n */\nexport const resolveMountProps = (\n  componentAttributes?: { [key: string]: unknown }\n): { [key: string]: unknown } => {\n  const raw = componentAttributes?.[MOUNT_PROPS_ATTRIBUTE];\n  if (!Array.isArray(raw)) {\n    return {};\n  }\n\n  const props: { [key: string]: unknown } = {};\n  for (const entry of raw) {\n    if (!isMountPropRow(entry)) {\n      continue;\n    }\n    const key = entry.key.trim();\n    if (!key || isForbiddenMountPropKey(key)) {\n      continue;\n    }\n    const coerced = coerceMountPropValue(entry);\n    if (coerced === undefined) {\n      continue;\n    }\n    props[key] = coerced;\n  }\n  return props;\n};\n","import type { CSSProperties } from 'react';\nimport type { WidgetInstanceProps } from '@source-components-react/types';\nimport {\n  resolveMountName,\n  resolveMountProps,\n  type MountsMap,\n} from './mount-attributes';\n\nconst PLACEHOLDER_STYLE: CSSProperties = {\n  boxSizing: 'border-box',\n  border: '1px dashed #94a3b8',\n  borderRadius: 4,\n  padding: '12px 16px',\n  color: '#64748b',\n  fontSize: 13,\n  lineHeight: 1.4,\n  fontFamily: 'system-ui, sans-serif',\n  backgroundColor: '#f8fafc',\n};\n\nconst warnUnmatchedMount = (name: string, registered: string[]): void => {\n  if (typeof process !== 'undefined' && process.env.NODE_ENV === 'production') {\n    return;\n  }\n  const available = registered.length ? registered.join(', ') : '(none)';\n  console.warn(\n    `r-mount: no host component registered as \"${name}\". Registered names: ${available}.`\n  );\n};\n\n/**\n * A leaf that renders a host-app component by name.\n *\n * The author sets a name and a list of typed props in the composer. The host\n * application passes the actual component on `RuleCMSWidget`'s `mounts` map.\n * This file never imports customer code: a miss is a placeholder, not a\n * lookup into executable text.\n *\n * The root is always a `<div>`. The host component is unknown, so it cannot\n * be trusted to carry column styles or a className itself. Author props are\n * never spread onto this div — only onto the resolved host component.\n */\nexport function RMount({\n  componentAttributes,\n  styleProps,\n  className,\n  mounts,\n  widgetInstanceProps,\n}: Readonly<{\n  componentAttributes?: { [key: string]: any };\n  styleProps?: { [key: string]: any };\n  className?: string;\n  mounts?: MountsMap;\n  widgetInstanceProps?: WidgetInstanceProps;\n}>) {\n  const name = resolveMountName(componentAttributes);\n  const HostComponent = name && mounts ? mounts[name] : undefined;\n\n  if (name && mounts && !HostComponent) {\n    warnUnmatchedMount(name, Object.keys(mounts));\n  }\n\n  if (HostComponent) {\n    const authorProps = resolveMountProps(componentAttributes);\n    return (\n      <div style={styleProps} {...(className !== undefined ? { className } : {})}>\n        <HostComponent {...widgetInstanceProps} {...authorProps} />\n      </div>\n    );\n  }\n\n  const style: CSSProperties = {\n    ...PLACEHOLDER_STYLE,\n    ...styleProps,\n  };\n\n  return (\n    <div style={style} {...(className !== undefined ? { className } : {})}>\n      {name ? name : 'Custom Component'}\n    </div>\n  );\n}\n","/**\n * Always renders a root `<div>` that holds the column's box styles — either\n * around the host component or around the placeholder. The host component is\n * unknown to this library, so it cannot be trusted to carry styles itself.\n */\nexport const resolveNoContainer = (): boolean => false;\n","import { CardGroup } from '@source-components-react/types';\nimport { cardEntries } from './card-entries/card-entries';\n\nexport const cardGroups: CardGroup[] = [\n  { title: 'Cards', cards: [{ title: 'Cards', entries: cardEntries }] },\n];\n","import type { DisplayCardEntry } from '@source-components-react/types';\nimport { displayCardEntry as rTextCardEntry } from '../components/r-text/display-card-entry';\nimport { displayCardEntry as cloudinaryAdvancedImageCardEntry } from '../components/cloudinary-advanced-image/display-card-entry';\nimport { displayCardEntry as rVideoCardEntry } from '../components/r-video/display-card-entry';\nimport { displayCardEntry as rIconCardEntry } from '../components/r-icon/display-card-entry';\nimport { displayCardEntry as rButtonCardEntry } from '../components/r-button/display-card-entry';\nimport { displayCardEntry as rDividerCardEntry } from '../components/r-divider/display-card-entry';\nimport { displayCardEntry as rEmbedCardEntry } from '../components/r-embed/display-card-entry';\nimport { displayCardEntry as rListCardEntry } from '../components/r-list/display-card-entry';\nimport { displayCardEntry as rAccordionCardEntry } from '../components/r-accordion/display-card-entry';\nimport { displayCardEntry as rMountCardEntry } from '../components/r-mount/display-card-entry';\n\nexport const cardEntries: DisplayCardEntry[] = [\n  rTextCardEntry,\n  cloudinaryAdvancedImageCardEntry,\n  rVideoCardEntry,\n  rIconCardEntry,\n  rButtonCardEntry,\n  rDividerCardEntry,\n  rEmbedCardEntry,\n  rListCardEntry,\n  rAccordionCardEntry,\n  rMountCardEntry,\n];\n","import { RMount } from './RMount';\nimport { resolveNoContainer } from './resolve-no-container';\nimport { resolveRendersOwnElement } from './resolve-renders-own-element';\nimport { defaultAttributeValues } from './default-attribute-values';\nimport { attributeEditModeConfigs } from './attribute-edit-mode-configs';\nimport type { DisplayCardEntry, WidgetInstanceProps } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\nimport type { MountsMap } from './mount-attributes';\n\nexport const displayCardEntry: DisplayCardEntry = {\n  id: ComponentIdentifiers.rMount,\n  resolveNoContainer,\n  resolveRendersOwnElement,\n  card: ({\n    componentAttributes,\n    styleProps,\n    className,\n    mounts,\n    widgetInstanceProps,\n  }: {\n    componentAttributes?: { [key: string]: any };\n    styleProps?: { [key: string]: any };\n    className?: string;\n    mounts?: MountsMap;\n    widgetInstanceProps?: WidgetInstanceProps;\n  }) => (\n    <RMount\n      componentAttributes={componentAttributes}\n      styleProps={styleProps}\n      className={className}\n      mounts={mounts}\n      widgetInstanceProps={widgetInstanceProps}\n    />\n  ),\n  defaultAttributeValues,\n  attributeEditModeConfigs,\n};\n","/**\n * The root `<div>` carries the className. There is always exactly one element\n * and it is always the root, whether the host component resolved or not.\n */\nexport const resolveRendersOwnElement = (): boolean => true;\n","import type { DefaultAttributeValue } from '@source-components-react/types';\nimport { MOUNT_NAME_ATTRIBUTE, MOUNT_PROPS_ATTRIBUTE } from './mount-attributes';\n\n/**\n * A fresh mount has no name and no props. Seeding either would invent a\n * registration the host does not have, so both stay empty until the author\n * fills them in.\n */\nexport const defaultAttributeValues: DefaultAttributeValue[] = [\n  {\n    name: MOUNT_NAME_ATTRIBUTE,\n    defaultValues: {},\n  },\n  {\n    name: MOUNT_PROPS_ATTRIBUTE,\n    defaultValues: {},\n  },\n];\n","import type { AttributeEditModeConfig } from '@source-components-react/types';\nimport { MOUNT_NAME_ATTRIBUTE, MOUNT_PROPS_ATTRIBUTE } from './mount-attributes';\n\n/**\n * Both settings the mount offers, in the order the Modify drawer shows them.\n *\n * Order is not cosmetic. The drawer renders these top to bottom and the\n * documentation registry asserts its pages match that sequence.\n */\nexport const attributeEditModeConfigs: AttributeEditModeConfig[] = [\n  {\n    name: MOUNT_NAME_ATTRIBUTE,\n    heading: 'Component Name',\n    subHeading:\n      'The name your developer registered in the app. Must match exactly, for 3 different device resolutions',\n    defaultValues: {},\n  },\n  {\n    name: MOUNT_PROPS_ATTRIBUTE,\n    heading: 'Props',\n    subHeading:\n      'Name and value pairs passed to that component. The component decides what each name means, for 3 different device resolutions',\n    defaultValues: {},\n  },\n];\n","import { cardGroups } from './base-components/display/card-groups';\nimport { CardGroup, Card, RenderCardEntry, DisplayCardEntry } from '@source-components-react/types';\n\nlet cachedSourceComponentsPlugin: { components: Record<string, RenderCardEntry> } | undefined;\n\nconst getComponentsFromRenderCardGroups = (group: CardGroup[]): Record<string, RenderCardEntry> => {\n  const entries = group.flatMap(group => group.cards.flatMap((card: Card) => card.entries)) as DisplayCardEntry[];\n  return entries.reduce(\n    (acc, entry) => {\n      acc[entry.id] = {\n        id: entry.id,\n        card: entry.card,\n        defaultAttributeValues: entry.defaultAttributeValues,\n        ...(entry.resolveNoContainer !== undefined ? { resolveNoContainer: entry.resolveNoContainer } : {}),\n        ...(entry.noContainer !== undefined ? { noContainer: entry.noContainer } : {}),\n        ...(entry.resolveRendersOwnElement !== undefined\n          ? { resolveRendersOwnElement: entry.resolveRendersOwnElement }\n          : {}),\n      }\n      return acc;\n    },\n    {} as Record<string, RenderCardEntry>\n  );\n};\n\n/**\n * Returns the source components plugin with a lookup map of render components.\n * Results are cached after the first call.\n */\nexport const getSourceComponentsPlugin = () => {\n  if (cachedSourceComponentsPlugin) {\n    return cachedSourceComponentsPlugin;\n  }\n\n  const components = getComponentsFromRenderCardGroups(cardGroups);\n\n  cachedSourceComponentsPlugin = {\n    components,\n  };\n\n  return cachedSourceComponentsPlugin;\n};","import { ComponentNameToModifyAttribute } from '@source-components-react/types';\n\n/**\n * Mapping of attribute names to their corresponding editor components.\n *\n * The RuleCMS editor dynamically loads the appropriate editor component for each attribute\n * that users can edit. This mapping ensures that the correct component is loaded when\n * editing a given attribute.\n *\n * The attributes defined here are shared across all source components available for user modification.\n */\nexport const componentNameToModifyAttribute: ComponentNameToModifyAttribute = {\n  'icon-name-3-resolutions': 'icon-name-3-resolutions',\n  'content-text-3-resolutions': 'content-text-3-resolutions',\n  // r-button owns its own label and icon attributes — one component per\n  // documented setting — but reuses the editors those names already resolve\n  // to, so no new editor component is needed for either.\n  'button-label-3-resolutions': 'content-text-3-resolutions',\n  'button-icon-3-resolutions': 'icon-name-3-resolutions',\n  // The preset is an object of three axes rather than one scalar, and the\n  // button takes a single link rather than the numbered list rich text uses,\n  // so neither fits an existing editor. Position and disabled are plain\n  // two-value choices and go through the shared select.\n  'button-preset-3-resolutions': 'button-preset-3-resolutions',\n  'button-link-3-resolutions': 'button-link-3-resolutions',\n  'button-icon-position-3-resolutions': 'button-icon-position-3-resolutions',\n  'button-disabled-3-resolutions': 'button-disabled-3-resolutions',\n  // Both of the divider's settings are closed sets of a handful of values, so\n  // both go through the shared select and neither needs an editor built for it.\n  'divider-style-3-resolutions': 'divider-style-3-resolutions',\n  'divider-thickness-3-resolutions': 'divider-thickness-3-resolutions',\n  // The embed URL is the one field in the library whose value is accepted or\n  // refused rather than merely stored, so it gets an editor that can say which\n  // it was while the author is still looking at the field. Its title is plain\n  // text, and the other two are closed sets.\n  'embed-url-3-resolutions': 'embed-url-3-resolutions',\n  'embed-title-3-resolutions': 'image-text-3-resolutions',\n  'embed-aspect-ratio-3-resolutions': 'embed-aspect-ratio-3-resolutions',\n  'embed-loading-3-resolutions': 'embed-loading-3-resolutions',\n  'enclosing-tag-3-resolutions': 'enclosing-tag-3-resolutions',\n  'links-3-resolutions': 'links-3-resolutions',\n  'inline-styles-3-resolutions': 'inline-styles-3-resolutions',\n  // r-list owns its own attribute names — one component per documented\n  // setting — but reuses existing editors wherever one fits: the multi-line\n  // text field for its items, the numbered link list for its links, and the\n  // plain text field for where numbering starts (the number editor is\n  // pixel-specific, and a numbering start is not a length). Kind, marker and\n  // reversed are closed sets and go through the shared select.\n  'list-items-3-resolutions': 'content-text-3-resolutions',\n  'list-kind-3-resolutions': 'list-kind-3-resolutions',\n  'list-marker-3-resolutions': 'list-marker-3-resolutions',\n  'list-start-3-resolutions': 'image-text-3-resolutions',\n  'list-reversed-3-resolutions': 'list-reversed-3-resolutions',\n  'list-links-3-resolutions': 'links-3-resolutions',\n  'list-inline-styles-3-resolutions': 'inline-styles-3-resolutions',\n  // r-accordion owns its own attribute names — one component per documented\n  // setting — and reuses existing editors for everything that fits: the\n  // multi-line text field for the summary and the detail, and the numbered\n  // link list for the detail's links. Whether it starts open and whether the\n  // summary is a heading are closed sets and go through the shared select.\n  'accordion-summary-3-resolutions': 'content-text-3-resolutions',\n  'accordion-detail-3-resolutions': 'content-text-3-resolutions',\n  'accordion-open-3-resolutions': 'accordion-open-3-resolutions',\n  'accordion-heading-level-3-resolutions': 'accordion-heading-level-3-resolutions',\n  'accordion-links-3-resolutions': 'links-3-resolutions',\n  'accordion-inline-styles-3-resolutions': 'inline-styles-3-resolutions',\n  // r-mount: the name is a constrained identifier and reuses the shared\n  // plain-text editor; the props list is a repeatable key/type/value row\n  // and needs its own editor.\n  'mount-name-3-resolutions': 'image-text-3-resolutions',\n  'mount-props-3-resolutions': 'mount-props-3-resolutions',\n  'content-title-3-resolutions': 'content-title-3-resolutions',\n  'media-selector-3-resolutions': 'media-selector-3-resolutions',\n  'pass-through-image-attributes-alt-3-resolutions': 'image-alt-text-3-resolutions',\n  'pass-through-image-attributes-width-3-resolutions': 'image-intrinsic-width-3-resolutions',\n  'pass-through-image-attributes-height-3-resolutions': 'image-intrinsic-height-3-resolutions',\n  'pass-through-image-attributes-sizes-3-resolutions': 'image-sizes-3-resolutions',\n  'pass-through-image-attributes-loading-3-resolutions': 'image-loading-3-resolutions',\n  'pass-through-image-attributes-fetchpriority-3-resolutions': 'image-fetchpriority-3-resolutions',\n  'cloudinary-transformations-3-resolutions': 'cloudinary-transformations-3-resolutions',\n  'video-selector-3-resolutions': 'video-selector-3-resolutions',\n  'video-poster-selector-3-resolutions': 'video-poster-selector-3-resolutions',\n  'pass-through-video-attributes-controls-3-resolutions': 'video-controls-3-resolutions',\n  'pass-through-video-attributes-autoplay-3-resolutions': 'video-autoplay-3-resolutions',\n  'pass-through-video-attributes-loop-3-resolutions': 'video-loop-3-resolutions',\n  'pass-through-video-attributes-muted-3-resolutions': 'video-muted-3-resolutions',\n  'pass-through-video-attributes-playsinline-3-resolutions': 'video-playsinline-3-resolutions',\n  'pass-through-video-attributes-preload-3-resolutions': 'video-preload-3-resolutions',\n  'pass-through-video-attributes-width-3-resolutions': 'video-intrinsic-width-3-resolutions',\n  'pass-through-video-attributes-height-3-resolutions': 'video-intrinsic-height-3-resolutions',\n  'video-transformations-3-resolutions': 'video-transformations-3-resolutions',\n};\n","import { AttributeListForComponentTypes } from '@source-components-react/types';\n\n/**\n * Mapping of component IDs to their supported modifiable attributes.\n *\n * Each component type can have a specific list of attributes that users are allowed to modify\n * in the editor. This mapping defines which attributes are available for each component type.\n *\n * Note: This defines which attributes each component supports, while component-name-to-modify-attribute.ts\n * defines which editor component should be used to modify each attribute type.\n * todo: this is a little confusing and might require a deeper look into how RuleCMS uses this.\n */\n/**\n * Empty on purpose. Every component this library ships declares its editable\n * attributes through its own `attribute-edit-mode-configs.ts`, which is the\n * path the composer's Modify drawer reads. Listing an attribute here as well\n * shows it twice — once through the legacy shared-attribute editor, which has\n * no entry for modern `-3-resolutions` names and renders an empty section.\n */\nexport const attributeListForComponentTypes: AttributeListForComponentTypes = {};\n","import { cardGroups } from './base-components/display/card-groups';\nimport { Card, ComponentMetadata, ComponentMetadataForEditing, DisplayCardEntry } from '@source-components-react/types';\nimport { componentNameToModifyAttribute } from './base-components/display/component-attributes/component-name-to-modify-attribute';\nimport { attributeListForComponentTypes } from './base-components/display/component-attributes/attribute-list-for-component-types';\n\nlet returnValue: ComponentMetadataForEditing | undefined;\n\n// Flatten nested card groups and cards into a flat array of entries, then converts to a lookup map by entry ID\nexport const getComponentMetadataForEditing = () => {\n  if (returnValue) {\n    return returnValue;\n  }\n  const entries = cardGroups.flatMap(group => group.cards.flatMap((card: Card) => card.entries)) as DisplayCardEntry[];\n  const componentMetadata: Record<string, ComponentMetadata> = entries.reduce(\n    (acc, entry) => {\n      acc[entry.id] = {\n        defaultAttributeValues: entry.defaultAttributeValues,\n        attributeEditModeConfigs: entry.attributeEditModeConfigs,\n      };\n      return acc;\n    },\n    {} as Record<string, ComponentMetadata>\n  );\n  returnValue = {\n    attributeListForComponentTypes,\n    componentNameToModifyAttribute,\n    componentMetadata,\n  };\n  return returnValue;\n};\n\n","import { EditorPreviewCardGroup } from \"@source-components-react/types\";\nimport { cardEntries } from \"./card-entries/card-entries\";\n\nexport const cardGroups: EditorPreviewCardGroup[] = [\n  {\n    title: \"Elements\",\n    showInitially: true,\n    cards: [{ title: \"Base Elements\", entries: cardEntries }],\n  },\n];\n","import { previewCardEntry as rTextCardEntry } from '../components/r-text/preview-card-entry';\nimport { previewCardEntry as cloudinaryAdvancedImageCardEntry } from '../components/cloudinary-advanced-image/preview-card-entry';\nimport { previewCardEntry as rVideoCardEntry } from '../components/r-video/preview-card-entry';\nimport { previewCardEntry as rIconCardEntry } from '../components/r-icon/preview-card-entry';\nimport { previewCardEntry as rButtonCardEntry } from '../components/r-button/preview-card-entry';\nimport { previewCardEntry as rDividerCardEntry } from '../components/r-divider/preview-card-entry';\nimport { previewCardEntry as rEmbedCardEntry } from '../components/r-embed/preview-card-entry';\nimport { previewCardEntry as rListCardEntry } from '../components/r-list/preview-card-entry';\nimport { previewCardEntry as rAccordionCardEntry } from '../components/r-accordion/preview-card-entry';\nimport { previewCardEntry as rMountCardEntry } from '../components/r-mount/preview-card-entry';\nimport type { EditorPreviewCardEntry } from '@source-components-react/types';\n\nexport const cardEntries: EditorPreviewCardEntry[] = [\n  rTextCardEntry,\n  cloudinaryAdvancedImageCardEntry,\n  rVideoCardEntry,\n  rIconCardEntry,\n  rButtonCardEntry,\n  rDividerCardEntry,\n  rEmbedCardEntry,\n  rListCardEntry,\n  rAccordionCardEntry,\n  rMountCardEntry,\n];\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.rText,\n  // The classic typography \"T\" glyph used by most CMS text tools.\n  icon: <span style={{ fontFamily: 'Georgia, serif', fontWeight: 'bold' }}>T</span>,\n  label: 'Text',\n};\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.cloudinaryAdvancedImage,\n  icon: '🖼️',\n  label: 'Image',\n};\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.rVideo,\n  icon: '🎬',\n  label: 'Video',\n};\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.rIcon,\n  icon: '◆',\n  label: 'Icon',\n};\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.rButton,\n  icon: '⬭',\n  label: 'Button',\n};\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.rDivider,\n  // The heavy box-drawing horizontal rather than the light one: the palette\n  // draws these at 24px in a muted grey, where a hairline glyph reads as a\n  // rendering artefact instead of as the rule the component draws.\n  icon: '━',\n  label: 'Divider',\n};\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.rEmbed,\n  // One frame inside another, which is what an embed is. A film clapper or a\n  // map pin would each name one provider at the expense of the other three,\n  // and the picture emoji is already the Image card.\n  icon: '⧉',\n  label: 'Embed',\n};\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.rList,\n  // Three stacked lines — the universal list glyph. A bullet character alone\n  // reads as a dot at the palette's 24px, and a numbered glyph would name one\n  // kind of list at the expense of the other.\n  icon: '☰',\n  label: 'List',\n};\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.rAccordion,\n  // The disclosure triangle a closed <summary> draws, pointing the way it\n  // points while the detail is collapsed. A question mark would name the FAQ\n  // case at the expense of every other use of a disclosure, and stacked bars\n  // are already the List card.\n  icon: '▸',\n  label: 'Accordion',\n};\n","import type { EditorPreviewCardEntry } from '@source-components-react/types';\nimport { ComponentIdentifiers } from '@source-components-react/component-identifiers';\n\nexport const previewCardEntry: EditorPreviewCardEntry = {\n  id: ComponentIdentifiers.rMount,\n  icon: '⌘',\n  label: 'Custom',\n};\n","import type { PreviewComponents, EditorPreviewCardGroup } from '@source-components-react/types';\nimport { cardGroups } from './base-components/editor-preview/card-groups';\n\nlet cachedPreviewComponents: PreviewComponents | undefined;\n\n/**\n * Returns the preview card groups for drag and drop functionality.\n * This replaces the dragDropPreviewCardGroups export.\n */\nexport const getPreviewCardGroups = (): EditorPreviewCardGroup[] => {\n  return cardGroups;\n};\n\n/**\n * Returns a lookup map of preview components by entry ID.\n * Results are cached after the first call.\n */\nexport const getPreviewComponents = (): PreviewComponents => {\n  if (cachedPreviewComponents) {\n    return cachedPreviewComponents;\n  }\n\n  // Flatten nested card groups and cards into a flat array of entries\n  const entries = cardGroups.flatMap(group =>\n    group.cards.flatMap(card => card.entries)\n  );\n\n  // Convert to a lookup map by entry ID\n  cachedPreviewComponents = entries.reduce(\n    (acc, entry) => {\n      acc[entry.id] = entry;\n      return acc;\n    },\n    {} as PreviewComponents\n  );\n\n  return cachedPreviewComponents;\n};\n","/**\n * SSR-safe rules for `r-button` pseudo-classes.\n *\n * widget-react injects this once per library via `ComponentLibraryModule.stylesheet`.\n * The sheet owns `background-color`, `color`, and `border-color` so `:hover`\n * can win — those properties on an inline style beat any stylesheet rule.\n * Resting values arrive as `--rulecms-button-fill` / `-ink` / `-border` on\n * the element. A column background override rewrites the fill slots and\n * derives hover/active with `color-mix`, so an arbitrary red does not keep\n * the brand-blue hover.\n *\n * Outline is never cleared: the designed ring is a `box-shadow` on\n * `:focus-visible`, on top of the browser's native focus outline.\n */\nexport const BUTTON_LIBRARY_STYLESHEET = `\n[data-rulecms-button] {\n  background-color: var(--rulecms-button-fill);\n  color: var(--rulecms-button-ink);\n  border-color: var(--rulecms-button-border, var(--rulecms-button-fill));\n}\n[data-rulecms-button]:hover:not(:disabled):not([aria-disabled=\"true\"]) {\n  background-color: var(--rulecms-button-fill-hover);\n  color: var(--rulecms-button-ink-hover, var(--rulecms-button-ink));\n  border-color: var(--rulecms-button-fill-hover);\n}\n[data-rulecms-button]:active:not(:disabled):not([aria-disabled=\"true\"]) {\n  background-color: var(--rulecms-button-fill-active);\n  border-color: var(--rulecms-button-fill-active);\n}\n[data-rulecms-button][data-rulecms-button-variant=\"outline\"]:hover:not(:disabled):not([aria-disabled=\"true\"]),\n[data-rulecms-button][data-rulecms-button-variant=\"ghost\"]:hover:not(:disabled):not([aria-disabled=\"true\"]) {\n  border-color: var(--rulecms-button-border, var(--rulecms-button-ink));\n}\n[data-rulecms-button][data-rulecms-button-variant=\"link\"]:hover:not(:disabled):not([aria-disabled=\"true\"]),\n[data-rulecms-button][data-rulecms-button-variant=\"link\"]:active:not(:disabled):not([aria-disabled=\"true\"]) {\n  background-color: transparent;\n  border-color: transparent;\n}\n[data-rulecms-button]:focus-visible {\n  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rulecms-button-ring, var(--rulecms-button-ink)) 40%, transparent);\n}\n`.trim();\n","/**\n * SSR-safe rules for `r-accordion` motion and the custom chevron.\n *\n * widget-react injects this once per library via `ComponentLibraryModule.stylesheet`.\n * Open/close height and opacity cannot be inline styles: they key off `[open]`\n * and `::details-content`. The chevron rotate is the same. Hiding the UA\n * triangle belongs here too — `summary::marker` is a pseudo-element.\n *\n * Browsers that do not support `::details-content` / `interpolate-size` still\n * open and close; they just snap. The chevron and the disclosure keep working.\n */\nexport const ACCORDION_LIBRARY_STYLESHEET = `\n[data-rulecms-accordion] {\n  interpolate-size: allow-keywords;\n}\n[data-rulecms-accordion] > summary {\n  list-style: none;\n}\n[data-rulecms-accordion] > summary::-webkit-details-marker {\n  display: none;\n}\n[data-rulecms-accordion] > summary::marker {\n  content: none;\n}\n[data-rulecms-accordion]::details-content {\n  overflow: hidden;\n  opacity: 0;\n  block-size: 0;\n  transition:\n    block-size 300ms ease,\n    opacity 300ms ease,\n    content-visibility 300ms allow-discrete;\n}\n[data-rulecms-accordion][open]::details-content {\n  opacity: 1;\n  block-size: auto;\n}\n[data-rulecms-accordion-chevron] {\n  transition: transform 300ms ease;\n}\n[data-rulecms-accordion][open] [data-rulecms-accordion-chevron] {\n  transform: rotate(180deg);\n}\n`.trim();\n","import { getSourceComponentsPlugin } from './get-source-components-plugin';\nimport { getComponentMetadataForEditing } from './get-component-metadata-for-editing';\nimport { getPreviewComponents, getPreviewCardGroups } from './get-preview-components';\nimport { BUTTON_LIBRARY_STYLESHEET } from './base-components/display/components/r-button/button-library-stylesheet';\nimport { ACCORDION_LIBRARY_STYLESHEET } from './base-components/display/components/r-accordion/accordion-library-stylesheet';\nimport packageJson from '../package.json';\n\n/**\n * ComponentLibraryModule conformance (the @rulecms/widget-react contract).\n *\n * This package is the DEFAULT component library. It conforms to the contract\n * STRUCTURALLY — it has no dependency on @rulecms/widget-react (the\n * devDependency-only type-conformance test in\n * __tests__/contract-conformance.test.ts guarantees the shape). Consumers\n * register it, e.g.:\n *\n *   libraries: { default: () => import('@rulecms/source-components-react') }\n *\n * or statically import the module for SSR pages.\n */\n\nexport const manifest = {\n  id: 'default',\n  version: packageJson.version,\n  reactPeerRange: '>=16.8.0',\n};\n\n/** The render surface: component type → renderable entry. */\nexport const components = getSourceComponentsPlugin().components;\n\n/**\n * Optional `ComponentLibraryModule.stylesheet`. widget-react injects this\n * once per library as an SSR `<style>` tag. Button hover / focus / active\n * plus accordion open/close motion and the custom chevron.\n */\nexport const stylesheet = `${BUTTON_LIBRARY_STYLESHEET}\\n${ACCORDION_LIBRARY_STYLESHEET}`;\n\n/** The editor surface: palette + attribute metadata for the RuleCMS composer. */\nexport const editor = {\n  previewComponents: getPreviewComponents(),\n  previewCardGroups: getPreviewCardGroups(),\n  componentMetadata: getComponentMetadataForEditing(),\n};\n"],"names":["MARKER","MARK_LETTERS","b","i","s","NO_MARKS","bold","italic","strike","parseInlineMarkers","value","runs","pendingText","cursor","flushPendingText","push","kind","text","length","open","indexOf","slice","lastIndex","match","exec","run","resolveRun","undefined","letters","digits","styleDigits","marks","letter","mark","resolveMarks","index","Number","parseInt","styleIndex","formatSpan","filter","map","join","formatInlineMarker","isLinkItem","Array","isArray","REJECTED_HREF_SCHEMES","SCHEME","isIgnoredWhenReadingAScheme","charCode","isSafeHref","trim","probe","charCodeAt","toLowerCase","schemeProbe","includes","IMPLIED_REL_FOR_BLANK_TARGET","cleanString","trimmed","resolveLinkAttributes","item","href","target","rel","authorRel","tokens","split","present","Set","token","merged","forEach","has","resolveRel","title","ariaLabel","download","resolveDownload","hrefLang","hreflang","referrerPolicy","isSpanStyleItem","getSpanStyleItemAt","spanStyles","oneBasedIndex","isInteger","entry","TOKEN_SEPARATOR","REJECTED_IN_TOKEN","isUsableToken","test","resolveClassNames","from","PROPERTY_NAME","REJECTED_PROPERTIES","REJECTED_IN_VALUE","REJECTED_CHARACTERS","IMPORTANT_FLAG","parseDeclaration","declaration","separator","property","replace","isUsableProperty","lowered","some","rejected","isUsableValue","parseStyleDeclarations","parsed","seen","pair","findIndex","existing","add","resolveSpanStyleAttributes","className","classNames","declarations","style","startsWith","isVendorPrefixed","camelCased","_","toUpperCase","charAt","toReactProperty","MARK_TAGS","renderSpan","anchor","spanStyle","node","createElement","position","tag","plainTextOf","renderInlineMarkup","links","anchors","getLinkItemAt","spanStyleAttributes","_jsx","Fragment","children","DEFAULT_CONTENT_TEXT","ENCLOSING_TAGS","BLOCK_ENCLOSING_TAGS","isEnclosingTag","isBlockEnclosingTag","resolveEnclosingTag","componentAttributes","fallback","userAssignedTag","RText","enclosingTag","styleProps","contentVal","content","React","ComponentIdentifiers","rText","somethingElse","cloudinaryAdvancedImage","rVideo","rIcon","rButton","rDivider","rEmbed","rList","rAccordion","rMount","displayCardEntry","id","card","name","defaultValues","all","heading","subHeading","cloudinarySrcSetWidths","defaultTransformations","f","q","c","buildCloudinaryImageUrl","cloudName","publicId","width","transformations","userTransformations","result","separatorIndex","parseTransformationString","w","encodeURI","Object","entries","key","serializeTransformations","buildCloudinarySrc","buildCloudinarySrcSet","CloudinaryAdvancedImage","alt","userCloudName","mediaSelectorPublicId","userPublicId","finalCloudName","finalPublicId","componentTransformations","finalTransformations","imageAttributes","decoding","sizes","imageStyles","height","keys","keyWithDimensionSuffix","endsWith","styleProp","attributeName","passThroughClassName","restImageAttributes","mergedClassName","Boolean","src","srcSet","hiddenToUser","defaultVideoTransformations","vc","defaultPosterTransformations","so","defaultCustomPosterTransformations","buildVideoSrc","defaults","mergeTransformations","buildVideoPosterSrc","buildCustomPosterSrc","posterPublicId","BOOLEAN_VIDEO_ATTRIBUTES","REACT_PROP_NAMES","autoplay","playsinline","RVideo","selectorPublicId","videoAttributes","controls","playsInline","preload","poster","videoStyles","maxWidth","reactName","coerceBoolean","restVideoAttributes","renderIconChildren","iconNode","attributes","PLACEHOLDER_ICON_NODE","d","x","y","rx","cx","cy","r","PLACEHOLDER_ICON","resolveIconAttributeValue","every","isIconNode","A11Y_ATTR_KEYS","RIcon","a11yProps","pickA11yProps","hasA11yProp","svgProps","xmlns","viewBox","fill","stroke","strokeWidth","strokeLinecap","strokeLinejoin","parseInlineLinkMarkers","BUTTON_VARIANTS","BUTTON_TONES","BUTTON_SIZES","DEFAULT_BUTTON_PRESET","variant","tone","size","isButtonVariant","isButtonTone","isButtonSize","TONE_PALETTES","brand","base","hover","active","contrast","border","soft","softHover","softContrast","neutral","danger","success","SIZE_SCALES","xs","fontSize","minHeight","paddingInline","paddingBlock","borderRadius","gap","sm","md","lg","xl","BUTTON_CSS_VARIABLE_PREFIX","BUTTON_FILL_VARIABLE","BUTTON_FILL_HOVER_VARIABLE","BUTTON_FILL_ACTIVE_VARIABLE","BUTTON_INK_VARIABLE","BUTTON_INK_HOVER_VARIABLE","BUTTON_BORDER_VARIABLE","BUTTON_RING_VARIABLE","toneCssVariable","slot","toneColor","variableName","cssVarWithFallback","structuralStyles","appearance","WebkitAppearance","boxSizing","display","alignItems","justifyContent","textAlign","textDecoration","userSelect","WebkitUserSelect","verticalAlign","borderStyle","fontFamily","fontWeight","lineHeight","transition","sizeStyles","preset","scale","variantStyles","palette","borderWidth","backgroundColor","borderColor","color","textUnderlineOffset","variantCssVariables","ink","inkHover","COLOR_STYLE_KEYS","buildButtonPresetStyles","buildButtonRenderStyles","styles","applyButtonColumnStyles","rest","disabledStyles","opacity","BUTTON_CLICK_HANDLER_PROP","resolveButtonPreset","candidate","BUTTON_PRESET_ATTRIBUTE","BUTTON_LABEL_ATTRIBUTE","BUTTON_LINK_ATTRIBUTE","BUTTON_DISABLED_ATTRIBUTE","BUTTON_ICON_ATTRIBUTE","BUTTON_ICON_POSITION_ATTRIBUTE","DEFAULT_BUTTON_LABEL","DEFAULT_ICON_POSITION","RButton","widgetInstanceProps","disabled","resolveButtonDisabled","onClick","handler","resolveButtonClickHandler","renderContent","resolveButtonAriaLabel","link","sharedProps","role","type","label","String","resolveButtonLabel","icon","renderButtonIcon","resolveIconPosition","_jsxs","_Fragment","flexShrink","DIVIDER_STYLE_ATTRIBUTE","DIVIDER_THICKNESS_ATTRIBUTE","DIVIDER_STYLES","DEFAULT_DIVIDER_STYLE","DIVIDER_THICKNESSES","DEFAULT_DIVIDER_THICKNESS","resolveDividerStyle","resolveDividerThickness","hostResetStyles","borderRightStyle","borderBottomStyle","borderLeftStyle","marginBlock","marginInline","buildDividerStyles","borderTopStyle","borderTopWidth","borderTopColor","RDivider","EMBED_ASPECT_RATIOS","EMBED_UNSHAPED_HEIGHT","DEFAULT_EMBED_LOADING","resolveEmbedAspectRatio","resolveEmbedLoading","resolveEmbedTitle","FRAMED_APP_SANDBOX","YOUTUBE_ID","VIMEO_ID","VIMEO_PRIVACY_HASH","CALENDLY_SEGMENT","GOOGLE_MAPS_PB","CALENDLY_RESERVED_SEGMENTS","CALENDLY_ALLOWED_PARAMS","normaliseHost","hostname","pathSegments","url","pathname","segment","EMBED_PROVIDERS","inputHosts","toEmbedUrl","videoId","segments","searchParams","get","youTubeVideoId","sandbox","allow","allowFullScreen","defaultAspectRatio","defaultTitle","withoutPrefix","trailing","hash","privacyHash","pb","options","URLSearchParams","parameter","set","query","toString","path","resolveEmbedUrl","providers","URL","protocol","username","password","host","provider","find","resolveEmbed","resolved","aspectRatio","loading","REmbed","embed","ratio","LIST_ITEMS_ATTRIBUTE","LIST_KIND_ATTRIBUTE","LIST_MARKER_ATTRIBUTE","LIST_START_ATTRIBUTE","LIST_REVERSED_ATTRIBUTE","LIST_LINKS_ATTRIBUTE","LIST_INLINE_STYLES_ATTRIBUTE","DEFAULT_LIST_ITEMS","LIST_KINDS","DEFAULT_LIST_KIND","LIST_MARKERS","RList","marker","resolveListMarker","inlineStyles","listStyleType","items","line","resolveListItems","resolveListKind","start","resolveListStart","reversed","resolveListReversed","ACCORDION_SUMMARY_ATTRIBUTE","ACCORDION_DETAIL_ATTRIBUTE","ACCORDION_LINKS_ATTRIBUTE","ACCORDION_INLINE_STYLES_ATTRIBUTE","ACCORDION_OPEN_ATTRIBUTE","ACCORDION_HEADING_LEVEL_ATTRIBUTE","DEFAULT_ACCORDION_SUMMARY","DEFAULT_ACCORDION_DETAIL","ACCORDION_HEADING_LEVELS","DEFAULT_ACCORDION_HEADING_LEVEL","resolveAccordionDetail","resolveAccordionOpen","SUMMARY_LAYOUT_STYLE","listStyle","SUMMARY_HEADING_STYLE","flex","minWidth","margin","CHEVRON_STYLE","marginLeft","AccordionChevron","RAccordion","summary","resolveAccordionSummary","headingLevel","resolveAccordionHeadingLevel","paragraph","MOUNT_NAME_ATTRIBUTE","MOUNT_PROPS_ATTRIBUTE","MOUNT_PROP_TYPES","MOUNT_NAME_PATTERN","FORBIDDEN_PROP_KEYS","HTML_EVENT_KEYS","isMountPropType","isAllowedMountName","isForbiddenMountPropKey","resolveMountName","isMountPropRow","row","coerceMountPropValue","isFinite","JSON","parse","resolveMountProps","raw","props","coerced","PLACEHOLDER_STYLE","padding","RMount","mounts","HostComponent","registered","process","env","NODE_ENV","available","console","warn","warnUnmatchedMount","authorProps","cardGroups","cards","rTextCardEntry","cloudinaryAdvancedImageCardEntry","rVideoCardEntry","rIconCardEntry","rButtonCardEntry","rDividerCardEntry","rEmbedCardEntry","rListCardEntry","rAccordionCardEntry","resolveNoContainer","resolveRendersOwnElement","defaultAttributeValues","attributeEditModeConfigs","cachedSourceComponentsPlugin","getSourceComponentsPlugin","components","flatMap","group","reduce","acc","noContainer","componentNameToModifyAttribute","attributeListForComponentTypes","returnValue","getComponentMetadataForEditing","componentMetadata","showInitially","cachedPreviewComponents","getPreviewCardGroups","getPreviewComponents","BUTTON_LIBRARY_STYLESHEET","ACCORDION_LIBRARY_STYLESHEET","manifest","version","reactPeerRange","stylesheet","editor","previewComponents","previewCardGroups"],"mappings":"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"}