@pie-element/mc-populated-blank 0.3.0-next.28 → 0.3.0-next.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { t as e } from "../McPopulatedBlank-yvmy2-rw.js";
1
+ import { t as e } from "../McPopulatedBlank-LqV-pdXv.js";
2
2
  //#region ../../lib-svelte/delivery-events/dist/index2.js
3
3
  function t(e) {
4
4
  return !!e && typeof e == "object" && !Array.isArray(e);
@@ -1 +1 @@
1
- {"version":3,"file":"layoutLimits-C2D60jW0.js","names":[],"sources":["../../src/shared/layoutLimits.ts"],"sourcesContent":["export type LayoutLimits = {\n blankStandaloneWidthRem: number;\n blankWideWidthRem: number;\n blankUnderlineWidthPx: number;\n blankUnderlineWideWidthPx: number;\n horizontalChoiceWidthPx: number;\n horizontalChoiceWidthVw: number;\n horizontalChoiceTileMinHeightRem: number;\n horizontalChoiceContentMinHeightRem: number;\n choiceImageMaxHeightRem: number;\n choiceImageMaxWidthRem: number;\n listenButtonSizePx: number;\n stimulusMinColumnPx: number;\n textMinColumnPx: number;\n legendMaxChars: number;\n choiceGroupGapRem: number;\n choiceRowGapRem: number;\n toggleButtonGapRem: number;\n horizontalChoiceRadioTopMarginRem: number;\n audioBlankTemplateMarginTopRem: number;\n audioBlankTemplateMarginBottomRem: number;\n audioInstructionsMaxWidthPx: number;\n stimulusGridColumnGapRem: number;\n stimulusGridRowGapRem: number;\n stimulusSentenceMarginTopRem: number;\n stimulusChoicesMarginTopRem: number;\n tokenGridColumnGapRem: number;\n tokenGridRowGapRem: number;\n tokenTemplateMarginTopRem: number;\n tokenInlineTokenGapRem: number;\n tokenChoicesMarginTopRem: number;\n inlineGridColumnGapRem: number;\n inlineGridRowGapRem: number;\n inlineTemplateMarginTopRem: number;\n inlineChoicesMarginTopRem: number;\n};\n\n/** Baseline values tuned to match historical CQT visuals; override via model.layoutLimits. */\nexport const DEFAULT_LAYOUT_LIMITS: LayoutLimits = {\n blankStandaloneWidthRem: 7,\n blankWideWidthRem: 10,\n blankUnderlineWidthPx: 2,\n blankUnderlineWideWidthPx: 4,\n horizontalChoiceWidthPx: 170,\n horizontalChoiceWidthVw: 30,\n horizontalChoiceTileMinHeightRem: 11,\n horizontalChoiceContentMinHeightRem: 7.5,\n // 9.375rem = 150px at the standard 16px root font size — Learnosity graphic-choice\n // SVGs are authored at 150x150; images up to that size must render at full size.\n // Shared by the choice tile image and the selected-answer image inside the cloze\n // blank, so the same choice image always renders at the same size in both places.\n choiceImageMaxHeightRem: 9.375,\n choiceImageMaxWidthRem: 9.375,\n listenButtonSizePx: 128,\n stimulusMinColumnPx: 210,\n textMinColumnPx: 260,\n legendMaxChars: 120,\n choiceGroupGapRem: 0.5,\n choiceRowGapRem: 0.5,\n toggleButtonGapRem: 0.5,\n horizontalChoiceRadioTopMarginRem: 0.5,\n audioBlankTemplateMarginTopRem: 0.8,\n audioBlankTemplateMarginBottomRem: 1.8,\n audioInstructionsMaxWidthPx: 875,\n stimulusGridColumnGapRem: 2,\n stimulusGridRowGapRem: 0.7,\n stimulusSentenceMarginTopRem: 0.2,\n stimulusChoicesMarginTopRem: 0.9,\n tokenGridColumnGapRem: 1.5,\n tokenGridRowGapRem: 0.8,\n tokenTemplateMarginTopRem: 0.6,\n tokenInlineTokenGapRem: 0.35,\n tokenChoicesMarginTopRem: 0.2,\n inlineGridColumnGapRem: 1.5,\n inlineGridRowGapRem: 0.65,\n inlineTemplateMarginTopRem: 0.45,\n inlineChoicesMarginTopRem: 0.25,\n};\n"],"mappings":";AAsCA,IAAa,IAAsC;CACjD,yBAAyB;CACzB,mBAAmB;CACnB,uBAAuB;CACvB,2BAA2B;CAC3B,yBAAyB;CACzB,yBAAyB;CACzB,kCAAkC;CAClC,qCAAqC;CAKrC,yBAAyB;CACzB,wBAAwB;CACxB,oBAAoB;CACpB,qBAAqB;CACrB,iBAAiB;CACjB,gBAAgB;CAChB,mBAAmB;CACnB,iBAAiB;CACjB,oBAAoB;CACpB,mCAAmC;CACnC,gCAAgC;CAChC,mCAAmC;CACnC,6BAA6B;CAC7B,0BAA0B;CAC1B,uBAAuB;CACvB,8BAA8B;CAC9B,6BAA6B;CAC7B,uBAAuB;CACvB,oBAAoB;CACpB,2BAA2B;CAC3B,wBAAwB;CACxB,0BAA0B;CAC1B,wBAAwB;CACxB,qBAAqB;CACrB,4BAA4B;CAC5B,2BAA2B;AAC7B"}
1
+ {"version":3,"file":"layoutLimits-C2D60jW0.js","names":[],"sources":["../../src/shared/layoutLimits.ts"],"sourcesContent":["export type LayoutLimits = {\n blankStandaloneWidthRem: number;\n blankWideWidthRem: number;\n blankUnderlineWidthPx: number;\n blankUnderlineWideWidthPx: number;\n horizontalChoiceWidthPx: number;\n horizontalChoiceWidthVw: number;\n horizontalChoiceTileMinHeightRem: number;\n horizontalChoiceContentMinHeightRem: number;\n choiceImageMaxHeightRem: number;\n choiceImageMaxWidthRem: number;\n listenButtonSizePx: number;\n stimulusMinColumnPx: number;\n textMinColumnPx: number;\n legendMaxChars: number;\n choiceGroupGapRem: number;\n choiceRowGapRem: number;\n toggleButtonGapRem: number;\n horizontalChoiceRadioTopMarginRem: number;\n audioBlankTemplateMarginTopRem: number;\n audioBlankTemplateMarginBottomRem: number;\n audioInstructionsMaxWidthPx: number;\n stimulusGridColumnGapRem: number;\n stimulusGridRowGapRem: number;\n stimulusSentenceMarginTopRem: number;\n stimulusChoicesMarginTopRem: number;\n tokenGridColumnGapRem: number;\n tokenGridRowGapRem: number;\n tokenTemplateMarginTopRem: number;\n tokenInlineTokenGapRem: number;\n tokenChoicesMarginTopRem: number;\n inlineGridColumnGapRem: number;\n inlineGridRowGapRem: number;\n inlineTemplateMarginTopRem: number;\n inlineChoicesMarginTopRem: number;\n};\n\n/** Baseline values tuned to match historical CQT visuals; override via model.layoutLimits. */\nexport const DEFAULT_LAYOUT_LIMITS: LayoutLimits = {\n blankStandaloneWidthRem: 7,\n blankWideWidthRem: 10,\n blankUnderlineWidthPx: 2,\n blankUnderlineWideWidthPx: 4,\n horizontalChoiceWidthPx: 170,\n horizontalChoiceWidthVw: 30,\n horizontalChoiceTileMinHeightRem: 11,\n horizontalChoiceContentMinHeightRem: 7.5,\n // 9.375rem = 150px at the standard 16px root font size — CQT graphic-choice\n // SVGs are authored at 150x150; images up to that size must render at full size.\n // Shared by the choice tile image and the selected-answer image inside the cloze\n // blank, so the same choice image always renders at the same size in both places.\n choiceImageMaxHeightRem: 9.375,\n choiceImageMaxWidthRem: 9.375,\n listenButtonSizePx: 128,\n stimulusMinColumnPx: 210,\n textMinColumnPx: 260,\n legendMaxChars: 120,\n choiceGroupGapRem: 0.5,\n choiceRowGapRem: 0.5,\n toggleButtonGapRem: 0.5,\n horizontalChoiceRadioTopMarginRem: 0.5,\n audioBlankTemplateMarginTopRem: 0.8,\n audioBlankTemplateMarginBottomRem: 1.8,\n audioInstructionsMaxWidthPx: 875,\n stimulusGridColumnGapRem: 2,\n stimulusGridRowGapRem: 0.7,\n stimulusSentenceMarginTopRem: 0.2,\n stimulusChoicesMarginTopRem: 0.9,\n tokenGridColumnGapRem: 1.5,\n tokenGridRowGapRem: 0.8,\n tokenTemplateMarginTopRem: 0.6,\n tokenInlineTokenGapRem: 0.35,\n tokenChoicesMarginTopRem: 0.2,\n inlineGridColumnGapRem: 1.5,\n inlineGridRowGapRem: 0.65,\n inlineTemplateMarginTopRem: 0.45,\n inlineChoicesMarginTopRem: 0.25,\n};\n"],"mappings":";AAsCA,IAAa,IAAsC;CACjD,yBAAyB;CACzB,mBAAmB;CACnB,uBAAuB;CACvB,2BAA2B;CAC3B,yBAAyB;CACzB,yBAAyB;CACzB,kCAAkC;CAClC,qCAAqC;CAKrC,yBAAyB;CACzB,wBAAwB;CACxB,oBAAoB;CACpB,qBAAqB;CACrB,iBAAiB;CACjB,gBAAgB;CAChB,mBAAmB;CACnB,iBAAiB;CACjB,oBAAoB;CACpB,mCAAmC;CACnC,gCAAgC;CAChC,mCAAmC;CACnC,6BAA6B;CAC7B,0BAA0B;CAC1B,uBAAuB;CACvB,8BAA8B;CAC9B,6BAA6B;CAC7B,uBAAuB;CACvB,oBAAoB;CACpB,2BAA2B;CAC3B,wBAAwB;CACxB,0BAA0B;CAC1B,wBAAwB;CACxB,qBAAqB;CACrB,4BAA4B;CAC5B,2BAA2B;AAC7B"}
@@ -1,5 +1,5 @@
1
1
  import { A as e, C as t, D as n, E as r, F as i, N as a, P as o, S as s, T as c, _ as l, d as u, g as d, h as f, j as p, k as m, m as h, n as g, p as _, t as v, u as y, w as b, x } from "../client-Dfdyltec.js";
2
- import { n as S, t as C } from "../McPopulatedBlank-yvmy2-rw.js";
2
+ import { n as S, t as C } from "../McPopulatedBlank-LqV-pdXv.js";
3
3
  import { n as w, o as T } from "../controller-DSkEI2th.js";
4
4
  //#region src/print/preparePrint.ts
5
5
  async function E(e, t) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../shared/controller-utils/dist/index.js","../../src/shared/layoutLimits.ts","../../src/controller/defaults.ts","../../src/author/defaults.ts","../../src/controller/index.ts"],"sourcesContent":["//#region \\0rolldown/runtime.js\nvar e = Object.defineProperty, t = (t, n) => {\n\tlet r = {};\n\tfor (var i in t) e(r, i, {\n\t\tget: t[i],\n\t\tenumerable: !0\n\t});\n\treturn n || e(r, Symbol.toStringTag, { value: \"Module\" }), r;\n};\n//#endregion\n//#region src/persistence.ts\nfunction n(e) {\n\treturn Array.isArray(e) ? e.filter((e) => e != null) : [];\n}\nfunction r(e) {\n\tlet t = [...e];\n\tfor (let e = t.length - 1; e > 0; e--) {\n\t\tlet n = Math.floor(Math.random() * (e + 1));\n\t\t[t[e], t[n]] = [t[n], t[e]];\n\t}\n\treturn t;\n}\nfunction i(e) {\n\treturn e == null ? !0 : Array.isArray(e) ? e.length === 0 : typeof e == \"object\" && Object.keys(e).length === 0;\n}\nasync function a(e, t, a, o = \"value\") {\n\tlet s = n(t?.data?.shuffledValues ?? t?.shuffledValues ?? []);\n\tif (!t) {\n\t\tconsole.warn(\"Unable to save shuffled choices because there's no session.\");\n\t\treturn;\n\t}\n\tif (!i(s)) return n(s.map((t) => e.find((e) => e[o] === t)));\n\tlet c = r(e);\n\tif (a && typeof a == \"function\") try {\n\t\tlet e = n(c.map((e) => e[o]));\n\t\ti(e) ? console.error(`shuffledValues is an empty array - refusing to call updateSession. shuffledChoices: ${JSON.stringify(c)}, key: ${o}`) : await a(t.id, t.element, { shuffledValues: e });\n\t} catch (e) {\n\t\tconsole.warn(\"Unable to save shuffled order for choices\"), console.error(e);\n\t}\n\telse console.warn(\"Unable to save shuffled choices, shuffle will happen every time.\");\n\treturn c;\n}\nfunction o(e, t, n) {\n\treturn !!(e.lockChoiceOrder || n?.[\"@pie-element\"]?.lockChoiceOrder);\n}\n//#endregion\n//#region src/partial-scoring.ts\nvar s = /* @__PURE__ */ t({ enabled: () => c });\nfunction c(e, t, n) {\n\treturn e?.partialScoring === !1 || t?.partialScoring === !1 ? !1 : typeof n != \"boolean\" || n;\n}\n//#endregion\nexport { a as getShuffledChoices, o as lockChoices, s as partialScoring };\n","export type LayoutLimits = {\n blankStandaloneWidthRem: number;\n blankWideWidthRem: number;\n blankUnderlineWidthPx: number;\n blankUnderlineWideWidthPx: number;\n horizontalChoiceWidthPx: number;\n horizontalChoiceWidthVw: number;\n horizontalChoiceTileMinHeightRem: number;\n horizontalChoiceContentMinHeightRem: number;\n choiceImageMaxHeightRem: number;\n choiceImageMaxWidthRem: number;\n listenButtonSizePx: number;\n stimulusMinColumnPx: number;\n textMinColumnPx: number;\n legendMaxChars: number;\n choiceGroupGapRem: number;\n choiceRowGapRem: number;\n toggleButtonGapRem: number;\n horizontalChoiceRadioTopMarginRem: number;\n audioBlankTemplateMarginTopRem: number;\n audioBlankTemplateMarginBottomRem: number;\n audioInstructionsMaxWidthPx: number;\n stimulusGridColumnGapRem: number;\n stimulusGridRowGapRem: number;\n stimulusSentenceMarginTopRem: number;\n stimulusChoicesMarginTopRem: number;\n tokenGridColumnGapRem: number;\n tokenGridRowGapRem: number;\n tokenTemplateMarginTopRem: number;\n tokenInlineTokenGapRem: number;\n tokenChoicesMarginTopRem: number;\n inlineGridColumnGapRem: number;\n inlineGridRowGapRem: number;\n inlineTemplateMarginTopRem: number;\n inlineChoicesMarginTopRem: number;\n};\n\n/** Baseline values tuned to match historical CQT visuals; override via model.layoutLimits. */\nexport const DEFAULT_LAYOUT_LIMITS: LayoutLimits = {\n blankStandaloneWidthRem: 7,\n blankWideWidthRem: 10,\n blankUnderlineWidthPx: 2,\n blankUnderlineWideWidthPx: 4,\n horizontalChoiceWidthPx: 170,\n horizontalChoiceWidthVw: 30,\n horizontalChoiceTileMinHeightRem: 11,\n horizontalChoiceContentMinHeightRem: 7.5,\n // 9.375rem = 150px at the standard 16px root font size — Learnosity graphic-choice\n // SVGs are authored at 150x150; images up to that size must render at full size.\n // Shared by the choice tile image and the selected-answer image inside the cloze\n // blank, so the same choice image always renders at the same size in both places.\n choiceImageMaxHeightRem: 9.375,\n choiceImageMaxWidthRem: 9.375,\n listenButtonSizePx: 128,\n stimulusMinColumnPx: 210,\n textMinColumnPx: 260,\n legendMaxChars: 120,\n choiceGroupGapRem: 0.5,\n choiceRowGapRem: 0.5,\n toggleButtonGapRem: 0.5,\n horizontalChoiceRadioTopMarginRem: 0.5,\n audioBlankTemplateMarginTopRem: 0.8,\n audioBlankTemplateMarginBottomRem: 1.8,\n audioInstructionsMaxWidthPx: 875,\n stimulusGridColumnGapRem: 2,\n stimulusGridRowGapRem: 0.7,\n stimulusSentenceMarginTopRem: 0.2,\n stimulusChoicesMarginTopRem: 0.9,\n tokenGridColumnGapRem: 1.5,\n tokenGridRowGapRem: 0.8,\n tokenTemplateMarginTopRem: 0.6,\n tokenInlineTokenGapRem: 0.35,\n tokenChoicesMarginTopRem: 0.2,\n inlineGridColumnGapRem: 1.5,\n inlineGridRowGapRem: 0.65,\n inlineTemplateMarginTopRem: 0.45,\n inlineChoicesMarginTopRem: 0.25,\n};\n","import { DEFAULT_LAYOUT_LIMITS } from '../shared/layoutLimits.js';\n\nexport { DEFAULT_LAYOUT_LIMITS };\n\n/** Token authors must include exactly once in `template` (HTML). */\nexport const BLANK_TOKEN = '{{blank}}';\n\n/**\n * The values `normalize` fills in for fields an item leaves out. They carry no\n * content and no `id` or `element`: those come from the item config, and a\n * default here would stand in for a field the author never set.\n */\nexport default {\n model: {\n prompt: '',\n promptEnabled: true,\n interactionMode: 'populate_blank' as const,\n layoutProfile: '',\n choiceLayout: '',\n layoutProfilePresets: {},\n layoutLimits: { ...DEFAULT_LAYOUT_LIMITS },\n audioButtonSkin: null,\n audioButtonSkinsByLocale: {},\n sentenceHtml: '',\n template: '',\n choiceMode: 'text' as const,\n choices: [],\n choiceGroupLabel: '',\n correctChoiceId: '',\n hasAudio: false,\n autoplayAudioEnabled: false,\n completeAudioEnabled: false,\n audioUrl: '',\n audioTranscript: '',\n locale: '',\n teacherInstructions: '',\n teacherInstructionsEnabled: true,\n },\n};\n","import controllerDefaults, { BLANK_TOKEN } from '../controller/defaults.js';\n\n/** The model a new item starts from: a valid question an author edits in place. */\nexport default {\n model: {\n ...controllerDefaults.model,\n prompt: '<p><strong>Demo prompt</strong></p>',\n template: `<p>The answer is ${BLANK_TOKEN}.</p>`,\n choices: [\n { id: 'c1', labelHtml: '<p>Option A</p>' },\n { id: 'c2', labelHtml: '<p>Option B</p>' },\n { id: 'c3', labelHtml: '<p>Option C</p>' },\n ],\n correctChoiceId: 'c2',\n },\n configuration: {\n prompt: {\n label: 'Prompt',\n settings: true,\n },\n },\n};\n","import { getShuffledChoices, lockChoices } from '@pie-element/shared-controller-utils';\nimport type { PieUpdateSession } from '@pie-element/shared-types';\nimport authorDefaults from '../author/defaults.js';\nimport defaults, { BLANK_TOKEN, DEFAULT_LAYOUT_LIMITS } from './defaults.js';\nimport type {\n McpbChoice,\n McpbQuestion,\n McpbSession,\n McpbEnv,\n McpbCorrectness,\n} from '../shared/types.js';\n\nconst isEmptyObject = (value: unknown): boolean =>\n !!value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.keys(value as Record<string, unknown>).length === 0;\n\nexport function countBlankTokens(template: string): number {\n if (!template) return 0;\n const re = /\\{\\{blank\\}\\}/g;\n return (template.match(re) || []).length;\n}\n\nexport const getCorrectness = (question: McpbQuestion, session: McpbSession): McpbCorrectness => {\n if (!session?.choiceId) {\n return 'unanswered';\n }\n const correct = question?.correctChoiceId || '';\n if (session.choiceId === correct) {\n return 'correct';\n }\n return 'incorrect';\n};\n\n/** One blank, one key: an answered session scores 1 only when it picked the key. */\nexport const getPartialScore = (question: McpbQuestion, session: McpbSession) =>\n getCorrectness(question, session) === 'correct' ? 1 : 0;\n\nexport const outcome = (question: McpbQuestion, session: McpbSession, env: McpbEnv) =>\n new Promise((resolve) => {\n if (!session || isEmptyObject(session)) {\n resolve({\n score: 0,\n empty: true,\n traceLog: ['Student did not select any answers. Score is 0.'],\n });\n return;\n }\n\n session = normalizeSession(session);\n const normalizedQuestion = normalize(question);\n const correctness = getCorrectness(normalizedQuestion, session);\n\n if (correctness === 'unanswered') {\n resolve({\n score: 0,\n empty: true,\n traceLog: ['Student did not select any answers. Score is 0.'],\n });\n return;\n }\n\n const score = getPartialScore(normalizedQuestion, session);\n const traceLog = [\n `Mode: ${env?.mode || 'unknown'}.`,\n `Student selected choice: ${session.choiceId}.`,\n `Correct choice: ${normalizedQuestion.correctChoiceId || 'none'}.`,\n `Final score: ${score}.`,\n ];\n resolve({ score, empty: false, traceLog });\n });\n\nconst withDefaults = (base: McpbQuestion, model: McpbQuestion = {}) => ({\n ...base,\n ...model,\n layoutLimits: {\n ...DEFAULT_LAYOUT_LIMITS,\n ...(model?.layoutLimits || {}),\n },\n});\n\n/** The authoring starting point: a question that passes `validate()` as it stands. */\nexport const createDefaultModel = (model: McpbQuestion = {}) =>\n withDefaults(authorDefaults.model, model);\n\n/**\n * What `model()` and `outcome()` read: the item's own fields, with neutral\n * values for the rest. Starter content here would reach a learner as the\n * item's prompt or choices.\n */\nexport const normalize = (question: McpbQuestion = {}) => {\n const { shuffle, ...rest } = question || {};\n return {\n ...withDefaults(defaults.model, rest),\n // `lockChoiceOrder` alone decides the order, as in multiple-choice. Older items\n // spell an unlocked order `shuffle: true`, which counts only when it is unset.\n lockChoiceOrder:\n typeof rest.lockChoiceOrder === 'boolean' ? rest.lockChoiceOrder : shuffle !== true,\n };\n};\n\nexport const normalizeSession = (s: McpbSession): McpbSession => ({ ...s });\n\n/**\n * The fields delivery renders. An imported choice can carry more, such as a\n * correctness flag or feedback, which must not reach a learner's browser.\n */\nconst toDeliveryChoice = ({ id, labelHtml, imageUrl, imageAlt }: McpbChoice): McpbChoice => ({\n id,\n ...(labelHtml !== undefined ? { labelHtml } : {}),\n ...(imageUrl !== undefined ? { imageUrl } : {}),\n ...(imageAlt !== undefined ? { imageAlt } : {}),\n});\n\nexport const model = async (\n question: McpbQuestion,\n session: McpbSession | null,\n env: McpbEnv | null,\n updateSession?: PieUpdateSession\n) => {\n const safeSession: McpbSession = session || {};\n const safeEnv: McpbEnv = env || {};\n const normalizedQuestion = normalize(question);\n let orderedChoices = Array.isArray(normalizedQuestion.choices)\n ? [...normalizedQuestion.choices]\n : [];\n if (orderedChoices.length && !lockChoices(normalizedQuestion, safeSession, safeEnv)) {\n orderedChoices =\n (await getShuffledChoices(orderedChoices, safeSession, updateSession, 'id')) ??\n orderedChoices;\n }\n const choices = orderedChoices\n .filter((c): c is McpbChoice => !!c && typeof c === 'object')\n .map(toDeliveryChoice);\n\n const out: Record<string, unknown> = {\n prompt: normalizedQuestion.promptEnabled ? normalizedQuestion.prompt : null,\n interactionMode: normalizedQuestion.interactionMode || 'populate_blank',\n layoutProfile: normalizedQuestion.layoutProfile || '',\n choiceLayout: normalizedQuestion.choiceLayout || '',\n layoutProfilePresets:\n normalizedQuestion.layoutProfilePresets &&\n typeof normalizedQuestion.layoutProfilePresets === 'object'\n ? normalizedQuestion.layoutProfilePresets\n : {},\n layoutLimits: normalizedQuestion.layoutLimits || { ...DEFAULT_LAYOUT_LIMITS },\n audioButtonSkin:\n normalizedQuestion.audioButtonSkin && typeof normalizedQuestion.audioButtonSkin === 'object'\n ? normalizedQuestion.audioButtonSkin\n : null,\n audioButtonSkinsByLocale:\n normalizedQuestion.audioButtonSkinsByLocale &&\n typeof normalizedQuestion.audioButtonSkinsByLocale === 'object'\n ? normalizedQuestion.audioButtonSkinsByLocale\n : {},\n sentenceHtml: normalizedQuestion.sentenceHtml || null,\n template: normalizedQuestion.template,\n choiceMode: normalizedQuestion.choiceMode,\n choices,\n choiceGroupLabel: normalizedQuestion.choiceGroupLabel || '',\n hasAudio: normalizedQuestion.hasAudio,\n autoplayAudioEnabled: !!normalizedQuestion.autoplayAudioEnabled,\n completeAudioEnabled: !!normalizedQuestion.completeAudioEnabled,\n audioUrl: normalizedQuestion.hasAudio ? normalizedQuestion.audioUrl : null,\n audioTranscript: normalizedQuestion.hasAudio ? normalizedQuestion.audioTranscript : null,\n customType: normalizedQuestion.customType || '',\n useFeatureButtonAudio:\n typeof normalizedQuestion.useFeatureButtonAudio === 'boolean'\n ? normalizedQuestion.useFeatureButtonAudio\n : undefined,\n locale: normalizedQuestion.locale || '',\n language: normalizedQuestion.language || normalizedQuestion.locale || undefined,\n disabled: safeEnv.mode !== 'gather',\n mode: safeEnv.mode,\n };\n\n if (safeEnv.mode === 'evaluate') {\n const correctness = getCorrectness(normalizedQuestion, safeSession);\n out.correctness = correctness;\n out.responseCorrect = correctness === 'correct';\n out.correctChoiceId = normalizedQuestion.correctChoiceId;\n }\n\n if (safeEnv.role === 'instructor' && (safeEnv.mode === 'view' || safeEnv.mode === 'evaluate')) {\n out.teacherInstructions = normalizedQuestion.teacherInstructionsEnabled\n ? normalizedQuestion.teacherInstructions\n : null;\n } else {\n out.teacherInstructions = null;\n }\n\n return out;\n};\n\nexport const createCorrectResponseSession = (question: McpbQuestion, env: McpbEnv) => {\n return new Promise((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n // The player sets `id` and `element` on the entry from the item config.\n resolve({\n id: '1',\n choiceId: question?.correctChoiceId || '',\n });\n } else {\n resolve(null);\n }\n });\n};\n\n/** Markup with its tags removed, keeping media an author can answer from. */\nconst getContent = (html?: string) =>\n (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '').trim();\n\ntype ValidateConfig = {\n minAnswerChoices?: number;\n maxAnswerChoices?: number;\n prompt?: { required?: boolean };\n teacherInstructions?: { required?: boolean };\n};\n\n/**\n * Authoring errors in multiple-choice's shape: a message per field, and under\n * `choices` a message per choice keyed by its id.\n */\nexport const validate = (question: McpbQuestion = {}, config: ValidateConfig = {}) => {\n const errors: Record<string, string | Record<string, string>> = {};\n const { minAnswerChoices = 2, maxAnswerChoices } = config;\n\n if ((question.promptEnabled || config.prompt?.required) && !getContent(question.prompt)) {\n errors.prompt = 'This field is required.';\n }\n\n if (config.teacherInstructions?.required && !getContent(question.teacherInstructions)) {\n errors.teacherInstructions = 'This field is required.';\n }\n\n const interactionMode = question.interactionMode || 'populate_blank';\n const template = question.template || '';\n const n = countBlankTokens(template);\n if (interactionMode === 'audio_mc_only') {\n if (n > 0) {\n errors.template = `Template cannot contain ${BLANK_TOKEN} in audio-only mode`;\n }\n } else if (interactionMode === 'populate_blank') {\n if (n === 0) {\n errors.template = `Template must contain exactly one ${BLANK_TOKEN} placeholder`;\n } else if (n > 1) {\n errors.template = `Template must contain only one ${BLANK_TOKEN} placeholder`;\n }\n } else {\n errors.interactionMode = 'Unknown interaction mode';\n }\n\n const choices = Array.isArray(question.choices) ? question.choices : [];\n if (choices.length < minAnswerChoices) {\n errors.answerChoices = `There should be at least ${minAnswerChoices} choices defined.`;\n } else if (maxAnswerChoices != null && choices.length > maxAnswerChoices) {\n errors.answerChoices = `No more than ${maxAnswerChoices} choices should be defined.`;\n }\n\n const mode = question.choiceMode || 'text';\n const choicesErrors: Record<string, string> = {};\n const seenIds = new Set<string>();\n const seenLabels = new Set<string>();\n choices.forEach((c, i) => {\n const key = c?.id || String(i);\n if (!c?.id) {\n choicesErrors[key] = 'Choice needs an id.';\n return;\n }\n // The session stores the picked id, so two choices sharing one cannot be told apart.\n if (seenIds.has(c.id)) {\n choicesErrors[key] = 'Choice id should be unique.';\n return;\n }\n seenIds.add(c.id);\n if (mode === 'text') {\n const label = getContent(c.labelHtml);\n if (!label) {\n choicesErrors[key] = 'Content should not be empty.';\n } else if (seenLabels.has(label)) {\n choicesErrors[key] = 'Content should be unique.';\n }\n seenLabels.add(label);\n } else if (!c.imageUrl?.trim()) {\n choicesErrors[key] = 'An image is required.';\n } else if (!c.imageAlt?.trim()) {\n choicesErrors[key] = 'Image alt text is required.';\n }\n });\n if (Object.keys(choicesErrors).length) {\n errors.choices = choicesErrors;\n }\n\n const correct = question.correctChoiceId;\n if (!correct) {\n errors.correctResponse = 'No correct response defined.';\n } else if (!choices.some((c) => c?.id === correct)) {\n errors.correctResponse = 'The correct response must be one of the choices.';\n }\n\n if (question.hasAudio) {\n const hasAudioUrl = !!question.audioUrl?.trim();\n if (!hasAudioUrl) {\n errors.audioUrl = 'A playable audio URL is required when audio is enabled';\n }\n }\n\n const limits = question?.layoutLimits;\n if (limits && typeof limits === 'object') {\n const numericLimitKeys = [\n 'blankStandaloneWidthRem',\n 'blankWideWidthRem',\n 'blankUnderlineWidthPx',\n 'blankUnderlineWideWidthPx',\n 'horizontalChoiceWidthPx',\n 'horizontalChoiceWidthVw',\n 'horizontalChoiceTileMinHeightRem',\n 'horizontalChoiceContentMinHeightRem',\n 'choiceImageMaxHeightRem',\n 'choiceImageMaxWidthRem',\n 'listenButtonSizePx',\n 'stimulusMinColumnPx',\n 'textMinColumnPx',\n 'legendMaxChars',\n 'choiceGroupGapRem',\n 'choiceRowGapRem',\n 'toggleButtonGapRem',\n 'horizontalChoiceRadioTopMarginRem',\n 'audioBlankTemplateMarginTopRem',\n 'audioBlankTemplateMarginBottomRem',\n 'audioInstructionsMaxWidthPx',\n 'stimulusGridColumnGapRem',\n 'stimulusGridRowGapRem',\n 'stimulusSentenceMarginTopRem',\n 'stimulusChoicesMarginTopRem',\n 'tokenGridColumnGapRem',\n 'tokenGridRowGapRem',\n 'tokenTemplateMarginTopRem',\n 'tokenInlineTokenGapRem',\n 'tokenChoicesMarginTopRem',\n 'inlineGridColumnGapRem',\n 'inlineGridRowGapRem',\n 'inlineTemplateMarginTopRem',\n 'inlineChoicesMarginTopRem',\n ] as const;\n for (const key of numericLimitKeys) {\n if (limits[key] === undefined || limits[key] === null) continue;\n const value = Number(limits[key]);\n if (!Number.isFinite(value) || value <= 0) {\n errors.layoutLimits = `${key} must be a positive number when provided`;\n break;\n }\n }\n }\n\n return errors;\n};\n"],"mappings":";AAWA,SAAS,EAAE,GAAG;CACb,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,QAAQ,MAAM,KAAK,IAAI,IAAI,CAAC;AACzD;AACA,SAAS,EAAE,GAAG;CACb,IAAI,IAAI,CAAC,GAAG,CAAC;CACb,KAAK,IAAI,IAAI,EAAE,SAAS,GAAG,IAAI,GAAG,KAAK;EACtC,IAAI,IAAI,KAAK,MAAM,KAAK,OAAO,KAAK,IAAI,EAAE;EAC1C,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE;CAC3B;CACA,OAAO;AACR;AACA,SAAS,EAAE,GAAG;CACb,OAAO,KAAK,OAAO,CAAC,IAAI,MAAM,QAAQ,CAAC,IAAI,EAAE,WAAW,IAAI,OAAO,KAAK,YAAY,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW;AAC/G;AACA,eAAe,EAAE,GAAG,GAAG,GAAG,IAAI,SAAS;CACtC,IAAI,IAAI,EAAE,GAAG,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,CAAC;CAC5D,IAAI,CAAC,GAAG;EACP,QAAQ,KAAK,6DAA6D;EAC1E;CACD;CACA,IAAI,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3D,IAAI,IAAI,EAAE,CAAC;CACX,IAAI,KAAK,OAAO,KAAK,YAAY,IAAI;EACpC,IAAI,IAAI,EAAE,EAAE,KAAK,MAAM,EAAE,EAAE,CAAC;EAC5B,EAAE,CAAC,IAAI,QAAQ,MAAM,uFAAuF,KAAK,UAAU,CAAC,EAAE,SAAS,GAAG,IAAI,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;CAC7L,SAAS,GAAG;EACX,QAAQ,KAAK,2CAA2C,GAAG,QAAQ,MAAM,CAAC;CAC3E;MACK,QAAQ,KAAK,kEAAkE;CACpF,OAAO;AACR;AACA,SAAS,EAAE,GAAG,GAAG,GAAG;CACnB,OAAO,CAAC,EAAE,EAAE,mBAAmB,IAAI,eAAe,EAAE;AACrD;;;;ACNA,IAAa,wBAAsC;CACjD,yBAAyB;CACzB,mBAAmB;CACnB,uBAAuB;CACvB,2BAA2B;CAC3B,yBAAyB;CACzB,yBAAyB;CACzB,kCAAkC;CAClC,qCAAqC;CAKrC,yBAAyB;CACzB,wBAAwB;CACxB,oBAAoB;CACpB,qBAAqB;CACrB,iBAAiB;CACjB,gBAAgB;CAChB,mBAAmB;CACnB,iBAAiB;CACjB,oBAAoB;CACpB,mCAAmC;CACnC,gCAAgC;CAChC,mCAAmC;CACnC,6BAA6B;CAC7B,0BAA0B;CAC1B,uBAAuB;CACvB,8BAA8B;CAC9B,6BAA6B;CAC7B,uBAAuB;CACvB,oBAAoB;CACpB,2BAA2B;CAC3B,wBAAwB;CACxB,0BAA0B;CAC1B,wBAAwB;CACxB,qBAAqB;CACrB,4BAA4B;CAC5B,2BAA2B;AAC7B;;;;ACxEA,IAAa,cAAc;;;;;;AAO3B,IAAA,qBAAe,EACb,OAAO;CACL,QAAQ;CACR,eAAe;CACf,iBAAiB;CACjB,eAAe;CACf,cAAc;CACd,sBAAsB,CAAC;CACvB,cAAc,EAAE,GAAG,sBAAsB;CACzC,iBAAiB;CACjB,0BAA0B,CAAC;CAC3B,cAAc;CACd,UAAU;CACV,YAAY;CACZ,SAAS,CAAC;CACV,kBAAkB;CAClB,iBAAiB;CACjB,UAAU;CACV,sBAAsB;CACtB,sBAAsB;CACtB,UAAU;CACV,iBAAiB;CACjB,QAAQ;CACR,qBAAqB;CACrB,4BAA4B;AAC9B,EACF;;;;ACnCA,IAAA,mBAAe;CACb,OAAO;EACL,GAAG,mBAAmB;EACtB,QAAQ;EACR,UAAU,oBAAoB,YAAY;EAC1C,SAAS;GACP;IAAE,IAAI;IAAM,WAAW;GAAkB;GACzC;IAAE,IAAI;IAAM,WAAW;GAAkB;GACzC;IAAE,IAAI;IAAM,WAAW;GAAkB;EAC3C;EACA,iBAAiB;CACnB;CACA,eAAe,EACb,QAAQ;EACN,OAAO;EACP,UAAU;CACZ,EACF;AACF;;;ACTA,IAAM,iBAAiB,UACrB,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,CAAC,MAAM,QAAQ,KAAK,KACpB,OAAO,KAAK,KAAgC,CAAC,CAAC,WAAW;AAE3D,SAAgB,iBAAiB,UAA0B;CACzD,IAAI,CAAC,UAAU,OAAO;CAEtB,QAAQ,SAAS,MAAM,gBAAE,KAAK,CAAC,EAAA,CAAG;AACpC;AAEA,IAAa,kBAAkB,UAAwB,YAA0C;CAC/F,IAAI,CAAC,SAAS,UACZ,OAAO;CAET,MAAM,UAAU,UAAU,mBAAmB;CAC7C,IAAI,QAAQ,aAAa,SACvB,OAAO;CAET,OAAO;AACT;;AAGA,IAAa,mBAAmB,UAAwB,YACtD,eAAe,UAAU,OAAO,MAAM,YAAY,IAAI;AAExD,IAAa,WAAW,UAAwB,SAAsB,QACpE,IAAI,SAAS,YAAY;CACvB,IAAI,CAAC,WAAW,cAAc,OAAO,GAAG;EACtC,QAAQ;GACN,OAAO;GACP,OAAO;GACP,UAAU,CAAC,iDAAiD;EAC9D,CAAC;EACD;CACF;CAEA,UAAU,iBAAiB,OAAO;CAClC,MAAM,qBAAqB,UAAU,QAAQ;CAG7C,IAFoB,eAAe,oBAAoB,OAEnD,MAAgB,cAAc;EAChC,QAAQ;GACN,OAAO;GACP,OAAO;GACP,UAAU,CAAC,iDAAiD;EAC9D,CAAC;EACD;CACF;CAEA,MAAM,QAAQ,gBAAgB,oBAAoB,OAAO;CAOzD,QAAQ;EAAE;EAAO,OAAO;EAAO,UAAA;GAL7B,SAAS,KAAK,QAAQ,UAAU;GAChC,4BAA4B,QAAQ,SAAS;GAC7C,mBAAmB,mBAAmB,mBAAmB,OAAO;GAChE,gBAAgB,MAAM;EAEO;CAAS,CAAC;AAC3C,CAAC;AAEH,IAAM,gBAAgB,MAAoB,QAAsB,CAAC,OAAO;CACtE,GAAG;CACH,GAAG;CACH,cAAc;EACZ,GAAG;EACH,GAAI,OAAO,gBAAgB,CAAC;CAC9B;AACF;;AAGA,IAAa,sBAAsB,QAAsB,CAAC,MACxD,aAAa,iBAAe,OAAO,KAAK;;;;;;AAO1C,IAAa,aAAa,WAAyB,CAAC,MAAM;CACxD,MAAM,EAAE,SAAS,GAAG,SAAS,YAAY,CAAC;CAC1C,OAAO;EACL,GAAG,aAAa,mBAAS,OAAO,IAAI;EAGpC,iBACE,OAAO,KAAK,oBAAoB,YAAY,KAAK,kBAAkB,YAAY;CACnF;AACF;AAEA,IAAa,oBAAoB,OAAiC,EAAE,GAAG,EAAE;;;;;AAMzE,IAAM,oBAAoB,EAAE,IAAI,WAAW,UAAU,gBAAwC;CAC3F;CACA,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;CAC/C,GAAI,aAAa,KAAA,IAAY,EAAE,SAAS,IAAI,CAAC;CAC7C,GAAI,aAAa,KAAA,IAAY,EAAE,SAAS,IAAI,CAAC;AAC/C;AAEA,IAAa,QAAQ,OACnB,UACA,SACA,KACA,kBACG;CACH,MAAM,cAA2B,WAAW,CAAC;CAC7C,MAAM,UAAmB,OAAO,CAAC;CACjC,MAAM,qBAAqB,UAAU,QAAQ;CAC7C,IAAI,iBAAiB,MAAM,QAAQ,mBAAmB,OAAO,IACzD,CAAC,GAAG,mBAAmB,OAAO,IAC9B,CAAC;CACL,IAAI,eAAe,UAAU,CAAC,EAAY,oBAAoB,aAAa,OAAO,GAChF,iBACG,MAAM,EAAmB,gBAAgB,aAAa,eAAe,IAAI,KAC1E;CAEJ,MAAM,UAAU,eACb,QAAQ,MAAuB,CAAC,CAAC,KAAK,OAAO,MAAM,QAAQ,CAAC,CAC5D,IAAI,gBAAgB;CAEvB,MAAM,MAA+B;EACnC,QAAQ,mBAAmB,gBAAgB,mBAAmB,SAAS;EACvE,iBAAiB,mBAAmB,mBAAmB;EACvD,eAAe,mBAAmB,iBAAiB;EACnD,cAAc,mBAAmB,gBAAgB;EACjD,sBACE,mBAAmB,wBACnB,OAAO,mBAAmB,yBAAyB,WAC/C,mBAAmB,uBACnB,CAAC;EACP,cAAc,mBAAmB,gBAAgB,EAAE,GAAG,sBAAsB;EAC5E,iBACE,mBAAmB,mBAAmB,OAAO,mBAAmB,oBAAoB,WAChF,mBAAmB,kBACnB;EACN,0BACE,mBAAmB,4BACnB,OAAO,mBAAmB,6BAA6B,WACnD,mBAAmB,2BACnB,CAAC;EACP,cAAc,mBAAmB,gBAAgB;EACjD,UAAU,mBAAmB;EAC7B,YAAY,mBAAmB;EAC/B;EACA,kBAAkB,mBAAmB,oBAAoB;EACzD,UAAU,mBAAmB;EAC7B,sBAAsB,CAAC,CAAC,mBAAmB;EAC3C,sBAAsB,CAAC,CAAC,mBAAmB;EAC3C,UAAU,mBAAmB,WAAW,mBAAmB,WAAW;EACtE,iBAAiB,mBAAmB,WAAW,mBAAmB,kBAAkB;EACpF,YAAY,mBAAmB,cAAc;EAC7C,uBACE,OAAO,mBAAmB,0BAA0B,YAChD,mBAAmB,wBACnB,KAAA;EACN,QAAQ,mBAAmB,UAAU;EACrC,UAAU,mBAAmB,YAAY,mBAAmB,UAAU,KAAA;EACtE,UAAU,QAAQ,SAAS;EAC3B,MAAM,QAAQ;CAChB;CAEA,IAAI,QAAQ,SAAS,YAAY;EAC/B,MAAM,cAAc,eAAe,oBAAoB,WAAW;EAClE,IAAI,cAAc;EAClB,IAAI,kBAAkB,gBAAgB;EACtC,IAAI,kBAAkB,mBAAmB;CAC3C;CAEA,IAAI,QAAQ,SAAS,iBAAiB,QAAQ,SAAS,UAAU,QAAQ,SAAS,aAChF,IAAI,sBAAsB,mBAAmB,6BACzC,mBAAmB,sBACnB;MAEJ,IAAI,sBAAsB;CAG5B,OAAO;AACT;AAEA,IAAa,gCAAgC,UAAwB,QAAiB;CACpF,OAAO,IAAI,SAAS,YAAY;EAC9B,IAAI,IAAI,SAAS,cAAc,IAAI,SAAS,cAE1C,QAAQ;GACN,IAAI;GACJ,UAAU,UAAU,mBAAmB;EACzC,CAAC;OAED,QAAQ,IAAI;CAEhB,CAAC;AACH;;AAGA,IAAM,cAAc,UACjB,QAAQ,GAAA,CAAI,QAAQ,sCAAsC,EAAE,CAAC,CAAC,KAAK;;;;;AAatE,IAAa,YAAY,WAAyB,CAAC,GAAG,SAAyB,CAAC,MAAM;CACpF,MAAM,SAA0D,CAAC;CACjE,MAAM,EAAE,mBAAmB,GAAG,qBAAqB;CAEnD,KAAK,SAAS,iBAAiB,OAAO,QAAQ,aAAa,CAAC,WAAW,SAAS,MAAM,GACpF,OAAO,SAAS;CAGlB,IAAI,OAAO,qBAAqB,YAAY,CAAC,WAAW,SAAS,mBAAmB,GAClF,OAAO,sBAAsB;CAG/B,MAAM,kBAAkB,SAAS,mBAAmB;CAEpD,MAAM,IAAI,iBADO,SAAS,YAAY,EACH;CACnC,IAAI,oBAAoB;MAClB,IAAI,GACN,OAAO,WAAW,2BAA2B,YAAY;CAAA,OAEtD,IAAI,oBAAoB;MACzB,MAAM,GACR,OAAO,WAAW,qCAAqC,YAAY;OAC9D,IAAI,IAAI,GACb,OAAO,WAAW,kCAAkC,YAAY;CAAA,OAGlE,OAAO,kBAAkB;CAG3B,MAAM,UAAU,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,UAAU,CAAC;CACtE,IAAI,QAAQ,SAAS,kBACnB,OAAO,gBAAgB,4BAA4B,iBAAiB;MAC/D,IAAI,oBAAoB,QAAQ,QAAQ,SAAS,kBACtD,OAAO,gBAAgB,gBAAgB,iBAAiB;CAG1D,MAAM,OAAO,SAAS,cAAc;CACpC,MAAM,gBAAwC,CAAC;CAC/C,MAAM,0BAAU,IAAI,IAAY;CAChC,MAAM,6BAAa,IAAI,IAAY;CACnC,QAAQ,SAAS,GAAG,MAAM;EACxB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;EAC7B,IAAI,CAAC,GAAG,IAAI;GACV,cAAc,OAAO;GACrB;EACF;EAEA,IAAI,QAAQ,IAAI,EAAE,EAAE,GAAG;GACrB,cAAc,OAAO;GACrB;EACF;EACA,QAAQ,IAAI,EAAE,EAAE;EAChB,IAAI,SAAS,QAAQ;GACnB,MAAM,QAAQ,WAAW,EAAE,SAAS;GACpC,IAAI,CAAC,OACH,cAAc,OAAO;QAChB,IAAI,WAAW,IAAI,KAAK,GAC7B,cAAc,OAAO;GAEvB,WAAW,IAAI,KAAK;EACtB,OAAO,IAAI,CAAC,EAAE,UAAU,KAAK,GAC3B,cAAc,OAAO;OAChB,IAAI,CAAC,EAAE,UAAU,KAAK,GAC3B,cAAc,OAAO;CAEzB,CAAC;CACD,IAAI,OAAO,KAAK,aAAa,CAAC,CAAC,QAC7B,OAAO,UAAU;CAGnB,MAAM,UAAU,SAAS;CACzB,IAAI,CAAC,SACH,OAAO,kBAAkB;MACpB,IAAI,CAAC,QAAQ,MAAM,MAAM,GAAG,OAAO,OAAO,GAC/C,OAAO,kBAAkB;CAG3B,IAAI,SAAS;MAEP,CAAC,CADgB,CAAC,SAAS,UAAU,KAAK,GAE5C,OAAO,WAAW;CAAA;CAItB,MAAM,SAAS,UAAU;CACzB,IAAI,UAAU,OAAO,WAAW,UAqC9B,KAAK,MAAM,OAAO;EAnChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAEgB,GAAkB;EAClC,IAAI,OAAO,SAAS,KAAA,KAAa,OAAO,SAAS,MAAM;EACvD,MAAM,QAAQ,OAAO,OAAO,IAAI;EAChC,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG;GACzC,OAAO,eAAe,GAAG,IAAI;GAC7B;EACF;CACF;CAGF,OAAO;AACT"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../shared/controller-utils/dist/index.js","../../src/shared/layoutLimits.ts","../../src/controller/defaults.ts","../../src/author/defaults.ts","../../src/controller/index.ts"],"sourcesContent":["//#region \\0rolldown/runtime.js\nvar e = Object.defineProperty, t = (t, n) => {\n\tlet r = {};\n\tfor (var i in t) e(r, i, {\n\t\tget: t[i],\n\t\tenumerable: !0\n\t});\n\treturn n || e(r, Symbol.toStringTag, { value: \"Module\" }), r;\n};\n//#endregion\n//#region src/persistence.ts\nfunction n(e) {\n\treturn Array.isArray(e) ? e.filter((e) => e != null) : [];\n}\nfunction r(e) {\n\tlet t = [...e];\n\tfor (let e = t.length - 1; e > 0; e--) {\n\t\tlet n = Math.floor(Math.random() * (e + 1));\n\t\t[t[e], t[n]] = [t[n], t[e]];\n\t}\n\treturn t;\n}\nfunction i(e) {\n\treturn e == null ? !0 : Array.isArray(e) ? e.length === 0 : typeof e == \"object\" && Object.keys(e).length === 0;\n}\nasync function a(e, t, a, o = \"value\") {\n\tlet s = n(t?.data?.shuffledValues ?? t?.shuffledValues ?? []);\n\tif (!t) {\n\t\tconsole.warn(\"Unable to save shuffled choices because there's no session.\");\n\t\treturn;\n\t}\n\tif (!i(s)) return n(s.map((t) => e.find((e) => e[o] === t)));\n\tlet c = r(e);\n\tif (a && typeof a == \"function\") try {\n\t\tlet e = n(c.map((e) => e[o]));\n\t\ti(e) ? console.error(`shuffledValues is an empty array - refusing to call updateSession. shuffledChoices: ${JSON.stringify(c)}, key: ${o}`) : await a(t.id, t.element, { shuffledValues: e });\n\t} catch (e) {\n\t\tconsole.warn(\"Unable to save shuffled order for choices\"), console.error(e);\n\t}\n\telse console.warn(\"Unable to save shuffled choices, shuffle will happen every time.\");\n\treturn c;\n}\nfunction o(e, t, n) {\n\treturn !!(e.lockChoiceOrder || n?.[\"@pie-element\"]?.lockChoiceOrder);\n}\n//#endregion\n//#region src/partial-scoring.ts\nvar s = /* @__PURE__ */ t({ enabled: () => c });\nfunction c(e, t, n) {\n\treturn e?.partialScoring === !1 || t?.partialScoring === !1 ? !1 : typeof n != \"boolean\" || n;\n}\n//#endregion\nexport { a as getShuffledChoices, o as lockChoices, s as partialScoring };\n","export type LayoutLimits = {\n blankStandaloneWidthRem: number;\n blankWideWidthRem: number;\n blankUnderlineWidthPx: number;\n blankUnderlineWideWidthPx: number;\n horizontalChoiceWidthPx: number;\n horizontalChoiceWidthVw: number;\n horizontalChoiceTileMinHeightRem: number;\n horizontalChoiceContentMinHeightRem: number;\n choiceImageMaxHeightRem: number;\n choiceImageMaxWidthRem: number;\n listenButtonSizePx: number;\n stimulusMinColumnPx: number;\n textMinColumnPx: number;\n legendMaxChars: number;\n choiceGroupGapRem: number;\n choiceRowGapRem: number;\n toggleButtonGapRem: number;\n horizontalChoiceRadioTopMarginRem: number;\n audioBlankTemplateMarginTopRem: number;\n audioBlankTemplateMarginBottomRem: number;\n audioInstructionsMaxWidthPx: number;\n stimulusGridColumnGapRem: number;\n stimulusGridRowGapRem: number;\n stimulusSentenceMarginTopRem: number;\n stimulusChoicesMarginTopRem: number;\n tokenGridColumnGapRem: number;\n tokenGridRowGapRem: number;\n tokenTemplateMarginTopRem: number;\n tokenInlineTokenGapRem: number;\n tokenChoicesMarginTopRem: number;\n inlineGridColumnGapRem: number;\n inlineGridRowGapRem: number;\n inlineTemplateMarginTopRem: number;\n inlineChoicesMarginTopRem: number;\n};\n\n/** Baseline values tuned to match historical CQT visuals; override via model.layoutLimits. */\nexport const DEFAULT_LAYOUT_LIMITS: LayoutLimits = {\n blankStandaloneWidthRem: 7,\n blankWideWidthRem: 10,\n blankUnderlineWidthPx: 2,\n blankUnderlineWideWidthPx: 4,\n horizontalChoiceWidthPx: 170,\n horizontalChoiceWidthVw: 30,\n horizontalChoiceTileMinHeightRem: 11,\n horizontalChoiceContentMinHeightRem: 7.5,\n // 9.375rem = 150px at the standard 16px root font size — CQT graphic-choice\n // SVGs are authored at 150x150; images up to that size must render at full size.\n // Shared by the choice tile image and the selected-answer image inside the cloze\n // blank, so the same choice image always renders at the same size in both places.\n choiceImageMaxHeightRem: 9.375,\n choiceImageMaxWidthRem: 9.375,\n listenButtonSizePx: 128,\n stimulusMinColumnPx: 210,\n textMinColumnPx: 260,\n legendMaxChars: 120,\n choiceGroupGapRem: 0.5,\n choiceRowGapRem: 0.5,\n toggleButtonGapRem: 0.5,\n horizontalChoiceRadioTopMarginRem: 0.5,\n audioBlankTemplateMarginTopRem: 0.8,\n audioBlankTemplateMarginBottomRem: 1.8,\n audioInstructionsMaxWidthPx: 875,\n stimulusGridColumnGapRem: 2,\n stimulusGridRowGapRem: 0.7,\n stimulusSentenceMarginTopRem: 0.2,\n stimulusChoicesMarginTopRem: 0.9,\n tokenGridColumnGapRem: 1.5,\n tokenGridRowGapRem: 0.8,\n tokenTemplateMarginTopRem: 0.6,\n tokenInlineTokenGapRem: 0.35,\n tokenChoicesMarginTopRem: 0.2,\n inlineGridColumnGapRem: 1.5,\n inlineGridRowGapRem: 0.65,\n inlineTemplateMarginTopRem: 0.45,\n inlineChoicesMarginTopRem: 0.25,\n};\n","import { DEFAULT_LAYOUT_LIMITS } from '../shared/layoutLimits.js';\n\nexport { DEFAULT_LAYOUT_LIMITS };\n\n/** Token authors must include exactly once in `template` (HTML). */\nexport const BLANK_TOKEN = '{{blank}}';\n\n/**\n * The values `normalize` fills in for fields an item leaves out. They carry no\n * content and no `id` or `element`: those come from the item config, and a\n * default here would stand in for a field the author never set.\n */\nexport default {\n model: {\n prompt: '',\n promptEnabled: true,\n interactionMode: 'populate_blank' as const,\n layoutProfile: '',\n choiceLayout: '',\n layoutProfilePresets: {},\n layoutLimits: { ...DEFAULT_LAYOUT_LIMITS },\n audioButtonSkin: null,\n audioButtonSkinsByLocale: {},\n sentenceHtml: '',\n template: '',\n choiceMode: 'text' as const,\n choices: [],\n choiceGroupLabel: '',\n correctChoiceId: '',\n hasAudio: false,\n autoplayAudioEnabled: false,\n completeAudioEnabled: false,\n audioUrl: '',\n audioTranscript: '',\n locale: '',\n teacherInstructions: '',\n teacherInstructionsEnabled: true,\n },\n};\n","import controllerDefaults, { BLANK_TOKEN } from '../controller/defaults.js';\n\n/** The model a new item starts from: a valid question an author edits in place. */\nexport default {\n model: {\n ...controllerDefaults.model,\n prompt: '<p><strong>Demo prompt</strong></p>',\n template: `<p>The answer is ${BLANK_TOKEN}.</p>`,\n choices: [\n { id: 'c1', labelHtml: '<p>Option A</p>' },\n { id: 'c2', labelHtml: '<p>Option B</p>' },\n { id: 'c3', labelHtml: '<p>Option C</p>' },\n ],\n correctChoiceId: 'c2',\n },\n configuration: {\n prompt: {\n label: 'Prompt',\n settings: true,\n },\n },\n};\n","import { getShuffledChoices, lockChoices } from '@pie-element/shared-controller-utils';\nimport type { PieUpdateSession } from '@pie-element/shared-types';\nimport authorDefaults from '../author/defaults.js';\nimport defaults, { BLANK_TOKEN, DEFAULT_LAYOUT_LIMITS } from './defaults.js';\nimport type {\n McpbChoice,\n McpbQuestion,\n McpbSession,\n McpbEnv,\n McpbCorrectness,\n} from '../shared/types.js';\n\nconst isEmptyObject = (value: unknown): boolean =>\n !!value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.keys(value as Record<string, unknown>).length === 0;\n\nexport function countBlankTokens(template: string): number {\n if (!template) return 0;\n const re = /\\{\\{blank\\}\\}/g;\n return (template.match(re) || []).length;\n}\n\nexport const getCorrectness = (question: McpbQuestion, session: McpbSession): McpbCorrectness => {\n if (!session?.choiceId) {\n return 'unanswered';\n }\n const correct = question?.correctChoiceId || '';\n if (session.choiceId === correct) {\n return 'correct';\n }\n return 'incorrect';\n};\n\n/** One blank, one key: an answered session scores 1 only when it picked the key. */\nexport const getPartialScore = (question: McpbQuestion, session: McpbSession) =>\n getCorrectness(question, session) === 'correct' ? 1 : 0;\n\nexport const outcome = (question: McpbQuestion, session: McpbSession, env: McpbEnv) =>\n new Promise((resolve) => {\n if (!session || isEmptyObject(session)) {\n resolve({\n score: 0,\n empty: true,\n traceLog: ['Student did not select any answers. Score is 0.'],\n });\n return;\n }\n\n session = normalizeSession(session);\n const normalizedQuestion = normalize(question);\n const correctness = getCorrectness(normalizedQuestion, session);\n\n if (correctness === 'unanswered') {\n resolve({\n score: 0,\n empty: true,\n traceLog: ['Student did not select any answers. Score is 0.'],\n });\n return;\n }\n\n const score = getPartialScore(normalizedQuestion, session);\n const traceLog = [\n `Mode: ${env?.mode || 'unknown'}.`,\n `Student selected choice: ${session.choiceId}.`,\n `Correct choice: ${normalizedQuestion.correctChoiceId || 'none'}.`,\n `Final score: ${score}.`,\n ];\n resolve({ score, empty: false, traceLog });\n });\n\nconst withDefaults = (base: McpbQuestion, model: McpbQuestion = {}) => ({\n ...base,\n ...model,\n layoutLimits: {\n ...DEFAULT_LAYOUT_LIMITS,\n ...(model?.layoutLimits || {}),\n },\n});\n\n/** The authoring starting point: a question that passes `validate()` as it stands. */\nexport const createDefaultModel = (model: McpbQuestion = {}) =>\n withDefaults(authorDefaults.model, model);\n\n/**\n * What `model()` and `outcome()` read: the item's own fields, with neutral\n * values for the rest. Starter content here would reach a learner as the\n * item's prompt or choices.\n */\nexport const normalize = (question: McpbQuestion = {}) => {\n const { shuffle, ...rest } = question || {};\n return {\n ...withDefaults(defaults.model, rest),\n // `lockChoiceOrder` alone decides the order, as in multiple-choice. Older items\n // spell an unlocked order `shuffle: true`, which counts only when it is unset.\n lockChoiceOrder:\n typeof rest.lockChoiceOrder === 'boolean' ? rest.lockChoiceOrder : shuffle !== true,\n };\n};\n\nexport const normalizeSession = (s: McpbSession): McpbSession => ({ ...s });\n\n/**\n * The fields delivery renders. An imported choice can carry more, such as a\n * correctness flag or feedback, which must not reach a learner's browser.\n */\nconst toDeliveryChoice = ({ id, labelHtml, imageUrl, imageAlt }: McpbChoice): McpbChoice => ({\n id,\n ...(labelHtml !== undefined ? { labelHtml } : {}),\n ...(imageUrl !== undefined ? { imageUrl } : {}),\n ...(imageAlt !== undefined ? { imageAlt } : {}),\n});\n\nexport const model = async (\n question: McpbQuestion,\n session: McpbSession | null,\n env: McpbEnv | null,\n updateSession?: PieUpdateSession\n) => {\n const safeSession: McpbSession = session || {};\n const safeEnv: McpbEnv = env || {};\n const normalizedQuestion = normalize(question);\n let orderedChoices = Array.isArray(normalizedQuestion.choices)\n ? [...normalizedQuestion.choices]\n : [];\n if (orderedChoices.length && !lockChoices(normalizedQuestion, safeSession, safeEnv)) {\n orderedChoices =\n (await getShuffledChoices(orderedChoices, safeSession, updateSession, 'id')) ??\n orderedChoices;\n }\n const choices = orderedChoices\n .filter((c): c is McpbChoice => !!c && typeof c === 'object')\n .map(toDeliveryChoice);\n\n const out: Record<string, unknown> = {\n prompt: normalizedQuestion.promptEnabled ? normalizedQuestion.prompt : null,\n interactionMode: normalizedQuestion.interactionMode || 'populate_blank',\n layoutProfile: normalizedQuestion.layoutProfile || '',\n choiceLayout: normalizedQuestion.choiceLayout || '',\n layoutProfilePresets:\n normalizedQuestion.layoutProfilePresets &&\n typeof normalizedQuestion.layoutProfilePresets === 'object'\n ? normalizedQuestion.layoutProfilePresets\n : {},\n layoutLimits: normalizedQuestion.layoutLimits || { ...DEFAULT_LAYOUT_LIMITS },\n audioButtonSkin:\n normalizedQuestion.audioButtonSkin && typeof normalizedQuestion.audioButtonSkin === 'object'\n ? normalizedQuestion.audioButtonSkin\n : null,\n audioButtonSkinsByLocale:\n normalizedQuestion.audioButtonSkinsByLocale &&\n typeof normalizedQuestion.audioButtonSkinsByLocale === 'object'\n ? normalizedQuestion.audioButtonSkinsByLocale\n : {},\n sentenceHtml: normalizedQuestion.sentenceHtml || null,\n template: normalizedQuestion.template,\n choiceMode: normalizedQuestion.choiceMode,\n choices,\n choiceGroupLabel: normalizedQuestion.choiceGroupLabel || '',\n hasAudio: normalizedQuestion.hasAudio,\n autoplayAudioEnabled: !!normalizedQuestion.autoplayAudioEnabled,\n completeAudioEnabled: !!normalizedQuestion.completeAudioEnabled,\n audioUrl: normalizedQuestion.hasAudio ? normalizedQuestion.audioUrl : null,\n audioTranscript: normalizedQuestion.hasAudio ? normalizedQuestion.audioTranscript : null,\n customType: normalizedQuestion.customType || '',\n useFeatureButtonAudio:\n typeof normalizedQuestion.useFeatureButtonAudio === 'boolean'\n ? normalizedQuestion.useFeatureButtonAudio\n : undefined,\n locale: normalizedQuestion.locale || '',\n language: normalizedQuestion.language || normalizedQuestion.locale || undefined,\n disabled: safeEnv.mode !== 'gather',\n mode: safeEnv.mode,\n };\n\n if (safeEnv.mode === 'evaluate') {\n const correctness = getCorrectness(normalizedQuestion, safeSession);\n out.correctness = correctness;\n out.responseCorrect = correctness === 'correct';\n out.correctChoiceId = normalizedQuestion.correctChoiceId;\n }\n\n if (safeEnv.role === 'instructor' && (safeEnv.mode === 'view' || safeEnv.mode === 'evaluate')) {\n out.teacherInstructions = normalizedQuestion.teacherInstructionsEnabled\n ? normalizedQuestion.teacherInstructions\n : null;\n } else {\n out.teacherInstructions = null;\n }\n\n return out;\n};\n\nexport const createCorrectResponseSession = (question: McpbQuestion, env: McpbEnv) => {\n return new Promise((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n // The player sets `id` and `element` on the entry from the item config.\n resolve({\n id: '1',\n choiceId: question?.correctChoiceId || '',\n });\n } else {\n resolve(null);\n }\n });\n};\n\n/** Markup with its tags removed, keeping media an author can answer from. */\nconst getContent = (html?: string) =>\n (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '').trim();\n\ntype ValidateConfig = {\n minAnswerChoices?: number;\n maxAnswerChoices?: number;\n prompt?: { required?: boolean };\n teacherInstructions?: { required?: boolean };\n};\n\n/**\n * Authoring errors in multiple-choice's shape: a message per field, and under\n * `choices` a message per choice keyed by its id.\n */\nexport const validate = (question: McpbQuestion = {}, config: ValidateConfig = {}) => {\n const errors: Record<string, string | Record<string, string>> = {};\n const { minAnswerChoices = 2, maxAnswerChoices } = config;\n\n if ((question.promptEnabled || config.prompt?.required) && !getContent(question.prompt)) {\n errors.prompt = 'This field is required.';\n }\n\n if (config.teacherInstructions?.required && !getContent(question.teacherInstructions)) {\n errors.teacherInstructions = 'This field is required.';\n }\n\n const interactionMode = question.interactionMode || 'populate_blank';\n const template = question.template || '';\n const n = countBlankTokens(template);\n if (interactionMode === 'audio_mc_only') {\n if (n > 0) {\n errors.template = `Template cannot contain ${BLANK_TOKEN} in audio-only mode`;\n }\n } else if (interactionMode === 'populate_blank') {\n if (n === 0) {\n errors.template = `Template must contain exactly one ${BLANK_TOKEN} placeholder`;\n } else if (n > 1) {\n errors.template = `Template must contain only one ${BLANK_TOKEN} placeholder`;\n }\n } else {\n errors.interactionMode = 'Unknown interaction mode';\n }\n\n const choices = Array.isArray(question.choices) ? question.choices : [];\n if (choices.length < minAnswerChoices) {\n errors.answerChoices = `There should be at least ${minAnswerChoices} choices defined.`;\n } else if (maxAnswerChoices != null && choices.length > maxAnswerChoices) {\n errors.answerChoices = `No more than ${maxAnswerChoices} choices should be defined.`;\n }\n\n const mode = question.choiceMode || 'text';\n const choicesErrors: Record<string, string> = {};\n const seenIds = new Set<string>();\n const seenLabels = new Set<string>();\n choices.forEach((c, i) => {\n const key = c?.id || String(i);\n if (!c?.id) {\n choicesErrors[key] = 'Choice needs an id.';\n return;\n }\n // The session stores the picked id, so two choices sharing one cannot be told apart.\n if (seenIds.has(c.id)) {\n choicesErrors[key] = 'Choice id should be unique.';\n return;\n }\n seenIds.add(c.id);\n if (mode === 'text') {\n const label = getContent(c.labelHtml);\n if (!label) {\n choicesErrors[key] = 'Content should not be empty.';\n } else if (seenLabels.has(label)) {\n choicesErrors[key] = 'Content should be unique.';\n }\n seenLabels.add(label);\n } else if (!c.imageUrl?.trim()) {\n choicesErrors[key] = 'An image is required.';\n } else if (!c.imageAlt?.trim()) {\n choicesErrors[key] = 'Image alt text is required.';\n }\n });\n if (Object.keys(choicesErrors).length) {\n errors.choices = choicesErrors;\n }\n\n const correct = question.correctChoiceId;\n if (!correct) {\n errors.correctResponse = 'No correct response defined.';\n } else if (!choices.some((c) => c?.id === correct)) {\n errors.correctResponse = 'The correct response must be one of the choices.';\n }\n\n if (question.hasAudio) {\n const hasAudioUrl = !!question.audioUrl?.trim();\n if (!hasAudioUrl) {\n errors.audioUrl = 'A playable audio URL is required when audio is enabled';\n }\n }\n\n const limits = question?.layoutLimits;\n if (limits && typeof limits === 'object') {\n const numericLimitKeys = [\n 'blankStandaloneWidthRem',\n 'blankWideWidthRem',\n 'blankUnderlineWidthPx',\n 'blankUnderlineWideWidthPx',\n 'horizontalChoiceWidthPx',\n 'horizontalChoiceWidthVw',\n 'horizontalChoiceTileMinHeightRem',\n 'horizontalChoiceContentMinHeightRem',\n 'choiceImageMaxHeightRem',\n 'choiceImageMaxWidthRem',\n 'listenButtonSizePx',\n 'stimulusMinColumnPx',\n 'textMinColumnPx',\n 'legendMaxChars',\n 'choiceGroupGapRem',\n 'choiceRowGapRem',\n 'toggleButtonGapRem',\n 'horizontalChoiceRadioTopMarginRem',\n 'audioBlankTemplateMarginTopRem',\n 'audioBlankTemplateMarginBottomRem',\n 'audioInstructionsMaxWidthPx',\n 'stimulusGridColumnGapRem',\n 'stimulusGridRowGapRem',\n 'stimulusSentenceMarginTopRem',\n 'stimulusChoicesMarginTopRem',\n 'tokenGridColumnGapRem',\n 'tokenGridRowGapRem',\n 'tokenTemplateMarginTopRem',\n 'tokenInlineTokenGapRem',\n 'tokenChoicesMarginTopRem',\n 'inlineGridColumnGapRem',\n 'inlineGridRowGapRem',\n 'inlineTemplateMarginTopRem',\n 'inlineChoicesMarginTopRem',\n ] as const;\n for (const key of numericLimitKeys) {\n if (limits[key] === undefined || limits[key] === null) continue;\n const value = Number(limits[key]);\n if (!Number.isFinite(value) || value <= 0) {\n errors.layoutLimits = `${key} must be a positive number when provided`;\n break;\n }\n }\n }\n\n return errors;\n};\n"],"mappings":"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"}
@@ -8602,57 +8602,49 @@ function computeLegendText(params) {
8602
8602
  }
8603
8603
  //#endregion
8604
8604
  //#region src/delivery/cqt-css/sel-r1-base.css?raw
8605
- var sel_r1_base_default = "/* Shared base styles for all sel_r1-*_plusggg variants.\n Applied via the data-mpb-variant=\"sel-r1-base\" style element.\n Every selector starts at .mc-populated-blank-root carrying any r1 variant class,\n so it cannot match outside this element. :where() keeps the attribute match out\n of the specificity, so each per-variant file still overrides a base rule with\n the selector shapes it already uses.\n Source: web-ItemBankViewer/learnosity/templates/Renaissance/r1.scss */\n\n/* r1.scss: Starial font throughout.\n font-size 14px mirrors the Learnosity host root (.lrn_widget) which sets the\n base for all em-scaled rules in r1.scss (e.g. .rli-r1-content-element { 1.9em })\n and any em-based author content (e.g. <span style=\"font-size:1.8em\">). Without\n this base PIE inherits the 16px browser default and renders ~14% larger than\n the LSY reference. See CONTOOL-2567. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) {\n font-family: Starial, Arial, sans-serif;\n font-size: 14px;\n}\n\n/* r1.scss: .rli-r1-content-element { font-size: 1.9em } wraps all stem tokens.\n Makes inline font-size spans in the template multiply on top of this base. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line {\n font-size: 1.9em;\n}\n\n/* r1.scss: .rli-r1-stem { display:flex; flex-direction:row; justify-content:center }\n r1.scss: .rli-r1-content-element { margin-right:2px; margin-left:2px } — 4px total gap.\n Apply the per-element margin to both classless tokens and the blank slot\n (which is the cloze equivalent) so the gap stacks with whitespace text-nodes\n between flex items the same way LSY does. Earlier this used column-gap:4px,\n but flex column-gap accumulates with the cloze margin and the implicit\n whitespace, producing a wider gap than LSY shows. See CONTOOL-2569. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line {\n display: flex;\n flex-direction: row;\n align-items: baseline;\n justify-content: center;\n}\n\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line p {\n margin: 0;\n display: flex;\n flex-direction: row;\n align-items: baseline;\n}\n\n/* r1.scss: .rli-r1-content-element { min-width:150px; max-width:150px; min-height:150px;\n line-height:89px; margin-right:2px; margin-left:2px } — wraps every stem token in\n LSY, giving each a 150px box with a 2px per-side margin. Applies to the token\n layouts, the same set that gets the feature button and the wide underline, so\n trailing tokens in audio_blank_only (gplusggg) match the blank's height the way\n LSY does. Stems on other layouts are prose, which the box would stack a word or\n two per line. Spans inside the blank are the answer's own markup, not stem tokens.\n The margin outranks token_sequence's span + span gap, so tokens sit LSY's 4px\n apart (CONTOOL-2569). */\n.mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(\n .layout-audio_blank_only,\n .layout-stimulus_image_blank,\n .layout-token_sequence\n )\n )\n .pie-template-line\n span:not([class]):not(.pie-blank-slot *) {\n min-width: 150px;\n max-width: 150px;\n min-height: 150px;\n line-height: 89px;\n margin-left: 2px;\n margin-right: 2px;\n}\n\n/* r1.scss: the content-element's wrapping flex line packs to the bottom of its box\n (align-content:flex-end), so a token's 89px line sits on the box's bottom edge\n whatever its font size; .rli-r1-stem { align-items:start } top-aligns the row.\n Every item in it has a fixed height (150px tokens and images, the 160px cloze),\n so the underline lands 4px under the tokens' bottom edge. */\n.mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line\n span:not([class]):not(.pie-blank-slot *) {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: baseline;\n align-content: flex-end;\n justify-content: center;\n margin-bottom: 8px;\n}\n\n.mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line,\n.mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line\n p {\n align-items: flex-start;\n}\n\n/* .pie-template-line's align-items:baseline (above) computes a shared baseline\n across all its flex items. .pie-blank-slot has no baseline-participating child\n of its own (.cloze-marker centers internally, align-items:center) — so it\n contributes a *synthesized* baseline back to the row, based on its own box\n geometry. That geometry swings wildly depending on the blank's content: an\n empty placeholder vs. a selected choice's image (up to 150x150). Every time a\n choice is selected the row's shared baseline is recomputed from that new\n geometry, visibly shifting sibling tokens like a leading/trailing stem word.\n align-self removes the blank from that shared computation entirely — its\n siblings' position no longer depends on what's currently displayed inside it.\n See CONTOOL-3159. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line .pie-blank-slot {\n align-self: flex-end;\n}\n\n/* r1.scss's .rli-r1-cloze and question.css's .rli-s3-cloze have no inline padding, so the\n 150px content-element spans the blank. ClozeMarker.svelte's 0.5rem would narrow an\n answer image to 134px or 144px. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line .pie-blank-slot {\n padding-inline: 0;\n}\n\n/* r1.scss: .rli-r1-cloze { height:160px; padding-bottom:4px; border-bottom:6px solid }\n — the blank in the token layouts is LSY's cloze box, top-aligned with the stem\n tokens (align-self overrides the flex-end above). A fixed height rather than a\n min-height keeps the empty and the filled blank at one geometry: the 150px image\n or content box below fits its 150px content area exactly, and the underline\n never moves on selection (CONTOOL-3159). .pie-blank-slot is border-box, as LSY's\n cloze is. McPopulatedBlank.svelte gives audio_blank_only the padding and the\n 6px underline; token_sequence takes both here. The 2px per-side margin matches\n LSY's .rli-r1-content-element rhythm (CONTOOL-2569). */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]):is(\n .layout-audio_blank_only,\n .layout-token_sequence\n )\n .pie-template-line\n .pie-blank-slot {\n align-self: flex-start;\n height: 160px;\n padding-bottom: 4px;\n margin-left: 2px;\n margin-right: 2px;\n}\n\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]).layout-token_sequence\n .pie-template-line\n .pie-blank-slot {\n min-width: 150px;\n max-width: 150px;\n line-height: 89px;\n}\n\n/* stimulus_image_blank keeps the 160px min-height blank slot McPopulatedBlank.svelte\n sets, with the same 2px per-side margin. See CONTOOL-2569. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]).layout-stimulus_image_blank\n .pie-template-line\n .pie-blank-slot {\n margin-left: 2px;\n margin-right: 2px;\n}\n\n/* cloze.js: the cloze holds an .rli-r1-content-element around the answer's markup,\n so the answer's line packs to the bottom of a 150px box the way a stem token's\n does. The box's 8px bottom margin, centred in the cloze's 150px content area,\n lifts it 4px, as it does in LSY. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]):is(\n .layout-audio_blank_only,\n .layout-token_sequence\n) .pie-blank-value {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: baseline;\n align-content: flex-end;\n justify-content: center;\n min-height: 150px;\n line-height: 89px;\n margin-bottom: 8px;\n}\n\n/* An imageUrl answer is .pie-blank-image directly in the blank, with no\n .pie-blank-value around it; the same margin puts it where a labelHtml image sits. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]):is(\n .layout-audio_blank_only,\n .layout-token_sequence\n) .pie-blank-image {\n margin-bottom: 8px;\n}\n\n/* Classless answer spans in the s3 blank keep line-height:normal, which outranks\n sel-r1-s3.css's 1.1. See CONTOOL-3159. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]).layout-stimulus_image_blank\n .pie-blank-value\n span:not([class]) {\n line-height: normal;\n}\n\n/* r1.scss: .rli-r1-content-element { font-size:1.9em; line-height:89px } on distractors */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-choice-label {\n font-size: 1.9em;\n line-height: 89px;\n}\n\n/* r1.scss: .rli-r1-content-element { min-width:150px; max-width:150px;\n min-height:150px } wraps every image (stem token + distractor) and forces\n the content box to 150×150. LSY's host also rewrites <img width/height>\n to 150 server-side. PIE keeps the authored attributes (typically 108×108),\n so without an explicit override images render ~28% smaller than LSY.\n See CONTOOL-2570. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line img,\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-choice-label img {\n width: 150px;\n height: 150px;\n}\n\n/* r1.scss: .rli-r1-stem and .rli-r1-distractors are flex-direction:column and become\n rows only in its @media (min-width:850px), so below it every stem token, the cloze\n and each distractor stack at the left edge (.rli-r1-stem { align-items:start }).\n Each token's 8px bottom margin spaces the stack. */\n@media not all and (min-width: 850px) {\n .mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line,\n .mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line\n p {\n flex-direction: column;\n align-items: flex-start;\n }\n\n .mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-choices.choices-horizontal {\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-start;\n --mpb-choice-width-vw: 100vw;\n }\n}\n";
8605
+ var sel_r1_base_default = "/* Shared base styles for all sel_r1-*_plusggg variants.\n Applied via the data-mpb-variant=\"sel-r1-base\" style element.\n Every selector starts at .mc-populated-blank-root carrying any r1 variant class,\n so it cannot match outside this element. :where() keeps the attribute match out\n of the specificity, so each per-variant file still overrides a base rule with\n the selector shapes it already uses. Comments cite the CQT template's r1.scss. */\n\n/* r1.scss: Starial font throughout.\n font-size 14px mirrors the CQT host root, which sets the\n base for all em-scaled rules in r1.scss (e.g. .rli-r1-content-element { 1.9em })\n and any em-based author content (e.g. <span style=\"font-size:1.8em\">). Without\n this base PIE inherits the 16px browser default and renders ~14% larger than\n the CQT reference. See CONTOOL-2567. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) {\n font-family: Starial, Arial, sans-serif;\n font-size: 14px;\n}\n\n/* r1.scss: .rli-r1-content-element { font-size: 1.9em } wraps all stem tokens.\n Makes inline font-size spans in the template multiply on top of this base. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line {\n font-size: 1.9em;\n}\n\n/* r1.scss: .rli-r1-stem { display:flex; flex-direction:row; justify-content:center }\n r1.scss: .rli-r1-content-element { margin-right:2px; margin-left:2px } — 4px total gap.\n Apply the per-element margin to both classless tokens and the blank slot\n (which is the cloze equivalent) so the gap stacks with whitespace text-nodes\n between flex items the same way the CQT does. Earlier this used column-gap:4px,\n but flex column-gap accumulates with the cloze margin and the implicit\n whitespace, producing a wider gap than the CQT shows. See CONTOOL-2569. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line {\n display: flex;\n flex-direction: row;\n align-items: baseline;\n justify-content: center;\n}\n\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line p {\n margin: 0;\n display: flex;\n flex-direction: row;\n align-items: baseline;\n}\n\n/* r1.scss: .rli-r1-content-element { min-width:150px; max-width:150px; min-height:150px;\n line-height:89px; margin-right:2px; margin-left:2px } — wraps every stem token in\n the CQT, giving each a 150px box with a 2px per-side margin. Applies to the token\n layouts, the same set that gets the feature button and the wide underline, so\n trailing tokens in audio_blank_only (gplusggg) match the blank's height the way\n the CQT does. Stems on other layouts are prose, which the box would stack a word or\n two per line. Spans inside the blank are the answer's own markup, not stem tokens.\n The margin outranks token_sequence's span + span gap, so tokens sit 4px apart,\n as in the CQT (CONTOOL-2569). */\n.mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(\n .layout-audio_blank_only,\n .layout-stimulus_image_blank,\n .layout-token_sequence\n )\n )\n .pie-template-line\n span:not([class]):not(.pie-blank-slot *) {\n min-width: 150px;\n max-width: 150px;\n min-height: 150px;\n line-height: 89px;\n margin-left: 2px;\n margin-right: 2px;\n}\n\n/* r1.scss: the content-element's wrapping flex line packs to the bottom of its box\n (align-content:flex-end), so a token's 89px line sits on the box's bottom edge\n whatever its font size; .rli-r1-stem { align-items:start } top-aligns the row.\n Every item in it has a fixed height (150px tokens and images, the 160px cloze),\n so the underline lands 4px under the tokens' bottom edge. */\n.mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line\n span:not([class]):not(.pie-blank-slot *) {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: baseline;\n align-content: flex-end;\n justify-content: center;\n margin-bottom: 8px;\n}\n\n.mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line,\n.mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line\n p {\n align-items: flex-start;\n}\n\n/* .pie-template-line's align-items:baseline (above) computes a shared baseline\n across all its flex items. .pie-blank-slot has no baseline-participating child\n of its own (.cloze-marker centers internally, align-items:center) — so it\n contributes a *synthesized* baseline back to the row, based on its own box\n geometry. That geometry swings wildly depending on the blank's content: an\n empty placeholder vs. a selected choice's image (up to 150x150). Every time a\n choice is selected the row's shared baseline is recomputed from that new\n geometry, visibly shifting sibling tokens like a leading/trailing stem word.\n align-self removes the blank from that shared computation entirely — its\n siblings' position no longer depends on what's currently displayed inside it.\n See CONTOOL-3159. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line .pie-blank-slot {\n align-self: flex-end;\n}\n\n/* r1.scss's .rli-r1-cloze and question.css's .rli-s3-cloze have no inline padding, so the\n 150px content-element spans the blank. ClozeMarker.svelte's 0.5rem would narrow an\n answer image to 134px or 144px. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line .pie-blank-slot {\n padding-inline: 0;\n}\n\n/* r1.scss: .rli-r1-cloze { height:160px; padding-bottom:4px; border-bottom:6px solid }\n — the blank in the token layouts is the CQT's cloze box, top-aligned with the stem\n tokens (align-self overrides the flex-end above). A fixed height rather than a\n min-height keeps the empty and the filled blank at one geometry: the 150px image\n or content box below fits its 150px content area exactly, and the underline\n never moves on selection (CONTOOL-3159). .pie-blank-slot is border-box, as the CQT's\n cloze is. McPopulatedBlank.svelte gives audio_blank_only the padding and the\n 6px underline; token_sequence takes both here. The 2px per-side margin matches\n the CQT's .rli-r1-content-element rhythm (CONTOOL-2569). */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]):is(\n .layout-audio_blank_only,\n .layout-token_sequence\n )\n .pie-template-line\n .pie-blank-slot {\n align-self: flex-start;\n height: 160px;\n padding-bottom: 4px;\n margin-left: 2px;\n margin-right: 2px;\n}\n\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]).layout-token_sequence\n .pie-template-line\n .pie-blank-slot {\n min-width: 150px;\n max-width: 150px;\n line-height: 89px;\n}\n\n/* stimulus_image_blank keeps the 160px min-height blank slot McPopulatedBlank.svelte\n sets, with the same 2px per-side margin. See CONTOOL-2569. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]).layout-stimulus_image_blank\n .pie-template-line\n .pie-blank-slot {\n margin-left: 2px;\n margin-right: 2px;\n}\n\n/* cloze.js: the cloze holds an .rli-r1-content-element around the answer's markup,\n so the answer's line packs to the bottom of a 150px box the way a stem token's\n does. The box's 8px bottom margin, centred in the cloze's 150px content area,\n lifts it 4px, as it does in the CQT. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]):is(\n .layout-audio_blank_only,\n .layout-token_sequence\n) .pie-blank-value {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: baseline;\n align-content: flex-end;\n justify-content: center;\n min-height: 150px;\n line-height: 89px;\n margin-bottom: 8px;\n}\n\n/* An imageUrl answer is .pie-blank-image directly in the blank, with no\n .pie-blank-value around it; the same margin puts it where a labelHtml image sits. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]):is(\n .layout-audio_blank_only,\n .layout-token_sequence\n) .pie-blank-image {\n margin-bottom: 8px;\n}\n\n/* Classless answer spans in the s3 blank keep line-height:normal, which outranks\n sel-r1-s3.css's 1.1. See CONTOOL-3159. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]).layout-stimulus_image_blank\n .pie-blank-value\n span:not([class]) {\n line-height: normal;\n}\n\n/* r1.scss: .rli-r1-content-element { font-size:1.9em; line-height:89px } on distractors */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-choice-label {\n font-size: 1.9em;\n line-height: 89px;\n}\n\n/* r1.scss: .rli-r1-content-element { min-width:150px; max-width:150px;\n min-height:150px } wraps every image (stem token + distractor) and forces\n the content box to 150×150. The CQT host also rewrites <img width/height>\n to 150 server-side. PIE keeps the authored attributes (typically 108×108),\n so without an explicit override images render ~28% smaller than in the CQT.\n See CONTOOL-2570. */\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-template-line img,\n.mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-choice-label img {\n width: 150px;\n height: 150px;\n}\n\n/* r1.scss: .rli-r1-stem and .rli-r1-distractors are flex-direction:column and become\n rows only in its @media (min-width:850px), so below it every stem token, the cloze\n and each distractor stack at the left edge (.rli-r1-stem { align-items:start }).\n Each token's 8px bottom margin spaces the stack. */\n@media not all and (min-width: 850px) {\n .mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line,\n .mc-populated-blank-root:where(\n [class*=\"variant-sel-r1-\"]:is(.layout-audio_blank_only, .layout-token_sequence)\n )\n .pie-template-line\n p {\n flex-direction: column;\n align-items: flex-start;\n }\n\n .mc-populated-blank-root:where([class*=\"variant-sel-r1-\"]) .pie-choices.choices-horizontal {\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-start;\n --mpb-choice-width-vw: 100vw;\n }\n}\n";
8606
8606
  //#endregion
8607
8607
  //#region src/delivery/variant-css-map.ts
8608
8608
  var VARIANT_BY_CUSTOM_TYPE = {
8609
8609
  "sel_r1-_plusggg": {
8610
8610
  variantId: "sel-r1-plusggg",
8611
8611
  variantClass: "variant-sel-r1-plusggg",
8612
- sourceUrl: "https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-_plusggg/0.0.1/question.css",
8613
- cssText: "/* Source baseline: https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-_plusggg/0.0.1/question.css */\n/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-plusggg {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n --mpb-choice-group-gap: 1rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n}\n\n.mc-populated-blank-root.variant-sel-r1-plusggg {\n --mpb-choice-radio-padding: 20px;\n}\n"
8612
+ cssText: "/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-plusggg {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n --mpb-choice-group-gap: 1rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n}\n\n.mc-populated-blank-root.variant-sel-r1-plusggg {\n --mpb-choice-radio-padding: 20px;\n}\n"
8614
8613
  },
8615
8614
  "sr-vic": {
8616
8615
  variantId: "sr-vic",
8617
8616
  variantClass: "variant-sr-vic",
8618
- sourceUrl: "https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sr-vic/0.0.1/question.css",
8619
- cssText: "/* Source baseline: https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sr-vic/0.0.1/question.css */\n/* Reference layout: web-ItemBankViewer/learnosity/templates/Renaissance/vic.scss */\n.mc-populated-blank-root.variant-sr-vic {\n font-family: Starial, Arial, sans-serif;\n}\n\n.mc-populated-blank-root.variant-sr-vic .pie-template-line,\n.mc-populated-blank-root.variant-sr-vic .pie-blank-value,\n.mc-populated-blank-root.variant-sr-vic .pie-choice-label {\n line-height: 123%;\n}\n\n/* vic.scss: .rli-vic-answer uses display:flex so sentence and cloze stay on\n one line. The template HTML wraps text in <p>, which is block by default and\n breaks to a new line. Making <p> inline keeps the sentence and blank inline. */\n.mc-populated-blank-root.variant-sr-vic .pie-template-line p {\n display: inline;\n margin: 0;\n}\n\n/* LSY .rli-vic-answer has no text-align rule and inherits start; PIE's\n inline_sentence layout sets text-align:center on .template-line via a\n Svelte-scoped selector with specificity (0,4,0), so we need to match or\n exceed that. Pin sr-vic back to start so wrapped lines stay left-aligned.\n Same root cause as sel-vic. See CONTOOL-2573. */\n.mc-populated-blank-root.variant-sr-vic.layout-inline_sentence .pie-template-line.template-line {\n text-align: start;\n}\n\n/* vic.scss: .rli-vic-cloze { margin-left:6px; margin-right:6px }, with no inline\n padding, where ClozeMarker.svelte gives the blank 0.5rem. See CONTOOL-2572. */\n.mc-populated-blank-root.variant-sr-vic .pie-template-line .pie-blank-slot {\n margin-left: 6px;\n margin-right: 6px;\n padding-inline: 0;\n}\n\n/* vic.scss: .rli-vic-cloze > .rli-vic-content-element { color: #cc3333 }\n The red holds AA only on LSY's white page (5.1:1), so the answer carries that\n surface: on a dark host it is 3.08:1. light-dark() would miss pie-players'\n colour schemes, which declare no color-scheme. */\n.mc-populated-blank-root.variant-sr-vic .pie-blank-value {\n color: #cc3333;\n background-color: #fff;\n}\n\n/* vic.scss: .rli-vic-selected { background-color: #fcfcd3 } applied to the\n whole distractor row (radio + label), not just the label wrapper. */\n.mc-populated-blank-root.variant-sr-vic .pie-choice.is-selected {\n background-color: #fcfcd3;\n color: #000;\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sr-vic .pie-choice.is-selected .pie-choice-label-wrap {\n background-color: transparent;\n color: inherit;\n}\n\n/* vic.scss: distractor label text sits to the right of the radio — no centering. */\n.mc-populated-blank-root.variant-sr-vic .pie-choice-label {\n text-align: left;\n justify-content: flex-start;\n}\n\n/* vic.scss: .rli-vic-distractor { align-items:center; margin-right:10px } and\n .rli-vic-distractor > input { margin:20px }. The radio sits 20px from the row edge and\n from its label, centred on the label, and its margins set the row pitch: the rows and\n the radio and label have no gap between them. */\n.mc-populated-blank-root.variant-sr-vic .pie-choices.choices-vertical {\n --mpb-choice-group-gap: 0;\n}\n\n.mc-populated-blank-root.variant-sr-vic .pie-choice:not(.pie-choice-horizontal) {\n --mpb-choice-row-gap: 0;\n align-items: center;\n margin-right: 10px;\n}\n\n.mc-populated-blank-root.variant-sr-vic .pie-choice-radio-inline {\n margin: 20px;\n}\n\n/* vic.scss: .rli-vic-distractor:hover { background-color: #F2F2F2 } applied to\n the full row element, not only the inner label wrapper. */\n.mc-populated-blank-root.variant-sr-vic .pie-choice:not(.pie-choice-horizontal):hover {\n background-color: #f2f2f2;\n color: #000;\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sr-vic\n .pie-choice:not(.pie-choice-horizontal):hover\n .pie-choice-label-wrap {\n background-color: transparent;\n color: inherit;\n}\n\n/* Selected state must win over hover — yellow persists when hovering a chosen row. */\n.mc-populated-blank-root.variant-sr-vic .pie-choice:not(.pie-choice-horizontal).is-selected:hover {\n background-color: #fcfcd3;\n color: #000;\n}\n"
8617
+ cssText: ".mc-populated-blank-root.variant-sr-vic {\n font-family: Starial, Arial, sans-serif;\n}\n\n.mc-populated-blank-root.variant-sr-vic .pie-template-line,\n.mc-populated-blank-root.variant-sr-vic .pie-blank-value,\n.mc-populated-blank-root.variant-sr-vic .pie-choice-label {\n line-height: 123%;\n}\n\n/* vic.scss: .rli-vic-answer uses display:flex so sentence and cloze stay on\n one line. The template HTML wraps text in <p>, which is block by default and\n breaks to a new line. Making <p> inline keeps the sentence and blank inline. */\n.mc-populated-blank-root.variant-sr-vic .pie-template-line p {\n display: inline;\n margin: 0;\n}\n\n/* The CQT's .rli-vic-answer has no text-align rule and inherits start; PIE's\n inline_sentence layout sets text-align:center on .template-line via a\n Svelte-scoped selector with specificity (0,4,0), so we need to match or\n exceed that. Pin sr-vic back to start so wrapped lines stay left-aligned.\n Same root cause as sel-vic. See CONTOOL-2573. */\n.mc-populated-blank-root.variant-sr-vic.layout-inline_sentence .pie-template-line.template-line {\n text-align: start;\n}\n\n/* vic.scss: .rli-vic-cloze { margin-left:6px; margin-right:6px }, with no inline\n padding, where ClozeMarker.svelte gives the blank 0.5rem. See CONTOOL-2572. */\n.mc-populated-blank-root.variant-sr-vic .pie-template-line .pie-blank-slot {\n margin-left: 6px;\n margin-right: 6px;\n padding-inline: 0;\n}\n\n/* vic.scss: .rli-vic-cloze > .rli-vic-content-element { color: #cc3333 }\n The red holds AA only on the CQT's white page (5.1:1), so the answer carries that\n surface: on a dark host it is 3.08:1. light-dark() would miss pie-players'\n colour schemes, which declare no color-scheme. */\n.mc-populated-blank-root.variant-sr-vic .pie-blank-value {\n color: #cc3333;\n background-color: #fff;\n}\n\n/* vic.scss: .rli-vic-selected { background-color: #fcfcd3 } applied to the\n whole distractor row (radio + label), not just the label wrapper. */\n.mc-populated-blank-root.variant-sr-vic .pie-choice.is-selected {\n background-color: #fcfcd3;\n color: #000;\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sr-vic .pie-choice.is-selected .pie-choice-label-wrap {\n background-color: transparent;\n color: inherit;\n}\n\n/* vic.scss: distractor label text sits to the right of the radio — no centering. */\n.mc-populated-blank-root.variant-sr-vic .pie-choice-label {\n text-align: left;\n justify-content: flex-start;\n}\n\n/* vic.scss: .rli-vic-distractor { align-items:center; margin-right:10px } and\n .rli-vic-distractor > input { margin:20px }. The radio sits 20px from the row edge and\n from its label, centred on the label, and its margins set the row pitch: the rows and\n the radio and label have no gap between them. */\n.mc-populated-blank-root.variant-sr-vic .pie-choices.choices-vertical {\n --mpb-choice-group-gap: 0;\n}\n\n.mc-populated-blank-root.variant-sr-vic .pie-choice:not(.pie-choice-horizontal) {\n --mpb-choice-row-gap: 0;\n align-items: center;\n margin-right: 10px;\n}\n\n.mc-populated-blank-root.variant-sr-vic .pie-choice-radio-inline {\n margin: 20px;\n}\n\n/* vic.scss: .rli-vic-distractor:hover { background-color: #F2F2F2 } applied to\n the full row element, not only the inner label wrapper. */\n.mc-populated-blank-root.variant-sr-vic .pie-choice:not(.pie-choice-horizontal):hover {\n background-color: #f2f2f2;\n color: #000;\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sr-vic\n .pie-choice:not(.pie-choice-horizontal):hover\n .pie-choice-label-wrap {\n background-color: transparent;\n color: inherit;\n}\n\n/* Selected state must win over hover — yellow persists when hovering a chosen row. */\n.mc-populated-blank-root.variant-sr-vic .pie-choice:not(.pie-choice-horizontal).is-selected:hover {\n background-color: #fcfcd3;\n color: #000;\n}\n"
8620
8618
  },
8621
8619
  sel_vic: {
8622
8620
  variantId: "sel-vic",
8623
8621
  variantClass: "variant-sel-vic",
8624
- sourceUrl: "https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_vic/0.0.1/question.css",
8625
- cssText: "/* Source baseline: https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_vic/0.0.1/question.css */\n/* Reference layout: web-ItemBankViewer/learnosity/templates/Renaissance/sel_vic/scss/main.scss + vic.scss */\n.mc-populated-blank-root.variant-sel-vic {\n font-family: Starial, Arial, sans-serif;\n /* LSY chain: .lrn_widget (14px host) → .sel-vic { 1.5em } = 21px.\n PIE collapses both LSY layers into this single variant-root selector, so\n the resolved value is set directly. See CONTOOL-2567. */\n font-size: 21px;\n max-width: 800px;\n margin-inline: auto;\n}\n\n.mc-populated-blank-root.variant-sel-vic .pie-template-line,\n.mc-populated-blank-root.variant-sel-vic .pie-blank-value,\n.mc-populated-blank-root.variant-sel-vic .pie-choice-label {\n line-height: 123%;\n}\n\n/* sel_vic/main.scss: template left ~75%, audio right ~25% on the same row.\n Svelte scoping prevents the component-internal grid-area rule from reaching\n .audio-container inside AudioPlayer, so we override it here via the global\n .pie-audio-container class which is not subject to scope hashing. */\n.mc-populated-blank-root.variant-sel-vic .pie-audio-container {\n grid-area: audio;\n margin: 0;\n justify-self: end;\n align-self: start;\n min-height: 128px;\n}\n\n/* vic.scss: sentence and cloze sit inline — the <p> wrapper must not break to\n a new line before the blank slot. */\n.mc-populated-blank-root.variant-sel-vic .pie-template-line p {\n display: inline;\n margin: 0;\n}\n\n/* LSY .rli-vic-answer has no text-align rule and inherits start; PIE's\n inline_sentence layout sets text-align:center on .template-line via a\n Svelte-scoped selector with specificity (0,4,0), so we need to match or\n exceed that. Pin sel-vic back to start so wrapped lines stay left-aligned.\n See CONTOOL-2573.\n vic.scss: .rli-vic-stimulus { align-items:flex-start } also starts the\n sentence at the content's left edge, where the same layout centres it in its\n grid column (justify-self:center). */\n.mc-populated-blank-root.variant-sel-vic.layout-inline_sentence .pie-template-line.template-line {\n text-align: start;\n justify-self: start;\n}\n\n/* vic.scss: @media (min-width:850px) { .rli-vic-stimulus { margin-left:20px;\n margin-right:20px } } — the sentence sits 20px in from the distractors. */\n@media (min-width: 850px) {\n .mc-populated-blank-root.variant-sel-vic.layout-inline_sentence .pie-template-line.template-line {\n margin-left: 20px;\n margin-right: 20px;\n }\n}\n\n/* sel_vic/main.scss: .sel-vic .rli-vic-stimulus { min-height:128px }. From 850px up the\n listen button beside the sentence gives the row that height; below it the button\n moves above, and the sentence keeps the 128px on its own. */\n@media not all and (min-width: 850px) {\n .mc-populated-blank-root.variant-sel-vic.layout-inline_sentence .pie-template-line.template-line {\n min-height: 128px;\n }\n}\n\n/* vic.scss: .rli-vic-cloze { margin-left: 6px; margin-right: 6px } gives the\n cloze breathing room from adjacent text or punctuation. The shared blank slot\n has no inline margin, so set it here so the cloze→trailing-punctuation gap\n matches LSY. See CONTOOL-2572. The cloze has no inline padding either, where\n ClozeMarker.svelte gives the blank 0.5rem. */\n.mc-populated-blank-root.variant-sel-vic .pie-template-line .pie-blank-slot {\n margin-left: 6px;\n margin-right: 6px;\n padding-inline: 0;\n}\n\n/* vic.scss: .rli-vic-cloze > .rli-vic-content-element { color: #cc3333 }\n The red holds AA only on LSY's white page (5.1:1), so the answer carries that\n surface: on a dark host it is 3.08:1. light-dark() would miss pie-players'\n colour schemes, which declare no color-scheme. */\n.mc-populated-blank-root.variant-sel-vic .pie-blank-value {\n color: #cc3333;\n background-color: #fff;\n}\n\n/* sel_vic/main.scss: .sel-vic { max-width: 800px } — the fieldset/choices must\n also be constrained so rows don't span beyond 800px. */\n.mc-populated-blank-root.variant-sel-vic .pie-choices-fieldset {\n max-width: 800px;\n width: 100%;\n}\n\n/* vic.scss: distractor label text sits to the right of the radio — no centering. */\n.mc-populated-blank-root.variant-sel-vic .pie-choice-label {\n text-align: left;\n justify-content: flex-start;\n}\n\n/* vic.scss: .rli-vic-distractor { align-items:center; margin-right:10px } and\n .rli-vic-distractor > input { margin:20px }. The radio sits 20px from the row edge and\n from its label, centred on the label, and its margins set the row pitch: the rows and\n the radio and label have no gap between them. */\n.mc-populated-blank-root.variant-sel-vic .pie-choices.choices-vertical {\n --mpb-choice-group-gap: 0;\n}\n\n.mc-populated-blank-root.variant-sel-vic .pie-choice:not(.pie-choice-horizontal) {\n --mpb-choice-row-gap: 0;\n align-items: center;\n margin-right: 10px;\n}\n\n.mc-populated-blank-root.variant-sel-vic .pie-choice-radio-inline {\n margin: 20px;\n}\n\n/* vic.scss: .rli-vic-selected { background-color: #fcfcd3 } on the full row. */\n.mc-populated-blank-root.variant-sel-vic .pie-choice.is-selected {\n background-color: #fcfcd3;\n color: #000;\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sel-vic .pie-choice.is-selected .pie-choice-label-wrap {\n background-color: transparent;\n color: inherit;\n}\n\n/* Selected state must win over hover. */\n.mc-populated-blank-root.variant-sel-vic .pie-choice:not(.pie-choice-horizontal).is-selected:hover {\n background-color: #fcfcd3;\n color: #000;\n}\n\n/* vic.scss: .rli-vic-distractor:hover { background-color: #F2F2F2 } on the full row. */\n.mc-populated-blank-root.variant-sel-vic .pie-choice:not(.pie-choice-horizontal):hover {\n background-color: #f2f2f2;\n color: #000;\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sel-vic\n .pie-choice:not(.pie-choice-horizontal):hover\n .pie-choice-label-wrap {\n background-color: transparent;\n color: inherit;\n}\n"
8622
+ cssText: ".mc-populated-blank-root.variant-sel-vic {\n font-family: Starial, Arial, sans-serif;\n /* CQT chain: 14px host → .sel-vic { 1.5em } = 21px.\n PIE collapses both CQT layers into this single variant-root selector, so\n the resolved value is set directly. See CONTOOL-2567. */\n font-size: 21px;\n max-width: 800px;\n margin-inline: auto;\n}\n\n.mc-populated-blank-root.variant-sel-vic .pie-template-line,\n.mc-populated-blank-root.variant-sel-vic .pie-blank-value,\n.mc-populated-blank-root.variant-sel-vic .pie-choice-label {\n line-height: 123%;\n}\n\n/* sel_vic/main.scss: template left ~75%, audio right ~25% on the same row.\n Svelte scoping prevents the component-internal grid-area rule from reaching\n .audio-container inside AudioPlayer, so we override it here via the global\n .pie-audio-container class which is not subject to scope hashing. */\n.mc-populated-blank-root.variant-sel-vic .pie-audio-container {\n grid-area: audio;\n margin: 0;\n justify-self: end;\n align-self: start;\n min-height: 128px;\n}\n\n/* vic.scss: sentence and cloze sit inline — the <p> wrapper must not break to\n a new line before the blank slot. */\n.mc-populated-blank-root.variant-sel-vic .pie-template-line p {\n display: inline;\n margin: 0;\n}\n\n/* The CQT's .rli-vic-answer has no text-align rule and inherits start; PIE's\n inline_sentence layout sets text-align:center on .template-line via a\n Svelte-scoped selector with specificity (0,4,0), so we need to match or\n exceed that. Pin sel-vic back to start so wrapped lines stay left-aligned.\n See CONTOOL-2573.\n vic.scss: .rli-vic-stimulus { align-items:flex-start } also starts the\n sentence at the content's left edge, where the same layout centres it in its\n grid column (justify-self:center). */\n.mc-populated-blank-root.variant-sel-vic.layout-inline_sentence .pie-template-line.template-line {\n text-align: start;\n justify-self: start;\n}\n\n/* vic.scss: @media (min-width:850px) { .rli-vic-stimulus { margin-left:20px;\n margin-right:20px } } — the sentence sits 20px in from the distractors. */\n@media (min-width: 850px) {\n .mc-populated-blank-root.variant-sel-vic.layout-inline_sentence .pie-template-line.template-line {\n margin-left: 20px;\n margin-right: 20px;\n }\n}\n\n/* sel_vic/main.scss: .sel-vic .rli-vic-stimulus { min-height:128px }. From 850px up the\n listen button beside the sentence gives the row that height; below it the button\n moves above, and the sentence keeps the 128px on its own. */\n@media not all and (min-width: 850px) {\n .mc-populated-blank-root.variant-sel-vic.layout-inline_sentence .pie-template-line.template-line {\n min-height: 128px;\n }\n}\n\n/* vic.scss: .rli-vic-cloze { margin-left: 6px; margin-right: 6px } gives the\n cloze breathing room from adjacent text or punctuation. The shared blank slot\n has no inline margin, so set it here so the cloze→trailing-punctuation gap\n matches the CQT. See CONTOOL-2572. The cloze has no inline padding either, where\n ClozeMarker.svelte gives the blank 0.5rem. */\n.mc-populated-blank-root.variant-sel-vic .pie-template-line .pie-blank-slot {\n margin-left: 6px;\n margin-right: 6px;\n padding-inline: 0;\n}\n\n/* vic.scss: .rli-vic-cloze > .rli-vic-content-element { color: #cc3333 }\n The red holds AA only on the CQT's white page (5.1:1), so the answer carries that\n surface: on a dark host it is 3.08:1. light-dark() would miss pie-players'\n colour schemes, which declare no color-scheme. */\n.mc-populated-blank-root.variant-sel-vic .pie-blank-value {\n color: #cc3333;\n background-color: #fff;\n}\n\n/* sel_vic/main.scss: .sel-vic { max-width: 800px } — the fieldset/choices must\n also be constrained so rows don't span beyond 800px. */\n.mc-populated-blank-root.variant-sel-vic .pie-choices-fieldset {\n max-width: 800px;\n width: 100%;\n}\n\n/* vic.scss: distractor label text sits to the right of the radio — no centering. */\n.mc-populated-blank-root.variant-sel-vic .pie-choice-label {\n text-align: left;\n justify-content: flex-start;\n}\n\n/* vic.scss: .rli-vic-distractor { align-items:center; margin-right:10px } and\n .rli-vic-distractor > input { margin:20px }. The radio sits 20px from the row edge and\n from its label, centred on the label, and its margins set the row pitch: the rows and\n the radio and label have no gap between them. */\n.mc-populated-blank-root.variant-sel-vic .pie-choices.choices-vertical {\n --mpb-choice-group-gap: 0;\n}\n\n.mc-populated-blank-root.variant-sel-vic .pie-choice:not(.pie-choice-horizontal) {\n --mpb-choice-row-gap: 0;\n align-items: center;\n margin-right: 10px;\n}\n\n.mc-populated-blank-root.variant-sel-vic .pie-choice-radio-inline {\n margin: 20px;\n}\n\n/* vic.scss: .rli-vic-selected { background-color: #fcfcd3 } on the full row. */\n.mc-populated-blank-root.variant-sel-vic .pie-choice.is-selected {\n background-color: #fcfcd3;\n color: #000;\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sel-vic .pie-choice.is-selected .pie-choice-label-wrap {\n background-color: transparent;\n color: inherit;\n}\n\n/* Selected state must win over hover. */\n.mc-populated-blank-root.variant-sel-vic .pie-choice:not(.pie-choice-horizontal).is-selected:hover {\n background-color: #fcfcd3;\n color: #000;\n}\n\n/* vic.scss: .rli-vic-distractor:hover { background-color: #F2F2F2 } on the full row. */\n.mc-populated-blank-root.variant-sel-vic .pie-choice:not(.pie-choice-horizontal):hover {\n background-color: #f2f2f2;\n color: #000;\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sel-vic\n .pie-choice:not(.pie-choice-horizontal):hover\n .pie-choice-label-wrap {\n background-color: transparent;\n color: inherit;\n}\n"
8626
8623
  },
8627
8624
  "sel_r1-_gplusggg": {
8628
8625
  variantId: "sel-r1-gplusggg",
8629
8626
  variantClass: "variant-sel-r1-gplusggg",
8630
- sourceUrl: "https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-_gplusggg/0.0.1/question.css",
8631
- cssText: "/* Source baseline: https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-_gplusggg/0.0.1/question.css */\n/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-gplusggg {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n --mpb-choice-group-gap: 1rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n}\n"
8627
+ cssText: "/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-gplusggg {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n --mpb-choice-group-gap: 1rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n}\n"
8632
8628
  },
8633
8629
  "sel_r1-g_plusggg": {
8634
8630
  variantId: "sel-r1-g-stem",
8635
8631
  variantClass: "variant-sel-r1-g-stem",
8636
- sourceUrl: "https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-g_plusggg/0.0.1/question.css",
8637
- cssText: "/* Source baseline: https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-g_plusggg/0.0.1/question.css */\n/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-g-stem {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n /* r1.scss: .rli-r1-cloze { border-bottom: 6px solid } */\n --mpb-blank-underline-wide-width: 6px;\n}\n"
8632
+ cssText: "/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-g-stem {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n /* r1.scss: .rli-r1-cloze { border-bottom: 6px solid } */\n --mpb-blank-underline-wide-width: 6px;\n}\n"
8638
8633
  },
8639
8634
  "sel_r1-gg_plusggg": {
8640
8635
  variantId: "sel-r1-gg-plus",
8641
8636
  variantClass: "variant-sel-r1-gg-plus",
8642
- sourceUrl: "https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-gg_plusggg/0.0.1/question.css",
8643
- cssText: "/* Source baseline: https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-gg_plusggg/0.0.1/question.css */\n/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-gg-plus {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n}\n"
8637
+ cssText: "/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-gg-plus {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n}\n"
8644
8638
  },
8645
8639
  "sel_r1-_ggplusggg": {
8646
8640
  variantId: "sel-r1-ggplus",
8647
8641
  variantClass: "variant-sel-r1-ggplus",
8648
- sourceUrl: "https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-_ggplusggg/0.0.1/question.css",
8649
- cssText: "/* Source baseline: https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-_ggplusggg/0.0.1/question.css */\n/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-ggplus {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n}\n"
8642
+ cssText: "/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-ggplus {\n max-width: 875px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n /* A fixed light surface takes fixed dark text: the scheme text colour is near-white in dark schemes. */\n --mpb-choice-hover-bg: #f2f2f2;\n --mpb-choice-hover-color: #000;\n --mpb-choice-selected-bg: #fcfcd3;\n --mpb-choice-selected-color: #000;\n}\n"
8650
8643
  },
8651
8644
  "sel_r1-s3_plusggg": {
8652
8645
  variantId: "sel-r1-s3",
8653
8646
  variantClass: "variant-sel-r1-s3",
8654
- sourceUrl: "https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-s3_plusggg/0.0.1/question.css",
8655
- cssText: "/* Source baseline: https://global-pr-ibvcustomfiles.renaissance-go.com/learnosity/sel_r1-s3_plusggg/0.0.1/question.css */\n/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-s3 {\n max-width: 860px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n --mpb-choice-group-gap: 0.35rem;\n --mpb-choice-row-gap: 0.35rem;\n --mpb-horizontal-choice-radio-top-margin: 0.25rem;\n --mpb-stimulus-min-column: 240px;\n --mpb-stimulus-grid-column-gap: 1.1rem;\n --mpb-stimulus-choices-margin-top: 0.85rem;\n --mpb-audio-blank-template-margin-bottom: 0.9rem;\n --mpb-blank-wide-width: 6.8rem;\n}\n\n/* sel_r1-s3_plusggg/question.css does not set font-weight on distractors or\n cloze content-element, so they inherit 400 from the host root. The earlier\n font-weight:500 override caused PIE to render visibly heavier than LSY for\n the same author content. See CONTOOL-2571. */\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-label,\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-label :is(span, p, div) {\n font-size: 1.9em;\n line-height: 89px;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-blank-value,\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-blank-value :is(span, p, div) {\n line-height: 1.1;\n white-space: nowrap;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-horizontal .pie-choice-tile {\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank {\n grid-template-columns: minmax(220px, auto) auto auto;\n grid-template-areas:\n \"sentence template audio\"\n \"choices choices choices\";\n justify-content: center;\n /* question.css: .rli-s3-cloze-container { justify-content:end } bottom-anchors\n the cloze against the shared sentence/audio row instead of top-aligning it —\n matches every item sitting on the same visual baseline. See CONTOOL-3159. */\n align-items: end;\n}\n\n/* The component's header row, restated because these areas replace its own. */\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank.has-header {\n grid-template-areas:\n \"header header header\"\n \"sentence template audio\"\n \"choices choices choices\";\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-template-line {\n grid-area: template;\n justify-self: center;\n}\n\n/* ClozeMarker.svelte's shared .cloze-marker rule vertically *centers* its content\n (align-items:center). question.css's .rli-s3-cloze-container uses\n justify-content:end instead, bottom-anchoring the answer text against the\n underline. Centering leaves it floating well above both the underline and the\n sentence image's own (bottom-anchored) baseline. The blank's own box height\n (160px, from McPopulatedBlank.svelte's shared audio_blank_only/stimulus_image_blank\n rule) is left as-is — it already matches the Learnosity reference (~161px)\n closely; only the internal alignment was wrong. See CONTOOL-3159. */\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-blank-slot {\n align-items: flex-end;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-sentence-line {\n justify-self: end;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-audio-container {\n justify-self: start;\n align-self: start;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-choices-fieldset {\n justify-self: center;\n width: fit-content;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-sentence-line img {\n max-width: 100%;\n height: auto;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-horizontal:hover .pie-choice-tile {\n background: #e2e2e2;\n color: #000;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-horizontal.is-selected .pie-choice-tile,\n.mc-populated-blank-root.variant-sel-r1-s3\n .pie-choice-horizontal.is-selected:hover\n .pie-choice-tile {\n background: #fcfcd3;\n color: #000;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-horizontal .pie-choice-radio {\n transform: scale(0.85);\n}\n\n/* question.css: below 850px .rli-s3-container is a one-column grid in DOM order\n (listen button, sentence, cloze, distractors), each at the left edge. The cloze\n container stretches, so the underline spans the column, with the 150px\n content-element at its start (.rli-s3-cloze { justify-content:flex-start }). */\n@media not all and (min-width: 850px) {\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank {\n grid-template-columns: minmax(0, 1fr);\n grid-template-areas:\n \"transcript\"\n \"audio\"\n \"sentence\"\n \"template\"\n \"choices\";\n align-items: start;\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank.has-header {\n grid-template-areas:\n \"header\"\n \"transcript\"\n \"audio\"\n \"sentence\"\n \"template\"\n \"choices\";\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-sentence-line,\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-choices-fieldset {\n justify-self: start;\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-template-line {\n justify-self: stretch;\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-blank-slot {\n flex-grow: 1;\n justify-content: flex-start;\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-blank-value {\n width: 150px;\n }\n}\n"
8647
+ cssText: "/* Shared r1 base styles (font, template-line flex, choice-label) are in sel-r1-base.css */\n.mc-populated-blank-root.variant-sel-r1-s3 {\n max-width: 860px;\n margin-inline: auto;\n --mpb-listen-button-size: 128px;\n --mpb-choice-width-px: 155px;\n --mpb-choice-tile-min-height: 11.25rem;\n --mpb-choice-content-min-height: 9.375rem;\n --mpb-choice-group-gap: 0.35rem;\n --mpb-choice-row-gap: 0.35rem;\n --mpb-horizontal-choice-radio-top-margin: 0.25rem;\n --mpb-stimulus-min-column: 240px;\n --mpb-stimulus-grid-column-gap: 1.1rem;\n --mpb-stimulus-choices-margin-top: 0.85rem;\n --mpb-audio-blank-template-margin-bottom: 0.9rem;\n --mpb-blank-wide-width: 6.8rem;\n}\n\n/* sel_r1-s3_plusggg/question.css does not set font-weight on distractors or\n cloze content-element, so they inherit 400 from the host root. The earlier\n font-weight:500 override caused PIE to render visibly heavier than the CQT for\n the same author content. See CONTOOL-2571. */\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-label,\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-label :is(span, p, div) {\n font-size: 1.9em;\n line-height: 89px;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-blank-value,\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-blank-value :is(span, p, div) {\n line-height: 1.1;\n white-space: nowrap;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-horizontal .pie-choice-tile {\n border-radius: 6px;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank {\n grid-template-columns: minmax(220px, auto) auto auto;\n grid-template-areas:\n \"sentence template audio\"\n \"choices choices choices\";\n justify-content: center;\n /* question.css: .rli-s3-cloze-container { justify-content:end } bottom-anchors\n the cloze against the shared sentence/audio row instead of top-aligning it —\n matches every item sitting on the same visual baseline. See CONTOOL-3159. */\n align-items: end;\n}\n\n/* The component's header row, restated because these areas replace its own. */\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank.has-header {\n grid-template-areas:\n \"header header header\"\n \"sentence template audio\"\n \"choices choices choices\";\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-template-line {\n grid-area: template;\n justify-self: center;\n}\n\n/* ClozeMarker.svelte's shared .cloze-marker rule vertically *centers* its content\n (align-items:center). question.css's .rli-s3-cloze-container uses\n justify-content:end instead, bottom-anchoring the answer text against the\n underline. Centering leaves it floating well above both the underline and the\n sentence image's own (bottom-anchored) baseline. The blank's own box height\n (160px, from McPopulatedBlank.svelte's shared audio_blank_only/stimulus_image_blank\n rule) is left as-is — it already matches the CQT reference (~161px)\n closely; only the internal alignment was wrong. See CONTOOL-3159. */\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-blank-slot {\n align-items: flex-end;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-sentence-line {\n justify-self: end;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-audio-container {\n justify-self: start;\n align-self: start;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-choices-fieldset {\n justify-self: center;\n width: fit-content;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-sentence-line img {\n max-width: 100%;\n height: auto;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-horizontal:hover .pie-choice-tile {\n background: #e2e2e2;\n color: #000;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-horizontal.is-selected .pie-choice-tile,\n.mc-populated-blank-root.variant-sel-r1-s3\n .pie-choice-horizontal.is-selected:hover\n .pie-choice-tile {\n background: #fcfcd3;\n color: #000;\n}\n\n.mc-populated-blank-root.variant-sel-r1-s3 .pie-choice-horizontal .pie-choice-radio {\n transform: scale(0.85);\n}\n\n/* question.css: below 850px .rli-s3-container is a one-column grid in DOM order\n (listen button, sentence, cloze, distractors), each at the left edge. The cloze\n container stretches, so the underline spans the column, with the 150px\n content-element at its start (.rli-s3-cloze { justify-content:flex-start }). */\n@media not all and (min-width: 850px) {\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank {\n grid-template-columns: minmax(0, 1fr);\n grid-template-areas:\n \"transcript\"\n \"audio\"\n \"sentence\"\n \"template\"\n \"choices\";\n align-items: start;\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank.has-header {\n grid-template-areas:\n \"header\"\n \"transcript\"\n \"audio\"\n \"sentence\"\n \"template\"\n \"choices\";\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-sentence-line,\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-choices-fieldset {\n justify-self: start;\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-template-line {\n justify-self: stretch;\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-blank-slot {\n flex-grow: 1;\n justify-content: flex-start;\n }\n\n .mc-populated-blank-root.variant-sel-r1-s3.layout-stimulus_image_blank .pie-blank-value {\n width: 150px;\n }\n}\n"
8656
8648
  }
8657
8649
  };
8658
8650
  var normalizeCustomType = (value) => {