@ohhwells/bridge 0.1.90 → 0.1.92

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.
package/dist/index.cjs CHANGED
@@ -142,6 +142,7 @@ function isRenderableTree(value) {
142
142
 
143
143
  // src/lib/ai-sections-store.ts
144
144
  var AI_SECTIONS_KEY = "__ohw_ai_sections";
145
+ var AI_SLOT_KEY_PREFIX = "ai.";
145
146
  var EMPTY_AI_SECTIONS = { v: 1, sections: [] };
146
147
  function parseAiSectionsState(raw) {
147
148
  if (!raw) return EMPTY_AI_SECTIONS;
@@ -185,6 +186,63 @@ function applyTreeToState(state, payload) {
185
186
  const others = state.sections.filter((existing) => existing.id !== entry.id);
186
187
  return { ...state, v: 1, sections: [...others, entry] };
187
188
  }
189
+ function foldAlignIntoTrees(state, store) {
190
+ const byId = new Map(state.sections.map((entry) => [entry.id, entry]));
191
+ const nextTrees = /* @__PURE__ */ new Map();
192
+ const treeFor = (id) => {
193
+ const cloned = nextTrees.get(id);
194
+ if (cloned) return cloned;
195
+ const entry = byId.get(id);
196
+ if (!entry) return void 0;
197
+ const fresh = {
198
+ ...entry.tree,
199
+ rows: entry.tree.rows.map((row) => ({ ...row, blocks: row.blocks.map((block) => ({ ...block })) }))
200
+ };
201
+ nextTrees.set(id, fresh);
202
+ return fresh;
203
+ };
204
+ const nodes = {};
205
+ for (const [key, override] of Object.entries(store.nodes)) {
206
+ const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
207
+ const tree = match ? treeFor(match[1]) : void 0;
208
+ const block = match && tree ? tree.rows[Number(match[2])]?.blocks[Number(match[3])] : void 0;
209
+ if (!block) {
210
+ nodes[key] = override;
211
+ continue;
212
+ }
213
+ block.align = override.align;
214
+ const rest = { ...override };
215
+ delete rest.align;
216
+ if (Object.keys(rest).length > 0) nodes[key] = rest;
217
+ }
218
+ const sections = {};
219
+ for (const [sectionId, override] of Object.entries(store.sections)) {
220
+ const tree = override.align !== void 0 ? treeFor(sectionId) : void 0;
221
+ if (!tree) {
222
+ sections[sectionId] = override;
223
+ continue;
224
+ }
225
+ for (const row of tree.rows) {
226
+ for (const block of row.blocks) block.align = override.align;
227
+ }
228
+ const rest = { ...override };
229
+ delete rest.align;
230
+ if (Object.keys(rest).length > 0) sections[sectionId] = rest;
231
+ }
232
+ if (nextTrees.size === 0) return { state, store, changed: false };
233
+ return {
234
+ state: {
235
+ ...state,
236
+ v: 1,
237
+ sections: state.sections.map((entry) => {
238
+ const tree = nextTrees.get(entry.id);
239
+ return tree ? { ...entry, tree } : entry;
240
+ })
241
+ },
242
+ store: { v: 1, sections, nodes },
243
+ changed: true
244
+ };
245
+ }
188
246
  function removeFromState(state, id) {
189
247
  return { ...state, v: 1, sections: state.sections.filter((entry) => entry.id !== id) };
190
248
  }
@@ -403,6 +461,9 @@ function styleSheetCss() {
403
461
  `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
404
462
  );
405
463
  }
464
+ for (const align of ["left", "center", "right"]) {
465
+ rules.push(`[data-ohw-style-align="${align}"] { text-align: ${align} !important; }`);
466
+ }
406
467
  return rules.join("\n");
407
468
  }
408
469
  var STYLE_FONT_LINK_ID = "ohw-style-fonts";
@@ -429,10 +490,24 @@ var SECTION_ATTRS = {
429
490
  textDistribution: "data-ohw-style-distribution",
430
491
  headlineScale: "data-ohw-style-headline",
431
492
  imageAspect: "data-ohw-style-aspect",
432
- spacing: "data-ohw-style-spacing"
493
+ spacing: "data-ohw-style-spacing",
494
+ align: "data-ohw-style-align"
433
495
  };
434
496
  var NODE_WROTE_ATTR = "data-ohw-style-node";
435
- var NODE_PROPS = ["color", "font-family", "font-size", "background"];
497
+ var NODE_PROPS = [
498
+ "color",
499
+ "font-family",
500
+ "font-size",
501
+ "background",
502
+ "text-align",
503
+ "justify-content",
504
+ "align-items"
505
+ ];
506
+ var ALIGN_JUSTIFY = {
507
+ left: "flex-start",
508
+ center: "center",
509
+ right: "flex-end"
510
+ };
436
511
  function saveInline(el, prop) {
437
512
  const attr = `data-ohw-style-prev-${prop}`;
438
513
  if (el.hasAttribute(attr)) return;
@@ -476,6 +551,10 @@ function clearNodeProps(root) {
476
551
  function buttonSurfaceOf(el) {
477
552
  return el.closest("a, button") ?? el;
478
553
  }
554
+ function alignSubjectOf(el) {
555
+ const button = el.closest('[data-ohw-role="button"]');
556
+ return button?.parentElement ?? el;
557
+ }
479
558
  function applyStylesToDom(store) {
480
559
  ensureStyleSheet();
481
560
  clearSectionAttrs(document);
@@ -521,6 +600,18 @@ function applyStylesToDom(store) {
521
600
  el.style.setProperty("font-size", `${override.fontSize}px`, "important");
522
601
  el.setAttribute(NODE_WROTE_ATTR, "");
523
602
  }
603
+ if (override.align !== void 0) {
604
+ const subject = alignSubjectOf(el);
605
+ saveInline(subject, "text-align");
606
+ saveInline(subject, "justify-content");
607
+ subject.style.setProperty("text-align", override.align, "important");
608
+ subject.style.setProperty(
609
+ "justify-content",
610
+ ALIGN_JUSTIFY[override.align] ?? "flex-start",
611
+ "important"
612
+ );
613
+ subject.setAttribute(NODE_WROTE_ATTR, "");
614
+ }
524
615
  if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
525
616
  const surface = buttonSurfaceOf(el);
526
617
  if (override.buttonBackground !== void 0) {
@@ -729,7 +820,7 @@ function TextBlock({ slots, ctx, path }) {
729
820
  }
730
821
  function SectionHeaderBlock({ node, ctx, path }) {
731
822
  const slots = node.slots ?? {};
732
- const align = slots.alignment === "center" ? "center" : "left";
823
+ const align = node.align ?? (slots.alignment === "center" ? "center" : "left");
733
824
  const children = node.children ?? [];
734
825
  const buttonRowIdx = children.findIndex((c) => c.type === "button-row");
735
826
  const buttonRow = buttonRowIdx >= 0 ? children[buttonRowIdx] : void 0;
@@ -773,7 +864,7 @@ function SectionHeaderBlock({ node, ctx, path }) {
773
864
  display: "flex",
774
865
  gap: AI_TREE_TOKENS.spacing6,
775
866
  marginTop: AI_TREE_TOKENS.spacing8,
776
- justifyContent: align === "center" ? "center" : "flex-start"
867
+ justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start"
777
868
  },
778
869
  children: (buttonRow.children ?? []).map((button, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
779
870
  ButtonEl,
@@ -1106,7 +1197,7 @@ function CardBlock({ node, ctx, path }) {
1106
1197
  editPath: `${path}.media`
1107
1198
  }
1108
1199
  ) : null;
1109
- const centered = slots.alignment === "center";
1200
+ const centered = (node.align ?? slots.alignment) === "center";
1110
1201
  const content = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1111
1202
  "div",
1112
1203
  {
@@ -1825,6 +1916,19 @@ function AiTreeRenderer({
1825
1916
  }
1826
1917
  })();
1827
1918
  const distributed = !isOverlay && settings.textDistribution;
1919
+ const rowAlignItems = (rowAlign) => {
1920
+ if (rowAlign === "top") return "start";
1921
+ if (rowAlign === "bottom") return "end";
1922
+ if (rowAlign === "center" || rowAlign === "stretch") return rowAlign;
1923
+ if (distributed === "space-between") return "stretch";
1924
+ return (distributed ?? settings.verticalPosition) === "top" ? "start" : "center";
1925
+ };
1926
+ const cellAlignStyle = (blockAlign) => blockAlign ? {
1927
+ display: "flex",
1928
+ flexDirection: "column",
1929
+ alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
1930
+ textAlign: blockAlign
1931
+ } : {};
1828
1932
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1829
1933
  "section",
1830
1934
  {
@@ -1861,7 +1965,7 @@ function AiTreeRenderer({
1861
1965
  display: "grid",
1862
1966
  gridTemplateColumns: "repeat(12, 1fr)",
1863
1967
  gap: AI_TREE_TOKENS.spacing6,
1864
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1968
+ alignItems: rowAlignItems(row.align),
1865
1969
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1866
1970
  },
1867
1971
  children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1871,6 +1975,8 @@ function AiTreeRenderer({
1871
1975
  style: {
1872
1976
  gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1873
1977
  minWidth: 0,
1978
+ // Horizontal placement of the block's content within its column.
1979
+ ...cellAlignStyle(block.align),
1874
1980
  // space-between: each column becomes a flex column whose content spreads over
1875
1981
  // the full row height instead of clumping at the top.
1876
1982
  ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
@@ -8119,9 +8225,6 @@ var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
8119
8225
  function isChromeSection(el) {
8120
8226
  return el.matches("header, nav, footer, aside");
8121
8227
  }
8122
- function isSchedulingSection(el) {
8123
- return (el.dataset.ohwSection ?? "").startsWith("scheduling-");
8124
- }
8125
8228
  function titleCaseSectionId(id) {
8126
8229
  return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
8127
8230
  }
@@ -8157,50 +8260,12 @@ function isRemovedSection(el) {
8157
8260
  }
8158
8261
  function topLevelSections() {
8159
8262
  return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8160
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el) && !isSchedulingSection(el) && !isRemovedSection(el)
8263
+ (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el) && !isRemovedSection(el)
8161
8264
  );
8162
8265
  }
8163
8266
  function instanceIdOf(el) {
8164
8267
  return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
8165
8268
  }
8166
- function moveableSequence() {
8167
- const scheduling = Array.from(
8168
- document.querySelectorAll('[data-ohw-section-container="scheduling"]')
8169
- );
8170
- return [...topLevelSections(), ...scheduling].sort((a, b) => {
8171
- const pos = a.compareDocumentPosition(b);
8172
- if (pos & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
8173
- if (pos & Node.DOCUMENT_POSITION_PRECEDING) return 1;
8174
- return 0;
8175
- });
8176
- }
8177
- function planSchedulingMoveTarget(instanceId, direction) {
8178
- const inner = document.querySelector(`[data-ohw-section="${CSS.escape(instanceId)}"]`);
8179
- const wrapper = inner?.closest('[data-ohw-section-container="scheduling"]');
8180
- if (!wrapper) return null;
8181
- const sequence = moveableSequence();
8182
- const index = sequence.indexOf(wrapper);
8183
- if (index === -1) return null;
8184
- const siblingIndex = direction === "up" ? index - 1 : index + 1;
8185
- if (siblingIndex < 0 || siblingIndex >= sequence.length) return null;
8186
- const others = sequence.filter((_, i) => i !== index);
8187
- const clamped = Math.max(0, Math.min(siblingIndex, others.length));
8188
- if (clamped === 0) return null;
8189
- const newPrev = others[clamped - 1];
8190
- if (newPrev.hasAttribute("data-ohw-section-container")) return null;
8191
- const newAnchorId = instanceIdOf(newPrev);
8192
- if (!newAnchorId) return null;
8193
- return { wrapper, others, clamped, newAnchorId };
8194
- }
8195
- function canMoveSchedulingSection(instanceId, direction) {
8196
- return planSchedulingMoveTarget(instanceId, direction) !== null;
8197
- }
8198
- function planSchedulingMove(instanceId, direction) {
8199
- const plan = planSchedulingMoveTarget(instanceId, direction);
8200
- if (!plan) return null;
8201
- plan.others[plan.clamped - 1].after(plan.wrapper);
8202
- return { newAnchorId: plan.newAnchorId };
8203
- }
8204
8269
  function findByInstanceId(instanceId) {
8205
8270
  const escapedId = CSS.escape(instanceId);
8206
8271
  return document.querySelector(`[data-ohw-instance="${escapedId}"]`) ?? document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
@@ -8406,12 +8471,6 @@ function useLiveSectionRect(sectionId) {
8406
8471
  return rect;
8407
8472
  }
8408
8473
  function computeSectionBoundaryFlags(instanceId) {
8409
- if (instanceId.startsWith("scheduling-")) {
8410
- return {
8411
- isFirst: !canMoveSchedulingSection(instanceId, "up"),
8412
- isLast: !canMoveSchedulingSection(instanceId, "down")
8413
- };
8414
- }
8415
8474
  const topLevel = topLevelSections();
8416
8475
  const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
8417
8476
  if (index === -1) return { isFirst: true, isLast: true };
@@ -15001,21 +15060,14 @@ function updateSectionScheduleId(insertAfter, scheduleId) {
15001
15060
  tracker.textContent = JSON.stringify(updated);
15002
15061
  return tracker.textContent ?? "[]";
15003
15062
  }
15004
- function updateSchedulingInsertAfter(oldInsertAfter, newInsertAfter) {
15005
- const tracker = getSectionsTracker();
15006
- let sections = [];
15007
- try {
15008
- sections = JSON.parse(tracker.textContent || "[]");
15009
- } catch {
15063
+ var schedulingRoots = /* @__PURE__ */ new WeakMap();
15064
+ function unmountSchedulingWrapper(wrapper) {
15065
+ const root = schedulingRoots.get(wrapper);
15066
+ if (root) {
15067
+ schedulingRoots.delete(wrapper);
15068
+ setTimeout(() => root.unmount(), 0);
15010
15069
  }
15011
- const currentPath = window.location.pathname;
15012
- const updated = sections.map((s) => {
15013
- if (s.type !== "scheduling" || s.insertAfter !== oldInsertAfter) return s;
15014
- if (s.pagePath && s.pagePath !== currentPath) return s;
15015
- return { ...s, insertAfter: newInsertAfter };
15016
- });
15017
- tracker.textContent = JSON.stringify(updated);
15018
- return tracker.textContent ?? "[]";
15070
+ wrapper.remove();
15019
15071
  }
15020
15072
  function schedulingSectionId(insertAfter) {
15021
15073
  return `scheduling-${insertAfter}`;
@@ -15070,12 +15122,12 @@ function retryMissingSchedulingMounts(entries, notifyOnConnect = false) {
15070
15122
  if (!hasMissingSchedulingWidgets(entries)) return;
15071
15123
  mountSchedulingEntries(entries, notifyOnConnect);
15072
15124
  }
15073
- function initSectionsFromContent(content, removeExisting = false) {
15125
+ function initSectionsFromContent(content, removeExisting = false, currentPath = typeof window !== "undefined" ? window.location.pathname : "/") {
15074
15126
  const raw = content["__ohw_sections"];
15075
15127
  if (!raw) return;
15076
15128
  try {
15077
15129
  if (removeExisting) {
15078
- document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => el.remove());
15130
+ document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => unmountSchedulingWrapper(el));
15079
15131
  }
15080
15132
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
15081
15133
  if (inEditor) getSectionsTracker().textContent = raw;
@@ -15083,8 +15135,16 @@ function initSectionsFromContent(content, removeExisting = false) {
15083
15135
  const preExisting = pageEntries.filter((e) => !isSchedulingWidgetMissing(e));
15084
15136
  const notifyForEntry = inEditor ? (e) => !e.scheduleId : false;
15085
15137
  mountSchedulingEntries(pageEntries, notifyForEntry);
15086
- requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry));
15087
- setTimeout(() => retryMissingSchedulingMounts(pageEntries, notifyForEntry), 250);
15138
+ const reapplyOrder = () => applyPersistedOrder(getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath));
15139
+ reapplyOrder();
15140
+ requestAnimationFrame(() => {
15141
+ retryMissingSchedulingMounts(pageEntries, notifyForEntry);
15142
+ reapplyOrder();
15143
+ });
15144
+ setTimeout(() => {
15145
+ retryMissingSchedulingMounts(pageEntries, notifyForEntry);
15146
+ reapplyOrder();
15147
+ }, 250);
15088
15148
  for (const entry of preExisting) {
15089
15149
  window.postMessage({ type: "ow:schedule-config", insertAfter: entry.insertAfter, scheduleId: entry.scheduleId ?? null }, "*");
15090
15150
  }
@@ -15099,6 +15159,8 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
15099
15159
  if (!mountPoint) return false;
15100
15160
  const container = document.createElement("div");
15101
15161
  container.dataset.ohwSectionContainer = "scheduling";
15162
+ container.dataset.ohwSection = sectionId;
15163
+ container.dataset.ohwInstance = sectionId;
15102
15164
  if (insertBefore) {
15103
15165
  const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
15104
15166
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
@@ -15112,6 +15174,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
15112
15174
  tail.insertAdjacentElement("afterend", container);
15113
15175
  }
15114
15176
  const root = (0, import_client2.createRoot)(container);
15177
+ schedulingRoots.set(container, root);
15115
15178
  (0, import_react_dom3.flushSync)(() => {
15116
15179
  root.render(
15117
15180
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
@@ -20367,12 +20430,35 @@ function OhhwellsBridge() {
20367
20430
  window.addEventListener("message", handleAiSetBrand);
20368
20431
  const handleAiSetStyles = (e) => {
20369
20432
  if (e.data?.type !== "ow:ai-set-styles") return;
20370
- const value = typeof e.data.value === "string" ? e.data.value : "";
20433
+ let value = typeof e.data.value === "string" ? e.data.value : "";
20371
20434
  const previous = stylesRef.current;
20435
+ let previousSections;
20436
+ const store = parseStyleStore(value);
20437
+ if (store) {
20438
+ const folded = foldAlignIntoTrees(parseAiSectionsState(aiSectionsRef.current), store);
20439
+ if (folded.changed) {
20440
+ const nextSections = serializeAiSectionsState(folded.state);
20441
+ if (nextSections !== aiSectionsRef.current) {
20442
+ previousSections = aiSectionsRef.current;
20443
+ aiSectionsRef.current = nextSections;
20444
+ applyAiSectionsToDom(folded.state);
20445
+ postToParentRef.current({
20446
+ type: "ow:change",
20447
+ nodes: [{ key: AI_SECTIONS_KEY, text: nextSections }]
20448
+ });
20449
+ }
20450
+ value = JSON.stringify(folded.store);
20451
+ }
20452
+ }
20372
20453
  stylesRef.current = value;
20373
20454
  applyStylesToDom(parseStyleStore(value));
20374
20455
  postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
20375
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
20456
+ postToParentRef.current({
20457
+ type: "ow:ai-styles-applied",
20458
+ previous,
20459
+ value,
20460
+ ...previousSections !== void 0 ? { previousSections } : {}
20461
+ });
20376
20462
  };
20377
20463
  window.addEventListener("message", handleAiSetStyles);
20378
20464
  const handleGetBrand = (e) => {
@@ -20387,15 +20473,6 @@ function OhhwellsBridge() {
20387
20473
  const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20388
20474
  const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
20389
20475
  if (!instanceId || !direction) return;
20390
- if (instanceId.startsWith("scheduling-")) {
20391
- const result = planSchedulingMove(instanceId, direction);
20392
- if (!result) return;
20393
- const oldInsertAfter = instanceId.slice("scheduling-".length);
20394
- const sectionsJson = updateSchedulingInsertAfter(oldInsertAfter, result.newAnchorId);
20395
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: sectionsJson }] });
20396
- window.dispatchEvent(new Event("resize"));
20397
- return;
20398
- }
20399
20476
  const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
20400
20477
  if (!entries) return;
20401
20478
  const orderJson = JSON.stringify(entries);
@@ -20417,8 +20494,13 @@ function OhhwellsBridge() {
20417
20494
  if (!instanceId) return;
20418
20495
  if (instanceId.startsWith("scheduling-")) {
20419
20496
  const insertAfter = instanceId.slice("scheduling-".length);
20420
- const inner = document.querySelector(`[data-ohw-section="${CSS.escape(instanceId)}"]`);
20421
- inner?.closest('[data-ohw-section-container="scheduling"]')?.remove();
20497
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(instanceId)}"]`);
20498
+ const wrapper = el?.closest('[data-ohw-section-container="scheduling"]') ?? null;
20499
+ if (wrapper) {
20500
+ unmountSchedulingWrapper(wrapper);
20501
+ } else {
20502
+ el?.setAttribute("style", "display:none");
20503
+ }
20422
20504
  const tracker = getSectionsTracker();
20423
20505
  let sections = [];
20424
20506
  try {
@@ -21397,7 +21479,7 @@ function OhhwellsBridge() {
21397
21479
  postToParent2({
21398
21480
  type: "ow:ready",
21399
21481
  version: "1",
21400
- bridgeVersion: "0.1.89",
21482
+ bridgeVersion: "0.1.91",
21401
21483
  path: pathname,
21402
21484
  nodes: collectEditableNodes(editContentRef.current),
21403
21485
  sections