/* ═══ Canvas Editing Surface — the styles, in the ONE file both shells load ═══
   Extracted from admin.css, unchanged, because of a real defect: every .ce-*
   and .ces-* rule lived only in admin.css, and platform.html does not load
   admin.css. So when the institution's private space opened the canvas page
   builder (m237), the editor rendered with NO styling at all — the toolbar came
   out as a row of bare 16px buttons and the Design/Sample Data and
   Desktop/Tablet/Mobile toggles as unstyled inline text. Reported from
   production: "the text, image, etc are too small. should have it as you did it
   in previous canvas with text next to it."

   Loading admin.css from platform.html was the other option and is the wrong
   one: it carries the whole staff console's chrome and would restyle the
   learner platform underneath itself. Extracting the ~670 lines the editor
   actually needs gives both shells the same editor and gives neither anything
   else.

   NOT A REWRITE. Every rule below is byte-for-byte what admin.css held; the
   only change is which file it lives in and that two pages now link it.
   admin/index.html and platform.html both load it AFTER their own stylesheet,
   so nothing about the staff console's appearance changes. */

/* ─── Canvas Engine v1.0 — freeform document authoring engine ─────────────── */
/* Implements docs/CANVAS_ENGINE_PRODUCT_DESIGN_SPEC.md. See
   admin-canvas-engine.js. Prefix .ce-* per the platform's established
   per-feature CSS prefix convention (.cd-*, .pb-*, .eb-*). */

/* Library shell */
.ce-lib-tabs { display: flex; gap: 0.35rem; flex-wrap: wrap; margin: 0.75rem 0 1rem; }
.ce-lib-tab {
  padding: 0.4rem 0.85rem; border: 1px solid var(--border); border-radius: 999px; background: transparent;
  font-size: 0.8rem; color: var(--text-secondary); cursor: pointer; font-family: var(--font-body); transition: var(--transition);
}
.ce-lib-tab:hover { background: var(--gold-soft); color: var(--text-primary); }
.ce-lib-tab.active { background: var(--gold); color: #1A1D2B; font-weight: 600; border-color: var(--gold); }

.ce-lib-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1rem; }
.ce-lib-card {
  border: 1px solid var(--border); border-radius: var(--radius-md, 8px); overflow: hidden; cursor: pointer;
  background: var(--surface, rgba(0,0,0,0.02)); transition: var(--transition);
}
.ce-lib-card:hover { border-color: var(--gold); transform: translateY(-2px); box-shadow: 0 6px 16px rgba(0,0,0,0.08); }
.ce-lib-card-thumb { height: 120px; background: linear-gradient(135deg, var(--gold-soft), rgba(0,0,0,0.03)); }
.ce-lib-card-thumb--certificate { aspect-ratio: 297/210; }
.ce-lib-card-thumb--badge { aspect-ratio: 1/1; }
.ce-lib-card-body { padding: 0.75rem 0.9rem; }
.ce-lib-card-name { font-weight: 600; font-size: 0.9rem; color: var(--text-primary); margin-bottom: 0.3rem; }
.ce-lib-card-meta { display: flex; align-items: center; gap: 0.5rem; font-size: 0.72rem; color: var(--text-muted); }
.ce-lib-card-draft-flag { padding: 0.1rem 0.45rem; border-radius: 999px; background: rgba(240,192,96,0.12); color: #B8860B; border: 1px solid rgba(240,192,96,0.3); font-weight: 600; }

/* New Document modal */
.ce-modal-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.45); display: flex; align-items: center; justify-content: center; z-index: 500;
}
.ce-modal {
  background: var(--bg-elevated, #fff); border-radius: var(--radius-md, 8px); padding: 1.5rem; width: 100%; max-width: 380px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.25);
}
.ce-modal h3 { margin: 0 0 1rem; font-size: 1.05rem; }
.ce-modal-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1.25rem; }

/* Editor shell */
.ce-editor-header {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border); flex-wrap: wrap;
}
.ce-editor-header-left, .ce-editor-header-right { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
/* Top toolbar grouping (Canvas Engine usability pass, "group actions
   logically" — View / History / Editing / Publishing) — purely visual: the
   same buttons with the same ids/handlers, wrapped in labelled clusters
   separated by a thin divider instead of one undifferentiated row. */
.ce-header-group { display: flex; align-items: center; gap: 0.4rem; }
.ce-header-divider { width: 1px; height: 22px; background: var(--border); margin: 0 0.2rem; }
.ce-title-input {
  font-size: 1.2rem; font-weight: 600; font-family: var(--font-body); background: transparent;
  border: 1px solid transparent; border-radius: var(--radius-sm, 6px); padding: 0.15rem 0.4rem; color: inherit; min-width: 180px;
}
.ce-title-input:hover:not(:disabled), .ce-title-input:focus:not(:disabled) {
  border-color: var(--border); background: var(--surface, rgba(0,0,0,0.03)); outline: none;
}
.ce-version-label { font-size: 0.75rem; color: var(--text-muted); }

/* Inspector redesign: moved from a permanent right-hand column to a
   horizontal strip along the BOTTOM of the workspace (explicit user
   request — reserving a ~300px side column was still starving the
   canvas of width the instant anything was selected, which is most of
   an editing session, even after the ce-no-selection column-collapse
   below). Only 2 grid columns now (toolbar / canvas) — the canvas gets
   its FULL width at all times, selection state or not. Rows are "auto
   auto" (not 1fr) deliberately: no forced viewport height here, same
   natural-scrolling constraint as the rest of this workspace — each
   row simply sizes to its own content. */
.ce-editor-body { display: grid; grid-template-columns: 56px 1fr; grid-template-rows: auto auto; min-height: 520px; }
/* .ce-inspector spans both columns as row 2 — see the .ce-inspector
   rule itself (below) for the flex-wrap layout that turns its
   previously-stacked vertical fields into a horizontal, wrapping bar. */
.ce-inspector { grid-column: 1 / -1; grid-row: 2; }
/* Workspace size fix (kept, repurposed): previously collapsed the
   Inspector's WIDTH column when nothing was selected; now that it's a
   bottom strip instead of a side column, this caps its HEIGHT to a
   slim single-line strip when empty instead, handing that vertical
   space back to the canvas — reappearing at full height the instant
   something is selected. Toggled from renderInspector()
   (admin-canvas-engine.js), same class, same call site, unchanged. */
.ce-editor-body.ce-no-selection .ce-inspector { max-height: 52px; }

/* Provider Inspector authoring UX round (Website Pages on Canvas Engine) —
   independently scrolling Inspector: "The Canvas itself should remain
   visible at all times. Only the Inspector should scroll... similar to
   Figma, Canva or PowerPoint." As Runtime Providers gain richer,
   Provider-specific editors (canvas-provider-editors.js — e.g. a Programme
   Listing "Selected Programmes" checklist), the Inspector's own content can
   now grow arbitrarily tall. Without a bound, that tall content would push
   the whole workspace taller and force the PAGE to scroll just to see the
   bottom of the Inspector — dragging the Canvas out of view along with it,
   exactly what this fix prevents.
   Scoped to .ces-body — the one distinguishing root class
   canvas-editing-surface.js's own render() emits (`<div class="ce-editor-
   body ces-body">`) that admin-canvas-engine.js's Certificate/Document
   editor root markup does NOT carry (confirmed via grep: that file emits
   bare `.ce-editor-body` only). This scoping is deliberate and
   load-bearing: an earlier, unscoped attempt at a similar bounded-height
   shell was reverted specifically because it broke that other editor (see
   this section's own git history / PROJECT_STATUS notes) — bounding only
   `.ces-body .ce-inspector` here cannot repeat that regression, because the
   selector never matches the Certificate/Document editor's DOM at all.
   Implementation: rather than bounding the whole .ce-editor-body shell to
   the viewport height (which would require knowing this screen's exact
   page chrome height above the editor — fragile, and the earlier reverted
   attempt's actual mistake), only the Inspector's own grid row (row 2,
   spanning full width below the Toolbar/Canvas row) is capped and given
   its own overflow-y:auto. Since the Inspector is the sole occupant of
   that "auto"-sized grid row, capping its own max-height caps the row too
   — the Canvas above is structurally untouched and never affected by how
   much content the Inspector holds.
   `.ce-editor-body.ce-no-selection .ce-inspector` (immediately above) has
   higher selector specificity (3 classes vs. this rule's 2) and so still
   correctly wins when nothing is selected, preserving that existing
   collapsed-to-52px behaviour unchanged. */
.ces-body .ce-inspector {
  max-height: min(46vh, 520px);
  overflow-y: auto;
}

/* Programme Showcase provider editor (canvas-provider-editors.js,
   "website_programme_showcase") — the ordered-list authoring UI for
   Available Programme + Coming Soon entries. Deliberately minimal: reuses
   .field-hint/.badge conventions already established elsewhere rather than
   introducing a new visual language for one Inspector panel. */
/* max-height + its own overflow-y:auto (Aug 2026 fix) — a Showcase with
   many placed entries used to push the "Add Available Programme"/"Add
   Coming Soon" selects further and further down the Inspector on every
   addition, forcing a scroll through the whole growing list just to reach
   them again. Capping the list's own height keeps those Add controls
   reachable at a fixed position right below it; the Inspector's own
   `.ces-body .ce-inspector` scroll region (immediately above) still
   handles the panel as a whole if its total content exceeds that. */
.cpe-ps-list { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 0.75rem; max-height: 260px; overflow-y: auto; }
.cpe-ps-row {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-secondary, rgba(255,255,255,0.03));
}
.cpe-ps-row-label { flex: 1; font-size: 0.82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cpe-ps-row-actions { display: flex; align-items: center; gap: 0.25rem; flex-shrink: 0; }
.cpe-ps-row-actions button {
  width: 1.6rem; height: 1.6rem; line-height: 1;
  border: 1px solid var(--border); border-radius: 4px; background: transparent;
  cursor: pointer; font-size: 0.85rem;
}
.cpe-ps-row-actions button:disabled { opacity: 0.35; cursor: not-allowed; }
.cpe-ps-badge {
  display: inline-flex; align-items: center; flex-shrink: 0;
  font-size: 0.65rem; font-weight: 600; padding: 0.15rem 0.5rem; border-radius: 999px;
}
.cpe-ps-badge--available { background: rgba(110,231,183,0.12); color: #6EE7B7; border: 1px solid rgba(110,231,183,0.2); }
.cpe-ps-badge--coming-soon { background: rgba(240,192,96,0.1); color: #F0C060; border: 1px solid rgba(240,192,96,0.2); }
.cpe-ps-badge--missing { background: rgba(239,68,68,0.10); color: #F87171; border: 1px solid rgba(239,68,68,0.2); }

/* Grid Container v1 (docs/GRID_LAYOUT_CONTAINER_ARCHITECTURE_PROPOSAL.md) —
   editor-stage visual indication of grid membership (proposal §4). A grid
   object itself gets a distinct dashed outline so an author can see its
   footprint even when empty; its children get a lighter/dotted outline on
   hover to read as "grid-managed," distinct from a free object's normal
   solid `.ce-selected` outline. Purely additive CSS — no effect on any page
   with no `grid` object, since `.ces-grid-container`/`.ce-grid-child` are
   never applied to anything else. */
.ces-grid-container {
  outline: 2px dashed rgba(201,162,75,0.55);
  outline-offset: -2px;
}
.ces-grid-container.ce-selected {
  outline-color: var(--gold, #C9A24B);
}
.ce-grid-child {
  transition: outline-color 0.1s ease;
}
.ce-grid-child:hover {
  outline: 1px dotted rgba(201,162,75,0.5);
  outline-offset: -1px;
}
.ce-grid-child.ce-selected {
  outline: 2px solid var(--gold, #C9A24B);
  outline-offset: -2px;
}

/* Left toolbar — grouped into Select / Draw / Content clusters (Canvas
   Engine usability pass, "review the toolbar, group actions logically") via
   a thin divider between .ce-toolbar-group blocks; no behaviour change, the
   same flat list of .ce-tool-btn buttons with the same data-tool values. */
.ce-toolbar {
  display: flex; flex-direction: column; align-items: center; gap: 0.5rem; padding: 0.6rem 0.25rem;
  border-right: 1px solid var(--border); overflow-y: auto;
}
.ce-toolbar-group { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; }
.ce-toolbar-divider { width: 24px; height: 1px; background: var(--border); margin: 0.15rem 0; flex-shrink: 0; }
.ce-tool-btn {
  width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border: 1px solid transparent;
  background: transparent; border-radius: var(--radius-sm, 6px); cursor: pointer; font-size: 1.05rem; color: var(--text-secondary);
  transition: var(--transition);
}
.ce-tool-btn:hover { background: var(--gold-soft); color: var(--text-primary); }
.ce-tool-btn.active { background: var(--gold); color: #1A1D2B; }

/* Canvas surface — a slightly deeper workspace tint (0.02 -> 0.04 alpha)
   gives the white certificate page real figure-ground contrast so it reads
   as the page's primary focus, the same treatment Figma/Canva use behind
   their own canvas. */
.ce-canvas-area { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; padding: 1rem; overflow: auto; background: rgba(0,0,0,0.02); }
.ce-page-nav { display: flex; align-items: center; gap: 0.5rem; font-size: 0.78rem; color: var(--text-muted); }
.ce-page-nav button { border: 1px solid var(--border); background: transparent; border-radius: 4px; cursor: pointer; padding: 0.1rem 0.5rem; }
/* Bug fix (top-of-certificate cropping): align-items/justify-content:center
   here used to centre the STAGE inside the wrap using the stage's
   PRE-TRANSFORM layout box (transform:scale() in setZoom() never changes an
   element's own layout size, only its rendered appearance) — but the wrap
   itself is deliberately resized by setZoom() to the stage's POST-transform
   (scaled) footprint, so the wrap is always SMALLER than the box flexbox is
   trying to centre. That mismatch produces a permanent offset of exactly
   half the shrink amount in each axis, pushing the certificate's rendered
   top/left up and to the left of the wrap — at high zoom (this workspace's
   80% floor) that offset is large enough to push the certificate's top edge
   above .ce-canvas-area's own scrollable boundary, i.e. permanently
   unreachable/cropped, no scroll position can reveal it. Fix: align to
   flex-start on both axes instead of center. Since wrap's box is already
   sized to exactly match the stage's scaled footprint, flex-start places
   the stage's top-left (== its transform-origin) exactly at the wrap's own
   top-left — a perfect, offset-free fit. Horizontal centring of the
   certificate within the wider workspace is untouched: it's handled by the
   OUTER .ce-canvas-area's own align-items:center (which centres the WRAP,
   already sized to the true visible footprint, inside itself) — this rule
   was only ever centring the stage inside the wrap, which is a different,
   and here actively harmful, second layer of centring. */
.ce-canvas-wrap { display: flex; align-items: flex-start; justify-content: flex-start; width: 100%; }
.ce-canvas-stage {
  /* z-index: 0 (with position: relative) deliberately establishes a LOCAL
     stacking context here. Canvas objects use very high inline z-index
     values (selected object: 9999 !important; resize/rotate handles:
     10000 — see .ce-object.ce-selected / .ce-handle / .ce-rotate-handle
     below) to stay correctly ordered relative to EACH OTHER as z changes.
     Without this containing stacking context those values aren't scoped
     to the canvas — they compete directly against page-level UI z-indices
     (e.g. .ce-preview-overlay's z-index: 700) and can paint on TOP of a
     modal/overlay that opens while an object is still selected. This is
     the real cause of the Preview "duplicate image" bug: the overlay
     being opaque never mattered, because the still-selected canvas object
     was winning the stacking order and rendering above it regardless. */
  position: relative; z-index: 0; width: 100%; max-width: 820px; background: #fff; border: 1px solid var(--border);
  box-shadow: 0 8px 32px rgba(0,0,0,0.14); transform-origin: top left; user-select: none;
}
.ce-zoom-control { display: flex; align-items: center; gap: 0.4rem; font-size: 0.75rem; color: var(--text-muted); flex-shrink: 0; }
.ce-zoom-control button { width: 24px; height: 24px; border: 1px solid var(--border); background: transparent; border-radius: 4px; cursor: pointer; }
.ce-zoom-control button:hover { background: var(--gold-soft); border-color: var(--gold); }
#ce-zoom-label { min-width: 38px; text-align: center; }
/* "Fit" — re-runs the same fit-to-container calculation the editor already
   does once on first open, on demand (e.g. after resizing the browser
   window, or after collapsing/expanding a drawer changes the available
   height) — the professional-tool "zoom to fit" affordance Canva/Figma both
   have, so a lost/awkward zoom level is always one click from a sane
   default again. */
#ce-zoom-fit { width: auto; padding: 0 0.5rem; font-size: 0.72rem; }

/* Objects, selection & handles — Canvas Engine usability pass ("the editor
   should feel similar to professional design tools such as Canva, Figma or
   PowerPoint"). The old selection treatment was a single 2px gold outline,
   which loses contrast against light/white certificate backgrounds (gold on
   near-white cream reads as barely-there). The new treatment layers a white
   halo INSIDE a gold ring INSIDE a soft drop shadow — visible against any
   object fill colour, light or dark, the same "outline + contrast ring"
   technique Figma/Canva use for exactly this reason. */
.ce-object {
  position: absolute; box-sizing: border-box; cursor: move; outline: 1px solid transparent; transition: outline-color 0.1s ease;
}
.ce-object:hover { outline: 1px solid rgba(201,162,75,0.55); }
.ce-object.ce-selected {
  outline: none;
  box-shadow: 0 0 0 1.5px #fff, 0 0 0 3px var(--gold, #C9A24B), 0 2px 10px rgba(0,0,0,0.18);
  z-index: 9999 !important;
}
/* A small floating label above the selected object naming what it is —
   admin-canvas-engine.js sets data-object-label alongside data-object-id on
   every rendered object, so this is CSS-only, no extra wiring. Mirrors the
   same "what am I clicking on" affordance Figma shows on hover/selection. */
.ce-object.ce-selected::before {
  content: attr(data-object-label);
  position: absolute; left: 0; top: -1.5rem; padding: 0.1rem 0.5rem; border-radius: 4px;
  background: var(--gold, #C9A24B); color: #1A1D2B; font-size: 0.68rem; font-weight: 600; white-space: nowrap;
  pointer-events: none; z-index: 10001;
}
.ce-object-empty {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,0.04);
  border: 1px dashed var(--border); font-size: 0.7rem; color: var(--text-muted);
}
.ce-object-img { width: 100%; height: 100%; }
.ce-object-video { width: 100%; height: 100%; background: #000; }
/* Canvas rendering contract unification (forensic WYSIWYG parity
   investigation follow-up) — canvas-editing-surface.js (Website Pages /
   Layout Templates) now renders image/video content through the exact same
   shared renderCanvasObjectContent() (canvas-object-renderer.js) the live
   SSR/Preview/Export path uses, which emits a bare <img>/<video> with no
   class of its own (matching CANVAS_RENDER_CSS's own `.ce-object img`/
   `.ce-object video` descendant-selector rules exactly). These two rules are
   ADDITIVE, not a replacement for .ce-object-img/.ce-object-video directly
   above, and deliberately scoped under `.ces-body` (canvas-editing-surface.js's
   own top-level mount class — confirmed unique to it, admin-canvas-engine.js's
   own top-level container is `.ce-editor-body` with no `.ces-body`) rather
   than left as a bare `.ce-object img`/`.ce-object video` rule: both editors
   reuse the identical `.ce-object`/`.ce-object-img` class vocabulary, and
   admin-canvas-engine.js (the separate Certificate/Report editor) still
   emits its own <img class="ce-object-img"> markup from its own, untouched
   renderer — an unscoped rule here would silently start applying inside
   that editor too. `display:block` closes the exact gap CANVAS_RENDER_CSS's
   own header comment documents as a historical Preview-vs-Export bug class,
   for canvas-editing-surface.js only. */
.ces-body .ce-object img { display: block; width: 100%; height: 100%; }
.ces-body .ce-object video { display: block; width: 100%; height: 100%; background: #000; }
.ce-object-text { width: 100%; height: 100%; overflow: hidden; white-space: pre-wrap; word-break: break-word; line-height: 1.3; }
/* Placeholder Visualisation (Canvas Engine usability pass) — editing-canvas
   ONLY: this rule lives in admin.css, which nothing but the live editing
   canvas (renderObjectContent() in admin-canvas-engine.js) reads. Preview
   and Export both render placeholders via the separate, shared
   canvas-object-renderer.js module (CANVAS_RENDER_CSS) and are untouched —
   a bound/unbound placeholder always renders as real resolved text (or the
   authored fallback) there, exactly as before. Here, a placeholder gets a
   visibly distinct "chip" treatment (dashed gold border, tinted fill, a
   small tag icon pinned to its corner) so it reads as bound/dynamic content
   at a glance instead of looking like ordinary static text — purely an
   authoring aid.
   */
.ce-object-placeholder {
  background: rgba(201,162,75,0.10); border: 1.5px dashed rgba(201,162,75,0.55); border-radius: 3px; position: relative;
}
.ce-object-placeholder::after {
  content: "\1F3F7"; /* 🏷 */
  position: absolute; top: 2px; right: 3px; font-size: 0.7em; opacity: 0.7; line-height: 1;
}
.ce-object-shape { width: 100%; height: 100%; }
.ce-object-qr { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
.ce-qr-pattern {
  width: 80%; height: 80%; background-image:
    linear-gradient(currentColor 50%, transparent 50%), linear-gradient(90deg, currentColor 50%, transparent 50%);
  background-size: 20% 20%; opacity: 0.85;
}
.ce-object-flow {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; text-align: center;
  background: rgba(0,0,0,0.03); border: 1px dashed var(--border); font-size: 0.75rem; color: var(--text-muted); padding: 0.5rem;
}
.ce-signature-inner { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 0.1rem; text-align: center; }
.ce-signature-inner img { max-width: 100%; max-height: 60%; object-fit: contain; }
.ce-signature-name { font-weight: 600; font-size: 0.85em; }
.ce-signature-title { font-size: 0.7em; color: var(--text-muted); }

/* Selection handles */
.ce-handle {
  position: absolute; width: 9px; height: 9px; background: #fff; border: 2px solid var(--gold, #F0C060); border-radius: 2px; z-index: 10000;
}
.ce-handle--nw { top: -5px; left: -5px; cursor: nwse-resize; }
.ce-handle--n  { top: -5px; left: 50%; margin-left: -4.5px; cursor: ns-resize; }
.ce-handle--ne { top: -5px; right: -5px; cursor: nesw-resize; }
.ce-handle--e  { top: 50%; right: -5px; margin-top: -4.5px; cursor: ew-resize; }
.ce-handle--se { bottom: -5px; right: -5px; cursor: nwse-resize; }
.ce-handle--s  { bottom: -5px; left: 50%; margin-left: -4.5px; cursor: ns-resize; }
.ce-handle--sw { bottom: -5px; left: -5px; cursor: nesw-resize; }
.ce-handle--w  { top: 50%; left: -5px; margin-top: -4.5px; cursor: ew-resize; }
.ce-rotate-handle {
  position: absolute; top: -22px; left: 50%; margin-left: -5px; width: 10px; height: 10px; border-radius: 50%;
  background: #fff; border: 2px solid var(--gold, #F0C060); cursor: grab; z-index: 10000;
}

.ce-snap-guides { position: absolute; inset: 0; pointer-events: none; z-index: 9998; }
.ce-snap-guide { position: absolute; background: #FF4D6D; box-shadow: 0 0 4px rgba(255,77,109,0.5); }
.ce-snap-guide--v { top: 0; bottom: 0; width: 1.5px; }
.ce-snap-guide--h { left: 0; right: 0; height: 1.5px; }
.ce-marquee { position: absolute; border: 1px dashed var(--gold, #C9A24B); background: rgba(201,162,75,0.08); }

/* Inspector — now a horizontal bar along the bottom of the workspace
   (see .ce-editor-body above) instead of a permanent right-hand column.
   Same permanent panel, same fields/ids/wiring (wireInspectorCommon()/
   wireTypeFields() in admin-canvas-engine.js are entirely unchanged) —
   only HOW those same elements are laid out changed: display:flex with
   wrapping turns what used to be one long stacked column into a
   left-to-right flow of compact field groups, wrapping onto additional
   lines as needed, with its own scrollbar (max-height) as a last resort
   rather than ever pushing the canvas row taller. border moves from
   left to top since it now sits below the canvas, not beside it. */
.ce-inspector {
  padding: 0.55rem 1rem; border-left: none; border-top: 1px solid var(--border); overflow-y: auto;
  background: rgba(0,0,0,0.012); display: flex; flex-direction: row; flex-wrap: wrap;
  align-items: flex-start; align-content: flex-start; gap: 0.25rem 1.5rem; max-height: 240px;
}
.ce-inspector-empty {
  padding: 0.4rem 0; text-align: left; font-size: 0.8rem; color: var(--text-muted); line-height: 1.5;
  display: flex; flex-direction: row; align-items: center; gap: 0.6rem; flex: 1 1 auto;
}
.ce-inspector-empty-icon {
  width: 28px; height: 28px; border-radius: 50%; background: var(--gold-soft); display: flex; align-items: center;
  justify-content: center; font-size: 0.9rem; color: var(--gold); flex-shrink: 0;
}
.ce-inspector-section {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); font-weight: 600;
  margin: 0.4rem 0 0.3rem; padding-bottom: 0.2rem; border-bottom: 1px solid var(--border);
  flex-basis: 100%; /* forces its own line, so the fields belonging to this
    section flow horizontally directly beneath its heading rather than
    mixing in with the previous section's fields */
}
.ce-inspector-section:first-child { margin-top: 0; }
.ce-inspector label {
  display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.78rem; color: var(--text-secondary);
  margin-bottom: 0.35rem; flex: 0 1 160px; min-width: 120px;
}
.ce-insp-checkbox { flex-direction: row !important; align-items: center; gap: 0.4rem !important; flex: 0 0 auto !important; }
/* Compact 2-column grid for Position/Size/Rotation/Opacity — the same four
   <label><input> pairs admin-canvas-engine.js already renders, just wrapped
   in a grid instead of stacked full-width, matching the dense numeric-field
   layout Figma/Canva/PowerPoint property panels use. Given a fixed flex-basis
   so it sits as one compact block among the other horizontal Inspector
   fields rather than stretching to fill the row. */
.ce-insp-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 0.5rem; flex: 0 0 260px; }
.ce-insp-grid label { margin-bottom: 0.4rem; }
.ce-align-row { display: flex; gap: 0.3rem; margin-bottom: 0.4rem; flex-wrap: wrap; flex: 0 0 auto; }
.ce-align-row button { flex: 1; min-width: 32px; padding: 0.35rem; border: 1px solid var(--border); background: transparent; border-radius: 4px; cursor: pointer; }
.ce-align-row button:hover { background: var(--gold-soft); border-color: var(--gold); }
/* Layer order — Properties Panel gap-close (Canvas Engine usability pass):
   the object's own z-order was previously reachable only via right-click ->
   context menu; this exposes the exact same runReorder() actions directly
   in the Inspector's Layer section, no new logic, just a second entry point
   to what already existed. */
.ce-layer-order-row { display: flex; gap: 0.3rem; margin-bottom: 0.4rem; flex: 0 0 auto; }
.ce-layer-order-row button {
  flex: 1; padding: 0.4rem 0.2rem; border: 1px solid var(--border); background: transparent; border-radius: 4px;
  cursor: pointer; font-size: 0.85rem; color: var(--text-secondary);
}
.ce-layer-order-row button:hover { background: var(--gold-soft); border-color: var(--gold); color: var(--text-primary); }
/* Image/signature Replace control (#ce-insp-image-control, rendered by
   renderImageControl()) — given a fixed flex-basis for the same reason as
   .ce-insp-grid above: without one, its child <img class="ce-asset-thumb-
   preview"> (width:100%) would resolve against an unconstrained auto-sized
   flex item, which is circular/undefined sizing. */
#ce-insp-image-control { flex: 0 0 140px; }
.ce-asset-thumb-preview { width: 100%; max-height: 100px; object-fit: contain; background: rgba(0,0,0,0.04); border-radius: 6px; margin-bottom: 0.4rem; }

/* Drawer strip (Layers / Asset Library / Placeholders) */
.ce-drawer-strip { display: flex; gap: 0.4rem; padding: 0.5rem 1rem; border-top: 1px solid var(--border); }
.ce-drawer-toggle {
  padding: 0.35rem 0.75rem; border: 1px solid var(--border); border-radius: 6px; background: transparent;
  font-size: 0.78rem; color: var(--text-secondary); cursor: pointer; transition: var(--transition);
}
.ce-drawer-toggle:hover { background: var(--gold-soft); }
.ce-drawer-toggle.active { background: var(--gold-soft); color: var(--gold); font-weight: 600; }
.ce-drawer-panel { padding: 0.85rem 1rem; border-top: 1px solid var(--border); max-height: 260px; overflow-y: auto; }

/* Layers */
.ce-layers-list { display: flex; flex-direction: column; gap: 0.2rem; }
.ce-layer-row { display: flex; align-items: center; gap: 0.5rem; padding: 0.3rem 0.4rem; border-radius: 4px; cursor: pointer; }
.ce-layer-row:hover { background: rgba(0,0,0,0.03); }
.ce-layer-row--selected { background: var(--gold-soft); }
.ce-layer-row--nested { padding-left: 1.5rem; }
.ce-layer-nested-name { font-size: 0.8rem; color: var(--text-secondary); }
.ce-layer-name { border: none; background: transparent; font-size: 0.82rem; color: var(--text-primary); flex: 1; min-width: 0; }

/* Asset Library drawer */
.ce-asset-toolbar { display: flex; gap: 0.6rem; margin-bottom: 0.6rem; flex-wrap: wrap; }
.ce-asset-tabs { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-bottom: 0.6rem; }
.ce-asset-tab {
  padding: 0.25rem 0.6rem; border: 1px solid var(--border); border-radius: 999px; background: transparent;
  font-size: 0.72rem; color: var(--text-secondary); cursor: pointer;
}
.ce-asset-tab.active { background: var(--gold); color: #1A1D2B; border-color: var(--gold); }
.ce-asset-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 0.5rem; }
.ce-asset-thumb-btn, .ce-symbol-thumb-btn {
  display: flex; flex-direction: column; align-items: center; gap: 0.25rem; border: 1px solid var(--border); background: transparent;
  border-radius: 6px; padding: 0.4rem; cursor: pointer; font-size: 0.65rem; color: var(--text-muted);
}
.ce-asset-thumb-btn:hover, .ce-symbol-thumb-btn:hover { border-color: var(--gold); }
.ce-asset-thumb-btn img, .ce-asset-thumb-btn video { width: 100%; height: 48px; object-fit: contain; background: rgba(0,0,0,0.04); border-radius: 4px; }
.ce-symbol-icon { font-size: 1.4rem; }

/* Placeholder Toolbox drawer */
.ce-placeholder-group { margin-bottom: 0.6rem; }
.ce-placeholder-group-title { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 0.3rem; }
.ce-placeholder-chip {
  display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.3rem 0.6rem; margin: 0 0.3rem 0.3rem 0;
  border: 1px solid var(--border); border-radius: 999px; background: transparent; font-size: 0.75rem; color: var(--text-secondary); cursor: pointer;
}
.ce-placeholder-chip:hover { background: var(--gold-soft); color: var(--text-primary); }

/* Context menu */
.ce-context-menu {
  position: fixed; background: var(--bg-elevated, #fff); border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.15); padding: 0.3rem; z-index: 600; min-width: 160px;
}
.ce-context-item {
  display: block; width: 100%; text-align: left; padding: 0.4rem 0.6rem; border: none; background: transparent;
  font-size: 0.8rem; color: var(--text-primary); cursor: pointer; border-radius: 4px;
}
.ce-context-item:hover { background: var(--gold-soft); }

/* Preview mode overlay — must be fully opaque, not a translucent scrim: the
   editor underneath is never hidden/unmounted while Preview is open
   (openPreview() only injects into #ce-preview-host, it doesn't touch the
   rest of the editor DOM), so a translucent backdrop lets the editor's own
   full-brightness copy of the same objects (background/logos) show through
   at its own on-screen position — which differs from where the centered
   Preview stage lands, since they sit in different layout containers. The
   two overlapping, slightly-offset renders of the same bright artwork read
   as "duplicated images" even though there's really only one copy of each
   object. Solid background removes the ghosting. */
.ce-preview-overlay {
  /* z-index comfortably above the canvas's own highest internal value
     (resize/rotate handles use 10000 — see .ce-handle / .ce-rotate-handle)
     as defense in depth, on top of the .ce-canvas-stage stacking-context
     containment fix above, which is the real fix. */
  /* Bug fix (Preview accuracy/cropping): plain block layout + text-align
     center, NOT display:flex + align-items:center. Centering a flex/inline
     child that's WIDER than its scrollable container is a known browser
     issue — the scroll range starts at 0, which lands on the CENTERED
     position, so the child's start edge is permanently unreachable/cropped
     with no way to scroll to it. openPreview() (admin-canvas-engine.js)
     avoids ever hitting this by sizing #ce-preview-viewport to the already
     SCALED-to-fit footprint, so it never actually overflows this container
     — but keeping this container's own centering method overflow-safe too
     is cheap insurance against the same bug recurring. */
  position: fixed; inset: 0; background: #101014; z-index: 20000;
  overflow: auto; padding: 2rem; text-align: center;
}
.ce-preview-topbar { margin-bottom: 1rem; }
/* Sized by JS (openPreview()) to the true certificate's scaled-to-fit
   footprint — see the bug-fix comment above and openPreview()'s own
   comment for the full reasoning. inline-block so the parent's
   text-align:center centers it horizontally without needing an explicit
   margin:auto (which would need this element to already have a width, the
   very thing JS is about to set — inline-block sidesteps that ordering). */
.ce-preview-viewport { display: inline-block; text-align: left; }
.ce-preview-stage { position: relative; background: #fff; box-shadow: 0 20px 60px rgba(0,0,0,0.4); transform-origin: top left; }

/* ─── Canvas Layout Templates — admin-canvas-layout-templates.js (.clt-*) ───
   Deliberately its own prefix and its own small CSS block, not folded into
   .ce-* above — a separate, narrower authoring screen for a separate
   storage concept (see admin-canvas-layout-templates.js's own header and
   migration-m60.sql). Reuses .admin-panel/.btn/.input tokens from the rest
   of admin.css; only Layout-Template-specific structure lives here. */
.admin-help-text { font-size: 0.8rem; color: var(--text-muted); line-height: 1.5; }
.admin-empty-state { padding: 2rem 1rem; text-align: center; color: var(--text-muted); font-size: 0.9rem; }

.clt-library-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: 1.25rem; }
.clt-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; }
.clt-card { border: 1px solid var(--border); border-radius: 12px; padding: 1rem 1.1rem; background: #fff; }
.clt-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.6rem; }
.clt-card-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.clt-badge { display: inline-block; padding: 0.15rem 0.55rem; border-radius: 999px; font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; }
.clt-badge--draft { background: rgba(240,192,96,0.14); color: #B8860B; border: 1px solid rgba(240,192,96,0.3); }
.clt-badge--published { background: rgba(46,125,50,0.12); color: #2E7D32; border: 1px solid rgba(46,125,50,0.25); }
.clt-badge--archived { background: rgba(0,0,0,0.06); color: var(--text-muted); border: 1px solid var(--border); }
.clt-history-list { list-style: none; margin: 0.75rem 0 0; padding: 0.6rem 0 0; border-top: 1px dashed var(--border); display: flex; flex-direction: column; gap: 0.35rem; }
.clt-history-row { display: flex; align-items: center; gap: 0.5rem; font-size: 0.78rem; }

.clt-editor-header { margin-bottom: 1rem; }
.clt-editor-body { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: 1.5rem; align-items: start; }
.clt-field-label { display: block; font-weight: 600; font-size: 0.85rem; margin: 1rem 0 0.3rem; }
.clt-field-label:first-child { margin-top: 0; }
.clt-slot-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.clt-slot-chip { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.2rem 0.6rem; border-radius: 999px; background: var(--gold-soft); font-size: 0.78rem; font-weight: 600; }
.clt-slot-chip-remove { border: none; background: none; cursor: pointer; font-size: 0.9rem; line-height: 1; color: inherit; opacity: 0.65; }
.clt-slot-chip-remove:hover { opacity: 1; }
.clt-add-object-toolbar { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.75rem; }
.clt-object-list { display: flex; flex-direction: column; gap: 0.6rem; }
.clt-object-row { border: 1px solid var(--border); border-radius: 10px; padding: 0.7rem 0.85rem; }
.clt-object-row-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem; }
.clt-object-row-fields { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.clt-object-row-fields label { display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.72rem; color: var(--text-muted); }
.clt-object-row-fields input { min-width: 90px; }
.clt-transform-grid { display: grid; grid-template-columns: repeat(4, minmax(60px, 1fr)); gap: 0.5rem; width: 100%; }
.clt-transform-grid label { font-size: 0.7rem; }
.clt-editor-preview { position: sticky; top: 1rem; }
.clt-editor-actions { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--border); }
@media (max-width: 900px) {
  .clt-editor-body { grid-template-columns: 1fr; }
  .clt-editor-preview { position: static; }
}
.ce-preview-stage .ce-object { position: absolute; }

/* ─── Canvas Layout Templates Phase 2 — Canvas Editing Surface + Starter
   Template Library. Deliberately reuses every generic .ce-* structural
   class above unchanged (.ce-object/.ce-handle/.ce-snap-guide/.ce-toolbar/
   .ce-tool-btn/.ce-canvas-stage/.ce-inspector/.ce-modal/etc.) — the same
   visual language as Canvas Engine, per the UX Review's "reuse as much of
   the existing Canvas Engine infrastructure as possible." Only genuinely
   new elements (mode toggle, component picker cards, starter thumbnails)
   get their own .ces-*/.clt-starter-* rules below. ───────────────────────*/
.ces-body { min-height: 480px; }
.ces-mode-toggle { display: flex; flex-direction: column; align-items: center; gap: 0.3rem; margin-bottom: 0.5rem; }
.ces-mode-toggle-group { display: inline-flex; border: 1px solid var(--border); border-radius: 999px; padding: 2px; }
.ces-mode-btn { border: none; background: transparent; padding: 0.3rem 0.9rem; border-radius: 999px; font-size: 0.78rem; font-weight: 600; cursor: pointer; color: var(--text-muted); }
.ces-mode-btn.active { background: var(--gold); color: #1A1D2B; }
.ces-mode-hint { text-align: center; max-width: 420px; margin: 0; }
/* Milestone 29 (Responsive Canvas Layout), Stage 3 — breakpoint switcher.
   Mirrors .ces-mode-toggle/.ces-mode-btn exactly (same pill-group visual
   language), a sibling control rather than a replacement — Design/Sample
   Data mode and Desktop/Tablet/Mobile breakpoint are orthogonal axes, both
   can be active at once. */
.ces-breakpoint-toggle { display: flex; flex-direction: column; align-items: center; gap: 0.3rem; margin-bottom: 0.5rem; }
.ces-breakpoint-toggle-group { display: inline-flex; border: 1px solid var(--border); border-radius: 999px; padding: 2px; }
.ces-breakpoint-btn { border: none; background: transparent; padding: 0.3rem 0.9rem; border-radius: 999px; font-size: 0.78rem; font-weight: 600; cursor: pointer; color: var(--text-muted); }
.ces-breakpoint-btn.active { background: var(--gold); color: #1A1D2B; }
.ces-breakpoint-hint { text-align: center; max-width: 420px; margin: 0; }
/* An object hidden at the currently-active non-Desktop breakpoint stays
   selectable/editable in the editor (so an author can find and un-hide it)
   but is visually dimmed and hatched rather than removed from the stage —
   deliberately different from o.visible===false's own display:none, which
   is a real Desktop-only hide with no such "still there, just muted" need. */
.ce-object.ces-hidden-at-breakpoint { opacity: 0.35 !important; outline: 1px dashed var(--text-muted); }
.ces-breakpoint-insp-note { margin: 0.3rem 0; font-size: 0.78rem; color: var(--text-muted); }
/* Inspector's own breakpoint-aware additions (Stage 3): a small tag next to
   "Position & Size" naming which breakpoint's override is being edited, and
   the Fit-to-width / Hidden-at-breakpoint controls that only ever render
   when a non-Desktop breakpoint is active (see renderInspector()). */
.ce-insp-bp-tag { display: inline-block; padding: 0.05rem 0.45rem; border-radius: 999px; background: var(--gold); color: #1A1D2B; font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; vertical-align: middle; }
.ce-insp-bp-controls { display: flex; flex-direction: column; gap: 0.4rem; margin: 0.4rem 0 0.6rem; padding: 0.5rem; border: 1px dashed var(--border); border-radius: 8px; }
.ce-insp-hidden-toggle { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; cursor: pointer; }
.ce-insp-hidden-toggle input { margin: 0; }
.ces-component-placeholder { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.15rem; text-align: center; padding: 0.4rem; }
.ces-component-placeholder span { font-size: 0.65rem; opacity: 0.75; text-transform: uppercase; letter-spacing: 0.03em; }
/* Bug fix — sample-data preview content (e.g. the "Programme Listing"
   Runtime Component, whose cards are real `<a href="/programmes/...">`
   wrappers) could overflow this mount's box, but scrolling it inside the
   canvas editor never worked ("the programmes scroll on the actual web
   page though the canvas shows no scrolling") even though `overflow: auto`
   was already set here. Root cause: `pointer-events: none` was applied to
   the WHOLE mount, not just its interactive descendants — the intent was
   only to stop sample `<a>`/`<button>` elements from being clickable/
   navigable while editing (which would otherwise navigate away from the
   canvas or fire real link behaviour inside an editing surface), but
   `pointer-events: none` on the mount itself also blocks wheel/scroll
   events from ever reaching it, since the browser's hit-testing skips it
   entirely. The live published page's equivalent mount (`.clt-slot-mount`,
   `canvas-layout-composer.js`) has no such restriction and scrolls
   normally — this was a canvas-editor-only regression relative to that.
   Fixed by scoping the pointer-events block to just the interactive tags
   (below) instead of the whole mount: a mousedown/wheel over any
   non-interactive part of the sample content (which is most of a card's
   surface — background, padding, gaps in the grid) now hits the mount
   itself (`pointer-events: auto` restored), which both lets native
   wheel-scroll work AND still lets the event bubble up to `.ce-object`'s
   own mousedown listener (`wireObjectPointerEvents` in
   canvas-editing-surface.js) for the normal select/drag-to-reposition
   behaviour — bubbling is unaffected by `pointer-events`, only initial
   hit-testing is. A mousedown/click that lands exactly on an `<a>`/
   `<button>`/form control still can't fire that element's own navigation/
   interaction (pointer-events: none on the specific tag), which is the
   actual behaviour this mechanism was meant to provide. */
.ces-sample-data-mount { width: 100%; height: 100%; overflow: auto; }
.ces-sample-data-mount a, .ces-sample-data-mount button, .ces-sample-data-mount input, .ces-sample-data-mount select, .ces-sample-data-mount textarea, .ces-sample-data-mount label { pointer-events: none; }
.ces-reserved-flag { display: inline-block; margin-top: 0.2rem; padding: 0.05rem 0.4rem; border-radius: 999px; background: rgba(179,38,30,0.1); color: #b3261e; font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; }
.ces-component-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 0.6rem; margin-bottom: 0.75rem; }
.ces-component-card { display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem; text-align: left; border: 1px solid var(--border); border-radius: 10px; padding: 0.7rem 0.8rem; background: #fff; cursor: pointer; }
.ces-component-card:hover { border-color: var(--gold); background: var(--gold-soft); }
.ces-component-card span { font-size: 0.75rem; color: var(--text-muted); }
.ces-component-card--reserved { opacity: 0.75; }
.ces-image-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.5rem; }

.clt-starter-modal { max-width: 720px; width: 92vw; }
.clt-starter-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.85rem; margin-bottom: 0.5rem; }
.clt-starter-card { display: flex; flex-direction: column; gap: 0.35rem; text-align: left; border: 1px solid var(--border); border-radius: 12px; padding: 0.7rem; background: #fff; cursor: pointer; }
.clt-starter-card:hover { border-color: var(--gold); box-shadow: 0 4px 14px rgba(0,0,0,0.08); }
.clt-starter-card span { font-size: 0.75rem; color: var(--text-muted); font-weight: 400; }

/* Video Library — Folder grouping (migration-m68.sql follow-up). Mirrors
   .pb-sequencing-group-title's heading treatment for the same "named-group
   heading over a list" shape, applied here to a real admin-table per folder
   instead of Sequencing's read-only preview list. */
.vl-folder-group { margin-bottom: 1.75rem; }
.vl-folder-group:last-child { margin-bottom: 0; }
.vl-folder-title { font-family: var(--font-display); font-size: 0.95rem; color: var(--gold); margin-bottom: 0.5rem; }
.clt-starter-thumb { position: relative; width: 100%; border-radius: 8px; overflow: hidden; border: 1px solid var(--border); background: #f4f2ec; }


/* The page designer's two-lifecycle action row. Lives here as well as in
   admin.css because the institution shell loads only this file — and the defect
   these styles exist for (a page taken off the PUBLIC site by a button that read
   like an undo) is the institution's to suffer most. */
.wcb-actions { display: flex; flex-wrap: wrap; gap: 1.25rem; align-items: center; }
.wcb-action-group { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.25rem 0.6rem 0.25rem 0.5rem; border: 1px solid var(--border, rgba(0,0,0,0.15)); border-radius: 10px; }
.wcb-action-label { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted, #5B6770); margin-right: 0.15rem; }
.wcb-lifecycle-note { margin: 0.5rem 0 0; }
