/*
 * UCM Components — the shipped set.
 *
 * A real stylesheet rather than class rows, because the class generator emits a
 * declaration list scoped to one selector (`body .ucm-x { … }`) and cannot
 * express `:hover`, `:focus-visible`, a child selector or a media query. A
 * component that cannot describe its own hover state is not a component, so
 * these live here where CSS is CSS.
 *
 * ── Scale ──────────────────────────────────────────────────────────────────
 * Every size-dependent value is a custom property with its own fallback, so the
 * tokens are optional: with no size class present the fallback is the medium
 * size, and `.ucm-size-lg` on any ancestor scales everything inside it because
 * custom properties inherit. That is what makes "make this section tight" a
 * class on one wrapper rather than an edit to forty elements.
 *
 * Colour comes from the theme variables the generator already emits into :root
 * (--ucm-color-primary and friends), so components follow the site's palette
 * without being told about it.
 */

/* ── A base of its own, whatever the theme ─────────────────────────────── */
/*
 * A component sets its own type rather than taking whatever surrounds it.
 *
 * Measured on a fresh install: under Twenty Twenty-Five a card's text came out
 * at 21.8px, weight 300, in the theme's Manrope with its letter-spacing, and
 * under the UCM Theme — which ships no CSS by design — in Times New Roman,
 * because nothing names a font until Site Fonts does. The same page looked
 * like two different products.
 *
 * Only on the outermost component (`ucm-light` / `ucm-dark` is on every
 * component root), so a component inside another keeps what it is given.
 * Zero specificity: any utility, and every component rule below, wins. The
 * values are the site's own where it has them — Site Fonts, the page's base
 * text size through rem — and a plain system font where it has not.
 */
:where(.ucm-light, .ucm-dark):where(:not(.ucm-light *, .ucm-dark *)) {
  font-family: var(--ucm-font-primary, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: normal;
}
/* One class of weight: a theme styles headings by element (0,0,1), which a
   zero-specificity rule loses to. A utility (0,1,1) still wins over this. */
:is(.ucm-light, .ucm-dark) :where(h1, h2, h3, h4, h5, h6) { letter-spacing: normal; }
:where(.ucm-light, .ucm-dark) :where(button, input, select, textarea) { font-family: inherit; }

/* ── Size: padding, gap, radius and type together ───────────────────────── */
.ucm-size-xs { --ucm-c-pad: .3125rem; --ucm-c-pad-x: .5625rem;  --ucm-c-gap: .3125rem; --ucm-c-radius: .25rem;   --ucm-c-font: .75rem; }
.ucm-size-sm { --ucm-c-pad: .4375rem; --ucm-c-pad-x: .8125rem;  --ucm-c-gap: .5rem;    --ucm-c-radius: .375rem;  --ucm-c-font: .8125rem; }
.ucm-size-md { --ucm-c-pad: .625rem;  --ucm-c-pad-x: 1rem;      --ucm-c-gap: .75rem;   --ucm-c-radius: .5rem;    --ucm-c-font: .875rem; }
.ucm-size-lg { --ucm-c-pad: .875rem;  --ucm-c-pad-x: 1.25rem;   --ucm-c-gap: 1rem;     --ucm-c-radius: .625rem;  --ucm-c-font: 1rem; }
.ucm-size-xl { --ucm-c-pad: 1.125rem; --ucm-c-pad-x: 1.5rem;    --ucm-c-gap: 1.25rem;  --ucm-c-radius: .75rem;   --ucm-c-font: 1.125rem; }

/* ── Radius, as its own axis ────────────────────────────────────────────── */
.ucm-round-none { --ucm-c-radius: 0; }
.ucm-round-sm   { --ucm-c-radius: .25rem; }
.ucm-round-md   { --ucm-c-radius: .5rem; }
.ucm-round-lg   { --ucm-c-radius: .875rem; }
.ucm-round-xl   { --ucm-c-radius: 1.25rem; }
.ucm-round-full { --ucm-c-radius: 999px; }

/* Borders off, background only. */
.ucm-flat { --ucm-c-border: transparent; }

/*
 * Surface, ink and border resolve at the point of use with their own fallbacks
 * rather than being declared on each component.
 *
 * Declaring `--ucm-c-surface: #fff` on `.ucm-card` would put the value on the
 * card itself, and a value on the element always beats one inherited from an
 * ancestor — so `.ucm-dark` on a wrapper could never reach it. Every token here
 * is therefore read as `var(--token, fallback)`: the fallback is the light
 * value, and any ancestor that sets the token wins.
 *
 * `--ucm-c-border-color` is the exception and stays on the element, because it
 * derives from `--ucm-c-border` — which is what `.ucm-flat` sets, and that has
 * to be resolvable per element for "flat inside this one section" to work.
 */
.ucm-btn, .ucm-card, .ucm-input, .ucm-select, .ucm-textarea,
.ucm-alert, .ucm-badge, .ucm-nav, .ucm-listing, .ucm-avatar {
  --ucm-c-border-color: var(--ucm-c-border, #e2e5ea);
  box-sizing: border-box;
}

/* ── Button ─────────────────────────────────────────────────────────────── */
.ucm-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: .5em;
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border: 1px solid transparent;
  border-radius: var(--ucm-c-radius, .5rem);
  font-size: var(--ucm-c-font, .875rem);
  font-weight: 600; line-height: 1.2;
  text-decoration: none !important; white-space: nowrap; cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease, box-shadow .15s ease;
}
/* A visible focus ring for keyboards only — never the mouse halo that makes
   people add `outline:none` and break keyboard access for everyone. */
.ucm-btn:focus-visible,
.ucm-input:focus-visible, .ucm-select:focus-visible, .ucm-textarea:focus-visible {
  outline: 2px solid var(--ucm-color-primary, #2563eb);
  outline-offset: 2px;
}
.ucm-btn:disabled, .ucm-btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }

/*
 * Each variant names its ink as a custom property rather than as a colour.
 *
 * A button is nearly always an <a>, and a theme styling `body a:hover` has
 * specificity (0,1,1) — which beats `.ucm-btn-primary` (0,1,0). The result is a
 * primary button whose label turns the site's link colour on hover, on top of
 * the primary background: unreadable, and reported as exactly that. Restating
 * the colour for every link state at (0,2,1) settles it, and it reads from the
 * property so each variant keeps its own ink without repeating the list.
 */
.ucm-btn { color: var(--ucm-c-btn-ink, var(--ucm-c-ink, var(--ucm-color-text, #1f2430))); }
a.ucm-btn, a.ucm-btn:link, a.ucm-btn:visited,
a.ucm-btn:hover, a.ucm-btn:focus, a.ucm-btn:active {
  color: var(--ucm-c-btn-ink, var(--ucm-c-ink, var(--ucm-color-text, #1f2430)));
}

.ucm-btn-primary   { --ucm-c-btn-ink: #fff; background: var(--ucm-color-primary, #2563eb); }
.ucm-btn-primary:hover   { background: var(--ucm-color-primary-dark, #1d4ed8); }

/*
 * Raised, not flat.
 *
 * A secondary button painted with the surface token disappears into the card it
 * sits on — which is invisible in light, where the surface is white and the page
 * usually is not, and obvious in dark, where they are the same colour. The token
 * is unset in light, so it falls back to the surface and nothing changes there.
 */
.ucm-btn-secondary { background: var(--ucm-c-raised, var(--ucm-c-surface, #ffffff)); border-color: var(--ucm-c-border-color); }
.ucm-btn-secondary:hover { background: var(--ucm-c-raised-hover, #f6f7f9); }

.ucm-btn-ghost     { background: transparent; }
.ucm-btn-ghost:hover, a.ucm-btn-ghost:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }

.ucm-btn-danger    { --ucm-c-btn-ink: #fff; background: var(--ucm-color-danger, #dc2626); }
.ucm-btn-danger:hover    { background: var(--ucm-color-danger-dark, #b91c1c); }

/*
 * One per semantic role in the palette, because a palette with eight roles and
 * a button with four styles is a library answering a narrower question than the
 * site is asking. Each reads the role and its -dark twin, so a button follows
 * whatever the theme sets and needs no colour of its own.
 *
 * Warning and success take dark ink. Both are light colours in most palettes —
 * amber and a mid green — and white on them is the pairing that cannot be read:
 * measured against the shipped fallbacks and against a real site's palette,
 * white on green came out at 2.5:1, where dark ink gives better than 4.5:1.
 */
.ucm-btn-accent    { --ucm-c-btn-ink: #fff; background: var(--ucm-color-accent, #7c3aed); }
.ucm-btn-accent:hover    { background: var(--ucm-color-accent-dark, #5b21b6); }

.ucm-btn-success   { --ucm-c-btn-ink: #10221a; background: var(--ucm-color-success, #16a34a); }
.ucm-btn-success:hover   { background: var(--ucm-color-success-dark, #15803d); }

.ucm-btn-warning   { --ucm-c-btn-ink: #1f2430; background: var(--ucm-color-warning, #f59e0b); }
.ucm-btn-warning:hover   { background: var(--ucm-color-warning-dark, #d97706); }

.ucm-btn-info      { --ucm-c-btn-ink: #fff; background: var(--ucm-color-info, #3b82f6); }
.ucm-btn-info:hover      { background: var(--ucm-color-info-dark, #1d4ed8); }

.ucm-btn-neutral   { --ucm-c-btn-ink: #fff; background: var(--ucm-color-neutral, #6b7280); }
.ucm-btn-neutral:hover   { background: var(--ucm-color-neutral-dark, #374151); }

/* The quiet one that still reads as a button: the role's colour, drawn rather
   than filled. currentColor keeps the border and the label in step. */
.ucm-btn-outline   { --ucm-c-btn-ink: var(--ucm-color-primary, #2563eb); background: transparent; border-color: currentColor; }
.ucm-btn-outline:hover   { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }

/*
 * Five more that a site reaches for, each from the palette with a fallback so
 * it works on a site that defines none.
 *
 * Soft: the primary as a tint behind its own colour — a quieter primary that
 *   still says which button matters. Contrast: the ink as the fill and the
 *   surface as the label, which is what flips inside `ucm-dark` without a
 *   second class. White: for a button on a picture or a dark band, where every
 *   other fill disappears. Link: a button that is only its words, underlined
 *   when pointed at — drawn with a shadow, because the base rule has to say
 *   `text-decoration: none !important` to beat a theme's link styles.
 *   Gradient: primary into accent, for the one call to action on a page.
 */
.ucm-btn-soft      { --ucm-c-btn-ink: var(--ucm-color-primary, #2563eb);
                     background: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 12%, transparent); }
.ucm-btn-soft:hover      { background: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 20%, transparent); }
/* On dark the tint reads as mud and the primary as dim ink: lift both. */
.ucm-dark .ucm-btn-soft, .ucm-btn-soft.ucm-dark {
  --ucm-c-btn-ink: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 55%, #ffffff);
  background: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 24%, transparent); }
.ucm-dark .ucm-btn-soft:hover, .ucm-btn-soft.ucm-dark:hover { background: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 34%, transparent); }

.ucm-btn-contrast  { --ucm-c-btn-ink: var(--ucm-c-surface, #ffffff); background: var(--ucm-c-ink, #1f2430); }
.ucm-btn-contrast:hover  { background: color-mix(in srgb, var(--ucm-c-ink, #1f2430) 85%, var(--ucm-c-surface, #ffffff)); }

.ucm-btn-white     { --ucm-c-btn-ink: #1f2430; background: #ffffff; border-color: rgba(0,0,0,.06); }
.ucm-btn-white:hover     { background: #f3f4f6; }

.ucm-btn-link      { --ucm-c-btn-ink: var(--ucm-color-primary, #2563eb); background: transparent;
                     padding-inline: 0; border-radius: 0; }
.ucm-btn-link:hover      { box-shadow: inset 0 -1px 0 currentColor; }

.ucm-btn-gradient  { --ucm-c-btn-ink: #fff;
                     background: linear-gradient(135deg, var(--ucm-color-primary, #2563eb), var(--ucm-color-accent, #7c3aed)); }
.ucm-btn-gradient:hover  { filter: brightness(1.08); }

.ucm-btn-block { display: flex; width: 100%; }

/* ── Card ───────────────────────────────────────────────────────────────── */
.ucm-card {
  background: var(--ucm-c-surface, #ffffff);
  border: 1px solid var(--ucm-c-border-color);
  border-radius: var(--ucm-c-radius, .5rem);
  padding: calc(var(--ucm-c-pad, .625rem) * 1.8);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
}
/*
 * The spacing inside a component names the component that owns it.
 *
 * Not decoration: the editor strips Gutenberg's own block spacing with a rule
 * at (0,1,0), chosen so an authored `ucm-mb-6` — which the generator emits as
 * `body .ucm-mb-6`, (0,1,1) — still wins. A component's internal margin written
 * as `.ucm-card-head` is (0,1,0) too, so it lost that tie and every card
 * collapsed in the canvas while spacing correctly on the page. Naming the owner
 * makes it (0,2,0), which is both true and enough.
 */
.ucm-card .ucm-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--ucm-c-gap, .75rem); margin-bottom: var(--ucm-c-gap, .75rem); }
/*
 * A title and its line are named twice rather than owned by the card, because
 * they are used outside one: three features in a row have no box round them and
 * are still a heading and a sentence. Scoped to `.ucm-card` they were unstyled
 * there — the theme's own heading size, the theme's own margins — which is what
 * made a row of three read as three paragraphs with gaps. Twice is the same
 * weight the owner gave them, for the reason above.
 */
/*
 * Split on purpose, and the split is the whole point of the doubling.
 *
 * What needs the weight is the margin: the editor strips block spacing with a
 * rule at (0,1,0), and a component's own margin has to beat it or every card
 * collapses in the canvas. Nothing else here needs it — and when the font size
 * carried that weight too, `ucm-text-4xl` on a heading did nothing, because a
 * utility is (0,1,1) at most and cannot reach past (0,2,0). A hero's headline
 * came out the size of a card's.
 *
 * So: margin twice, everything else once, where a utility can still win.
 */
/* Weighted like a utility rather than doubled, and for the same two reasons at
   once. These have to beat the theme's own heading and paragraph margins when a
   title is used outside a card — that is why they were `.ucm-card-title
   .ucm-card-title` — but at 0,2,0 they also beat `body .ucm-mb-4`, so a card
   heading given a margin utility silently lost it and sat flat against whatever
   came next. `body .ucm-card-title` is 0,1,1: exactly what the generated
   utilities weigh, in a stylesheet that loads before them, so the theme still
   loses and an explicit utility still wins. */
body .ucm-card-title { margin: 0; }
body .ucm-card-sub { margin: .25rem 0 0; }
.ucm-card-title { font-size: calc(var(--ucm-c-font, .875rem) * 1.25); font-weight: 700; line-height: 1.3; }
.ucm-card-sub { font-size: var(--ucm-c-font, .875rem); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-card .ucm-card-foot { display: flex; align-items: center; gap: var(--ucm-c-gap, .75rem); margin-top: calc(var(--ucm-c-gap, .75rem) * 1.5); }

/* ── Form controls ──────────────────────────────────────────────────────── */
.ucm-field { display: flex; flex-direction: column; min-width: 0; }
/* A fieldset is the right element for a group of choices and arrives with a
   border, a margin and inline padding that no other field has. */
fieldset.ucm-field { margin: 0; padding: 0; border: 0; min-inline-size: 0; }
fieldset.ucm-field > legend { padding: 0; }
.ucm-field .ucm-label { margin-bottom: .35rem; font-size: calc(var(--ucm-c-font, .875rem) * .92); font-weight: 600; color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
.ucm-field .ucm-help  { margin-top: .35rem; font-size: calc(var(--ucm-c-font, .875rem) * .88); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-field .ucm-error { margin-top: .35rem; font-size: calc(var(--ucm-c-font, .875rem) * .88); color: var(--ucm-color-danger, #dc2626); }
/* An empty error slot must take no space, or every form carries a gap that
   looks like a mistake until something goes wrong. */
.ucm-field .ucm-error:empty { display: none; }

.ucm-input, .ucm-select, .ucm-textarea {
  width: 100%;
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border: 1px solid var(--ucm-c-border-color);
  border-radius: var(--ucm-c-radius, .5rem);
  font-size: var(--ucm-c-font, .875rem);
  font-family: inherit; line-height: 1.4;
  background: var(--ucm-c-raised, var(--ucm-c-surface, #ffffff)); color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  transition: border-color .15s ease, box-shadow .15s ease;
}
.ucm-input:hover, .ucm-select:hover, .ucm-textarea:hover { border-color: var(--ucm-c-border-hover, #cbd1d9); }
.ucm-input:focus, .ucm-select:focus, .ucm-textarea:focus {
  border-color: var(--ucm-color-primary, #2563eb);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ucm-color-primary, #2563eb) 18%, transparent);
}
.ucm-input[aria-invalid="true"], .ucm-select[aria-invalid="true"], .ucm-textarea[aria-invalid="true"] {
  border-color: var(--ucm-color-danger, #dc2626);
}
/*
 * Stated twice, deliberately.
 *
 * `input[type="text"]` is (0,1,1) and outranks a single class (0,1,0) — and
 * that selector is everywhere: WordPress's own forms.min.css uses it, and it
 * reaches the editor canvas, so a dark form previewed white while the same
 * markup rendered dark on the page. Themes do the same thing. Doubling our own
 * class is (0,2,0), which wins without `!important` and without naming anybody
 * else's stylesheet — so it keeps working when they change theirs.
 */
.ucm-input.ucm-input, .ucm-select.ucm-select, .ucm-textarea.ucm-textarea {
  /* background-COLOR, not the shorthand: the shorthand resets background-image
     too, and the select draws its arrow with one. */
  background-color: var(--ucm-c-raised, var(--ucm-c-surface, #ffffff));
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  border-color: var(--ucm-c-border-color);
}

/*
 * A list, not a closed control: the drawn arrow and the single-line height are
 * both wrong for one, and `appearance: none` on a multiple select hides the
 * selection highlight in some browsers.
 */
.ucm-select[multiple]:not(.ucm-picker-source) {
  appearance: auto; -webkit-appearance: auto;
  background-image: none; padding-right: var(--ucm-c-pad-x, 1rem);
  /* Not once the picker has taken over: a min-height outranks the 1px the
     visually-hidden rule sets, leaving a 112px box behind the control. */
  min-height: 7rem;
}
.ucm-select[multiple] option { padding: .25rem .35rem; }

.ucm-textarea { min-height: 6rem; resize: vertical; }
/* The native arrow is drawn in the platform's own colours and ignores the
   component's, so it is replaced rather than left to clash. */
.ucm-select {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .7rem center;
  background-size: 1.1em;
  padding-right: 2.4rem;
}

/* ── Badge ──────────────────────────────────────────────────────────────── */
.ucm-badge {
  display: inline-flex; align-items: center; gap: .35em;
  /* A badge hugs its text. As a grid item it would otherwise stretch to the
     full column, which is how a status ended up as a wide tinted bar. */
  justify-self: start; align-self: center; width: fit-content;
  padding: .25em .7em;
  border: 1px solid transparent;
  border-radius: var(--ucm-c-radius, 999px);
  font-size: calc(var(--ucm-c-font, .875rem) * .85);
  font-weight: 600; line-height: 1.45; white-space: nowrap;
}
/* Tints rather than solid fills: a row of solid badges shouts, and a grey one
   on a tinted row reads as a rendering fault. */
.ucm-badge-neutral { background: #fff;     color: #475569; border-color: #d8dde3; }
.ucm-badge-success { background: #dcfce7;  color: #166534; border-color: #bbf7d0; }
.ucm-badge-warning { background: #fef3c7;  color: #92400e; border-color: #fde68a; }
.ucm-badge-danger  { background: #fee2e2;  color: #991b1b; border-color: #fecaca; }
.ucm-badge-info    { background: #dbeafe;  color: #1e40af; border-color: #bfdbfe; }

/* ── Alert ──────────────────────────────────────────────────────────────── */
.ucm-alert {
  display: flex; align-items: flex-start; gap: var(--ucm-c-gap, .75rem);
  padding: calc(var(--ucm-c-pad, .625rem) * 1.4) calc(var(--ucm-c-pad-x, 1rem) * 1.1);
  border: 1px solid transparent;
  border-left-width: 4px;
  border-radius: var(--ucm-c-radius, .5rem);
  font-size: var(--ucm-c-font, .875rem); line-height: 1.6;
}
.ucm-alert-info    { background: #eff6ff; border-color: #bfdbfe; border-left-color: #3b82f6; color: #1e3a8a; }
.ucm-alert-success { background: #f0fdf4; border-color: #bbf7d0; border-left-color: #16a34a; color: #14532d; }
.ucm-alert-warning { background: #fffbeb; border-color: #fde68a; border-left-color: #f59e0b; color: #78350f; }
.ucm-alert-danger  { background: #fef2f2; border-color: #fecaca; border-left-color: #dc2626; color: #7f1d1d; }
.ucm-alert .ucm-alert-title { margin: 0 0 .25rem; font-weight: 700; }
.ucm-alert .ucm-alert-body  { margin: 0; }

/* ── Navigation ─────────────────────────────────────────────────────────── */
.ucm-nav { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ucm-c-gap, .75rem); }
.ucm-nav-vertical { flex-wrap: nowrap; flex-direction: column; align-items: stretch; gap: .15rem; }
.ucm-nav-link {
  display: flex; align-items: center; gap: .55em;
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border-radius: var(--ucm-c-radius, .5rem);
  font-size: var(--ucm-c-font, .875rem); font-weight: 600;
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); text-decoration: none !important;
  transition: background-color .15s ease, color .15s ease;
}
a.ucm-nav-link, a.ucm-nav-link:link, a.ucm-nav-link:visited,
a.ucm-nav-link:hover, a.ucm-nav-link:focus, a.ucm-nav-link:active {
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
}
.ucm-nav-link:hover, a.ucm-nav-link:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }
/* aria-current is the accessible way to say "you are here", so it is also what
   drives the styling — one fact, not a class kept in step by hand. */
.ucm-nav-link[aria-current="page"], a.ucm-nav-link[aria-current="page"] { background: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 12%, transparent); color: var(--ucm-color-primary, #2563eb); }
.ucm-nav .ucm-nav-heading, .ucm-stack .ucm-nav-heading { padding: 0 var(--ucm-c-pad-x, 1rem); margin: .75rem 0 .35rem; font-size: .7rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/*
 * A group of links that opens, with no script to write.
 *
 * <details> already does this, and a <summary> is a button the keyboard and a
 * screen reader both know. Styled as a nav link so an open group and a plain
 * link are the same row until one of them has children.
 */
.ucm-nav-group { border-radius: var(--ucm-c-radius, .5rem); }
.ucm-nav-group > summary {
  display: flex; align-items: center; gap: .55em;
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border-radius: var(--ucm-c-radius, .5rem);
  font-size: var(--ucm-c-font, .875rem); font-weight: 600;
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  cursor: pointer; list-style: none;
}
.ucm-nav-group > summary::-webkit-details-marker { display: none; }
.ucm-nav-group > summary:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }
/* The chevron is the last thing on the row and turns when the group opens. Its
   own element rather than a pseudo, so a component can put an icon in front of
   the label without the two fighting over ::after. */
.ucm-nav-caret {
  flex: none; width: 0; height: 0; margin-inline-start: auto;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor; opacity: .6;
  transition: transform .15s ease;
}
.ucm-nav-group[open] > summary > .ucm-nav-caret { transform: rotate(180deg); }
/* Indented to the depth of a link's own label, so a child lines up under the
   words above it rather than under the icon. */
.ucm-nav-sub {
  display: flex; flex-direction: column; gap: .1rem;
  padding-inline-start: calc(var(--ucm-c-pad-x, 1rem) + 1.15em + .55em);
}
.ucm-nav-sub .ucm-nav-link { padding-block: calc(var(--ucm-c-pad, .625rem) * .7); font-weight: 500; }

/* ── The shell of an application ────────────────────────────────────────── */
/*
 * A bar across the top and a column down the side. Neither is fixed: a page
 * decides that, and a component that pinned itself would be unusable in the
 * canvas and in a preview both. What they do here is lay their own contents
 * out — which is the part every app bar gets wrong by hand.
 */
.ucm-appbar {
  display: flex; align-items: center; gap: var(--ucm-c-gap, .75rem);
  padding: calc(var(--ucm-c-pad, .625rem) * .8) var(--ucm-c-pad-x, 1rem);
  background: var(--ucm-c-surface, #ffffff);
  border-bottom: 1px solid var(--ucm-c-border, #e2e5ea);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
}
/* Everything after this sits on the right. A spacer element rather than
   `margin-left:auto` on a named child, so the order of what follows is the
   markup's business. */
.ucm-appbar-gap { flex: 1 1 auto; min-width: 0; }
.ucm-appbar-side {
  display: flex; flex-direction: column; min-width: 0;
  background: var(--ucm-c-surface, #ffffff);
  /*
   * All four edges, then the one that is wanted.
   *
   * This is a popover, and the UA gives a popover `border: solid` — three
   * pixels of black on every side. Naming only `border-inline-end` overrode one
   * of them and left the other three, so a light sidebar came out framed in
   * black on a light page. Every popover here resets the shorthand for the same
   * reason; this one was the exception.
   */
  border: 0;
  border-inline-end: 1px solid var(--ucm-c-border, #e2e5ea);
  padding: var(--ucm-c-pad-x, 1rem) calc(var(--ucm-c-pad-x, 1rem) * .6);
  gap: var(--ucm-c-gap, .75rem);
}
/* The logo's place, whether or not there is a logo yet. */
.ucm-appbar-brand {
  display: flex; align-items: center; gap: .6rem;
  padding: calc(var(--ucm-c-pad, .625rem) * .5) calc(var(--ucm-c-pad-x, 1rem) * .5);
  font-weight: 800; letter-spacing: -.01em;
  font-size: calc(var(--ucm-c-font, .875rem) * 1.15);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
}
.ucm-appbar-brand .ucm-card-media { width: 2rem; aspect-ratio: 1; flex: none; }
/* A round button holding nothing but a mark, with room for a count on it. */
.ucm-iconbtn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  gap: .4rem; flex: none;
  min-width: 2.25rem; min-height: 2.25rem; padding: .35rem .5rem;
  border: 0; border-radius: var(--ucm-c-radius, .5rem);
  background: none; color: inherit; font: inherit;
  font-size: var(--ucm-c-font, .875rem); cursor: pointer;
}
.ucm-iconbtn:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }
/*
 * Folding the column away, with no script.
 *
 * A checkbox is the state and its label is the button: `:has()` lets the body
 * read a box anywhere inside it, so the control can live in the bar and the
 * thing it folds can live in another area entirely — which is exactly what
 * these two are, separate areas that only meet on the page.
 *
 * The phone already has a burger, and it is a different button on purpose:
 * there the column is a popover, so it gets the top layer, Escape and
 * click-away, none of which a checkbox can do. One is shown at a time.
 */
.ucm-fold {
  position: absolute; width: 1px; height: 1px;
  opacity: 0; pointer-events: none; margin: 0;
}
@media (min-width: 768px) {
  body.ucm-app:has( .ucm-fold:checked ) { grid-template-columns: 0 minmax(0, 1fr); }
  body.ucm-app:has( .ucm-fold:checked ) > .ucm-appbar-side { display: none; }
}

/* The words beside a mark, which a narrow bar drops rather than overflow. The
   mark stays: an avatar and a globe are still the account and the language. */
.ucm-appbar-label { white-space: nowrap; }
@media (max-width: 560px) {
  .ucm-appbar-label { display: none; }
}
.ucm-iconbtn-count {
  position: absolute; top: .1rem; inset-inline-end: .05rem;
  min-width: 1.05rem; padding: 0 .2rem;
  border-radius: 1rem; background: var(--ucm-color-danger, #dc2626); color: #fff;
  font-size: .62rem; font-weight: 700; line-height: 1.05rem; text-align: center;
}

/*
 * The shell itself, on the page's own <body>.
 *
 * A UCM template renders a header area, then any before-areas, then
 * `<main id="ucm-content">` — all siblings, with nothing wrapping them. That is
 * exactly the three things an app shell has to place, so the layout goes on the
 * body rather than asking a component to contain a column it does not own. The
 * page creator sets this class; nothing else needs to know.
 */
body.ucm-app {
  /* How tall the bar is, for the two things that have to sit under it. Content
     decides the real height; this is what `sticky` and `calc` can be written
     against, and an author who changes the bar changes this with it. */
  --ucm-app-bar: 3.75rem;
  display: grid; min-height: 100vh;
  grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
  /* The rows are named, not left to `auto`. A grid taller than its content
     stretches auto rows to fill it — so on a short page the bar came out two
     hundred and ninety pixels tall with its buttons floating in the middle of
     it. The bar is as tall as a bar; the rest is the rest. */
  grid-template-rows: auto 1fr;
  grid-template-areas: "bar bar" "side main";
}
/*
 * Staying put is a class, not something the shell does behind your back.
 *
 * The app bar ships with `ucm-stick-top` on it, so it is sticky out of the box
 * and the Designer's Sticky control can take it off again — which is the whole
 * shape of a setting here: a setting is a class, and every class is visible in
 * the list on the block. The same pair works on a site header, a toolbar above
 * a table, or a bar of actions at the foot of a form.
 */
.ucm-stick-top    { position: sticky; top: 0;    z-index: 30; }
.ucm-stick-bottom { position: sticky; bottom: 0; z-index: 30; }
body.ucm-app > .ucm-appbar      { grid-area: bar; }
body.ucm-app > .ucm-appbar-side { grid-area: side; }
/*
 * Where the content sits is the shell's business; how much room it has is the
 * page's. No padding is set here at all — an id outranks every class, so a
 * default here could not be overridden by the width and room classes that exist
 * to set it. The page creator writes those; a page assembled by hand says so
 * with the same two classes.
 */
body.ucm-app > #ucm-content     { grid-area: main; min-width: 0; }
/*
 * Wide enough for a column beside the content: it is the column, and it stays
 * put under the bar with a scrollbar of its own when the list is long.
 *
 * 768 is `md`, which is also where `ucm-hidden--md` takes the burger away — so
 * the button that opens the column and the width at which the column stops
 * needing opening are the same number. At 860 there was a hundred pixels where
 * the burger was gone and the column was still stacked above the content.
 */
@media (min-width: 768px) {
  /*
   * The column is a popover so a phone can open it over the page. Here it is
   * simply the column, and the top layer never comes into it — which needs
   * saying, because an author rule is all it takes to beat the UA's
   * closed-popover rule, and `.ucm-appbar-side` already declares `display`.
   */
  .ucm-appbar-side[popover] {
    display: flex; position: static; inset: auto;
    width: auto; max-width: none; height: auto; max-height: none; margin: 0;
  }
  /*
   * The column is as tall as the page, not as tall as its links.
   *
   * `align-self: start` left it ending under the last link with the page
   * showing below — which on a dark column against a light page is a band of
   * the wrong colour halfway down. Stretching it fills the row, and the minimum
   * covers the case where the page is shorter than the window.
   */
  body.ucm-app > .ucm-appbar-side {
    align-self: stretch;
    min-height: calc(100vh - var(--ucm-app-bar));
  }
}

/*
 * On a phone it opens over the page rather than sitting above it.
 *
 * Stacked between the bar and the content it was a screenful of links to scroll
 * past before reaching the page — which is the one thing an app sidebar must
 * not be on a phone. `popover` gives the top layer, the click-away and Escape,
 * and the burger already points at it.
 */
@media (max-width: 767px) {
  body.ucm-app {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto 1fr;
    grid-template-areas: "bar" "side" "main";
  }
  /* Stated, not left to the UA: any author declaration beats the UA's rule for
     a closed popover, and this component declares `display: flex`. */
  .ucm-appbar-side[popover]:not(:popover-open) { display: none; }
  .ucm-appbar-side[popover] {
    position: fixed; inset: var(--ucm-app-bar, 3.75rem) auto 0 0;
    width: min(20rem, 84vw); max-width: none; height: auto; margin: 0;
    border: 0; border-inline-end: 1px solid var(--ucm-c-border, #e2e5ea);
    border-radius: 0;
    box-shadow: 0 0 2rem rgba(16, 24, 40, .25);
    overflow-y: auto;
  }
  /*
   * The bar stays out of it.
   *
   * A popover is in the top layer and its backdrop fills the window, so opening
   * the column dimmed the bar and put the overlay over it — including the
   * burger that had just been pressed, which is the one control somebody is
   * looking for next. The backdrop starts where the column does.
   */
  .ucm-appbar-side[popover]::backdrop {
    background: rgba(16, 24, 40, .38);
    inset: var(--ucm-app-bar, 3.75rem) 0 0 0;
  }
}

/* ── Table ──────────────────────────────────────────────────────────────── */
.ucm-listing { width: 100%; }
.ucm-listing-head, .ucm-listing-row {
  display: grid; gap: var(--ucm-c-gap, .75rem); align-items: center;
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
}
.ucm-listing-head {
  font-size: .7rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); border-bottom: 1px solid var(--ucm-c-border-color);
}
.ucm-listing-row {
  border-bottom: 1px solid var(--ucm-c-border-color);
  border-radius: var(--ucm-c-radius, .5rem);
  transition: background-color .12s ease;
}
.ucm-listing-row:last-child { border-bottom: 0; }
.ucm-listing-row:hover { background: var(--ucm-c-wash-soft, rgba(0,0,0,.025)); }
.ucm-listing-row > * { min-width: 0; overflow-wrap: anywhere; }
/* A title that is a link reads as the title: the card or row is the thing you
   click, not a line of underlined browser-blue. Underlined only while pointed at. */
a:where(.ucm-card-title, .ucm-board-card-title), :where(.ucm-card-title, .ucm-board-card-title, .ucm-row-main) a { color: inherit; text-decoration: none; }
a:where(.ucm-card-title, .ucm-board-card-title):hover, :where(.ucm-card-title, .ucm-board-card-title, .ucm-row-main) a:hover { text-decoration: underline; }
/* A column title that sorts: it reads as the title, not as a button, and shows
   an arrow for the sort in force (data.js sets data-sort-dir). (0,1,0) — above a
   theme's `button`, below every utility. */
.ucm-listing-head :where(button) {
  appearance: none; background: none; border: 0; padding: 0; margin: 0; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit; text-align: inherit;
  display: inline-flex; align-items: center; gap: .3em;
}
.ucm-listing-head :where(button.ucm-num) { justify-content: flex-end; }
.ucm-listing-head :where(button):hover { color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
.ucm-listing-head :where(button):focus-visible { outline: 2px solid var(--ucm-color-primary, #2563eb); outline-offset: 2px; border-radius: 2px; }
.ucm-listing-head :where(button[data-control="sort"])::after { content: "↕"; opacity: .35; font-size: 1.1em; }
.ucm-listing-head :where(button[data-sort-dir="asc"])::after { content: "↑"; opacity: 1; }
.ucm-listing-head :where(button[data-sort-dir="desc"])::after { content: "↓"; opacity: 1; }
/* Below this width the columns become lines, and each prints the label the
   header would have given it — a bare date under a bare name says nothing about
   which is which once the header is gone. */
@media (max-width: 760px) {
  .ucm-listing-head { display: none; }
  .ucm-listing-row { grid-template-columns: auto minmax(0, 1fr); gap: .2rem .5rem; align-items: baseline; }
  /*
   * A cell with no label is a line of its own.
   *
   * The narrow layout is a label column and a value column, which only works
   * for cells that have a label to put in it. The one that usually has none is
   * the first — the record's name — and left in the grid it takes the `auto`
   * track, sizes to its own text and pushes the row wider than the phone.
   */
  .ucm-listing-row > :not([data-label]) { grid-column: 1 / -1; min-width: 0; }
  .ucm-listing-row > [data-label] { display: contents; }
  .ucm-listing-row > [data-label]::before {
    content: attr(data-label); grid-column: 1; justify-self: start; white-space: nowrap;
    font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
  }
}

/* ── Layout primitives ──────────────────────────────────────────────────── */
.ucm-stack   { display: flex; flex-direction: column; gap: var(--ucm-c-gap, .75rem); }
.ucm-cluster { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ucm-c-gap, .75rem); }
.ucm-split   { display: flex; align-items: center; justify-content: space-between; gap: var(--ucm-c-gap, .75rem); }
.ucm-stack > *, .ucm-cluster > *, .ucm-split > * { min-width: 0; }
/*
 * Down the middle. One class rather than one per layout: a stack centres what
 * is in it by cross-axis alignment and a block by text alignment, and saying
 * both means the class works wherever it is put — a mark, a title and a line
 * under it all sit on the same centre line.
 */
.ucm-middle { align-items: center; text-align: center; }
/*
 * Columns that only ever divide the number of things in them.
 *
 * `auto-fit` fills the width, which is right for a list somebody keeps adding
 * to and wrong for a fixed composition: four figures in a three-column grid is
 * three and then one on its own, which reads as a mistake rather than a layout.
 * These step through divisors only — four goes to two and then to one, three
 * goes straight to one — so a shipped component never orphans its last item.
 *
 * A list that grows past its count can orphan, and should: that is the author's
 * content, not the component's shape.
 */
.ucm-even-2, .ucm-even-3, .ucm-even-4, .ucm-even-6 {
  display: grid;
  gap: calc(var(--ucm-c-gap, .75rem) * 1.4);
}
.ucm-even-2 > *, .ucm-even-3 > *, .ucm-even-4 > *, .ucm-even-6 > * { min-width: 0; }
.ucm-even-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ucm-even-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ucm-even-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.ucm-even-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }

@media (max-width: 1024px) {
  .ucm-even-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ucm-even-6 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* Three has no even step down, so it goes to one rather than to two and a half. */
@media (max-width: 720px) {
  .ucm-even-3 { grid-template-columns: 1fr; }
  .ucm-even-6 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .ucm-even-2, .ucm-even-4, .ucm-even-6 { grid-template-columns: 1fr; }
}

/*
 * How wide a part of a page is, and how much room it has inside it.
 *
 * Shipped here rather than composed from utilities, because a utility only
 * exists on a site whose library provides it and whose generator has seen it
 * used — and the page creator writes these before anything has used anything.
 * A class the plugin itself writes has to be one the plugin itself defines.
 *
 * Centred, because a capped box that is not is a box pinned to the left, which
 * is never what somebody choosing a width meant.
 */
/*
 * The box stays the width it was; its contents are pulled into a column.
 *
 * A header is full-bleed and its row of links is centred — capping the header
 * itself gives a bar with the page showing either side of it, which is a
 * different design and almost never the one somebody means. So the cap is
 * expressed as inline padding rather than as a max width: the background runs
 * edge to edge, and everything inside sits in a column of the width asked for.
 *
 * `max()` keeps a minimum gutter, so a window narrower than the column does not
 * push the contents against the glass.
 */
/*
 * The class is named twice on purpose. A component's own rule is a class too,
 * and several of them set padding with the shorthand further down this file —
 * `.ucm-footer { padding: ... 0 }` for one. At equal weight the later rule wins,
 * so a footer asked for a column got none. Doubling the selector puts these
 * above any single component class wherever it sits in the file, which is what
 * "I chose a width" should mean.
 */
.ucm-wide-sm.ucm-wide-sm, .ucm-wide-md.ucm-wide-md,
.ucm-wide-lg.ucm-wide-lg, .ucm-wide-xl.ucm-wide-xl {
  --ucm-gut: var(--ucm-c-pad-x, 1rem);
  padding-inline: max(var(--ucm-gut), calc((100% - var(--ucm-col)) / 2));
}
/* The width itself is a property, so which side the column sits on can be
   written once instead of once per width. */
.ucm-wide-sm { --ucm-col: 40rem; }
.ucm-wide-md { --ucm-col: 48rem; }
.ucm-wide-lg { --ucm-col: 64rem; }
.ucm-wide-xl { --ucm-col: 80rem; }

/*
 * Which side of the box the column sits on.
 *
 * Centred is what a width does on its own, and it is stated here as well so it
 * can be chosen back — "I meant centre" and "I said nothing" are different
 * things, the same way light and unset are.
 *
 * After the widths, because these are the same property said differently.
 */
.ucm-col-center.ucm-col-center { padding-inline: max(var(--ucm-gut, 1rem), calc((100% - var(--ucm-col)) / 2)); }
.ucm-col-start.ucm-col-start   { padding-inline: var(--ucm-gut, 1rem) max(var(--ucm-gut, 1rem), calc(100% - var(--ucm-col) - var(--ucm-gut, 1rem))); }
.ucm-col-end.ucm-col-end       { padding-inline: max(var(--ucm-gut, 1rem), calc(100% - var(--ucm-col) - var(--ucm-gut, 1rem))) var(--ucm-gut, 1rem); }

/*
 * Out of the column and across the window.
 *
 * The column is padding on the box rather than a width on it, so a section
 * inside one sits inside that padding like everything else. This takes it back
 * out: a negative margin the width of the gap either side, which is what
 * `50% - 50vw` measures. Put it on a section that wants the whole window — a
 * band of colour, a picture, a full-width row — and leave the rest in the
 * column.
 *
 * It measures from the window, so it belongs on a page whose column is centred,
 * which is what a column is unless somebody says otherwise. The body clips
 * sideways, and only when something on the page actually bleeds: `100vw` counts
 * the scrollbar, and half of one either side is a scrollbar along the bottom of
 * the page. `clip` rather than `hidden` — hidden makes the body a scroll
 * container, and a sticky header inside one stops sticking.
 */
/*
 * Side margins a component sets on purpose, said a second time for the block
 * editor. The canvas zeroes every nested block's side margins (Gutenberg centres
 * them with `auto`, which components never asked for), and the zeroing rule is
 * stronger than any component rule — so a bleed sat at the column's left edge
 * running off the right, cut off on a narrow canvas. The editor rule reads
 * this property and uses 0 where it is not set. Not inherited: a bleed's own
 * contents must not be pulled out with it.
 */
@property --ucm-margin-inline { syntax: '*'; inherits: false; }

.ucm-bleed {
  /* The width is the window's, so whatever padding it has is inside it. This
     file does not set border-box everywhere, and a band 100vw wide plus its own
     padding is a page that scrolls sideways. */
  box-sizing: border-box;
  width: 100vw; max-width: 100vw;
  margin-inline: calc(50% - 50vw);
  --ucm-margin-inline: calc(50% - 50vw);
}
/* Where @property is not supported the value would inherit; stop it here. */
.ucm-bleed > * { --ucm-margin-inline: initial; }
body:has( .ucm-bleed ) { overflow-x: clip; }
/*
 * And what is in it goes back in a column, because a heading spanning a wide
 * window is a line nobody reads to the end of. The width is the one the page is
 * already using where there is one, and a sensible one where there is not.
 */
.ucm-bleed > .ucm-inner {
  box-sizing: border-box;
  width: 100%; max-width: var(--ucm-col, 64rem);
  margin-inline: auto;
  --ucm-margin-inline: auto;
  padding-inline: var(--ucm-gut, 1rem);
}

/* Room is the other axis, so the two compose: a width says how wide the column
   is, and room says how much space there is above and below it. */
.ucm-room-sm.ucm-room-sm { padding-block: .5rem; }
.ucm-room-md.ucm-room-md { padding-block: 1rem; }
.ucm-room-lg.ucm-room-lg { padding-block: 1.5rem; }
.ucm-room-xl.ucm-room-xl { padding-block: 2rem; }

/*
 * And room on one edge, because the commonest thing a page wants is air
 * everywhere except where it meets its header.
 *
 * A hero with a background of its own under a header is supposed to touch it;
 * room above the content is a stripe of the page's own background between the
 * two, which reads as a mistake — and the only way to avoid it was no room at
 * all, which then took the air away from the footer as well. These are the same
 * four steps on whichever edges are asked for.
 */
.ucm-room-t-sm.ucm-room-t-sm { padding-block-start: .5rem; }
.ucm-room-t-md.ucm-room-t-md { padding-block-start: 1rem; }
.ucm-room-t-lg.ucm-room-t-lg { padding-block-start: 1.5rem; }
.ucm-room-t-xl.ucm-room-t-xl { padding-block-start: 2rem; }

.ucm-room-b-sm.ucm-room-b-sm { padding-block-end: .5rem; }
.ucm-room-b-md.ucm-room-b-md { padding-block-end: 1rem; }
.ucm-room-b-lg.ucm-room-b-lg { padding-block-end: 1.5rem; }
.ucm-room-b-xl.ucm-room-b-xl { padding-block-end: 2rem; }

/* The other axis. Written as inline rather than left and right so a page in a
   right-to-left language gets the side it meant, not the side it named. */
.ucm-room-x-sm.ucm-room-x-sm { padding-inline: .5rem; }
.ucm-room-x-md.ucm-room-x-md { padding-inline: 1rem; }
.ucm-room-x-lg.ucm-room-x-lg { padding-inline: 1.5rem; }
.ucm-room-x-xl.ucm-room-x-xl { padding-inline: 2rem; }

.ucm-room-l-sm.ucm-room-l-sm { padding-inline-start: .5rem; }
.ucm-room-l-md.ucm-room-l-md { padding-inline-start: 1rem; }
.ucm-room-l-lg.ucm-room-l-lg { padding-inline-start: 1.5rem; }
.ucm-room-l-xl.ucm-room-l-xl { padding-inline-start: 2rem; }

.ucm-room-r-sm.ucm-room-r-sm { padding-inline-end: .5rem; }
.ucm-room-r-md.ucm-room-r-md { padding-inline-end: 1rem; }
.ucm-room-r-lg.ucm-room-r-lg { padding-inline-end: 1.5rem; }
.ucm-room-r-xl.ucm-room-r-xl { padding-inline-end: 2rem; }

/*
 * A whole page's rhythm.
 *
 * Sections of a page want more air between them than the things inside a
 * section do, and `.ucm-stack` is set for the inside case. Without this a
 * landing page reads as one long list rather than as five things.
 */
.ucm-page { gap: calc(var(--ucm-c-gap, .75rem) * 3); }
/* A page that is one thing — a sign-in card, a message saying the page is not
   there — is that thing in the middle, not that thing stretched across a desk. */
.ucm-page-narrow { max-width: 30rem; margin-inline: auto; }

/* Columns that collapse on their own, with no breakpoint to choose. */
.ucm-autofit { display: grid; gap: calc(var(--ucm-c-gap, .75rem) * 1.4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); }
.ucm-autofit > * { min-width: 0; }

/*
 * A main column with something narrower beside it.
 *
 * The aside is capped rather than proportional: a sidebar of links wants the
 * width its links need, and at 1600px a `1fr 4fr` split gives it 320px of
 * whitespace. `minmax(0, …)` on both tracks so a long word in either one
 * cannot push the other out — a grid track's default minimum is its content.
 *
 * Which side it lands on is the class; which one comes first on a phone is the
 * markup, because that is the question the author is answering when they write
 * it in that order.
 */
.ucm-aside { display: grid; gap: calc(var(--ucm-c-gap, .75rem) * 1.6); align-items: start;
  grid-template-columns: minmax(0, 1fr) minmax(0, 18rem); }
.ucm-aside-start { grid-template-columns: minmax(0, 18rem) minmax(0, 1fr); }
.ucm-aside > * { min-width: 0; }

/* Two columns that are deliberately not equal. */
.ucm-asym { display: grid; gap: calc(var(--ucm-c-gap, .75rem) * 1.4); align-items: start;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.ucm-asym-start { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
.ucm-asym > * { min-width: 0; }

@media (max-width: 860px) {
  .ucm-aside, .ucm-aside-start { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 720px) {
  .ucm-asym, .ucm-asym-start { grid-template-columns: minmax(0, 1fr); }
}

/*
 * What one cell does differently from its neighbours.
 *
 * These go on a child of any grid above, so a composition is the grid plus the
 * one cell that breaks it rather than a grid class per composition.
 *
 * Both spans stand down at the width where the grids themselves are near one
 * column. `span 2` in a one-column grid does not clamp — it makes a second,
 * implicit column and the row runs off the side — so this is not tidiness.
 */
.ucm-cell-wide { grid-column: 1 / -1; }
.ucm-cell-2    { grid-column: span 2; }
.ucm-cell-tall { grid-row: span 2; }
@media (max-width: 720px) {
  .ucm-cell-2    { grid-column: auto; }
  .ucm-cell-tall { grid-row: auto; }
}

/*
 * Equal columns, for a head and its rows to agree on.
 *
 * `.ucm-listing-head` and `.ucm-listing-row` are grids with no tracks of their own,
 * because how many columns a table has is the table's business rather than the
 * component's. One of these on both says it once.
 */
.ucm-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ucm-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ucm-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.ucm-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.ucm-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
/*
 * Or once on the listing itself.
 *
 * A row that is stamped out per record cannot carry a count of its own, and
 * writing the class on the head and again on the template says the same fact
 * twice. On the listing it is said once and the parts inherit it.
 */
.ucm-listing.ucm-cols-2 > .ucm-listing-head, .ucm-listing.ucm-cols-2 > .ucm-listing-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ucm-listing.ucm-cols-3 > .ucm-listing-head, .ucm-listing.ucm-cols-3 > .ucm-listing-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ucm-listing.ucm-cols-4 > .ucm-listing-head, .ucm-listing.ucm-cols-4 > .ucm-listing-row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.ucm-listing.ucm-cols-5 > .ucm-listing-head, .ucm-listing.ucm-cols-5 > .ucm-listing-row { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.ucm-listing.ucm-cols-6 > .ucm-listing-head, .ucm-listing.ucm-cols-6 > .ucm-listing-row { grid-template-columns: repeat(6, minmax(0, 1fr)); }
/* Row buttons (Edit, Delete) take the width they need after the equal columns. */
.ucm-listing-with-actions.ucm-cols-1 > .ucm-listing-head, .ucm-listing-with-actions.ucm-cols-1 > .ucm-listing-row { grid-template-columns: repeat(1, minmax(0, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-cols-2 > .ucm-listing-head, .ucm-listing-with-actions.ucm-cols-2 > .ucm-listing-row { grid-template-columns: repeat(2, minmax(0, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-cols-3 > .ucm-listing-head, .ucm-listing-with-actions.ucm-cols-3 > .ucm-listing-row { grid-template-columns: repeat(3, minmax(0, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-cols-4 > .ucm-listing-head, .ucm-listing-with-actions.ucm-cols-4 > .ucm-listing-row { grid-template-columns: repeat(4, minmax(0, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-cols-5 > .ucm-listing-head, .ucm-listing-with-actions.ucm-cols-5 > .ucm-listing-row { grid-template-columns: repeat(5, minmax(0, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-cols-6 > .ucm-listing-head, .ucm-listing-with-actions.ucm-cols-6 > .ucm-listing-row { grid-template-columns: repeat(6, minmax(0, 1fr)) max-content; }

/* A number reads as a number: right-aligned, and figures that line up. */
.ucm-num { text-align: end; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* `ucm-sr-only` is deliberately not defined here: the bundled class library
   already generates one, and two rules of the same name in two stylesheets is
   the shadowing that tests/suites/components.php exists to catch. */

/* Below the width the rows become lines, the tracks stop applying anyway. */
@media (max-width: 760px) {
  .ucm-listing-row.ucm-cols-2, .ucm-listing-row.ucm-cols-3, .ucm-listing-row.ucm-cols-4,
  .ucm-listing-row.ucm-cols-5, .ucm-listing-row.ucm-cols-6,
  .ucm-listing[class*="ucm-cols-"] > .ucm-listing-row {
    grid-template-columns: auto minmax(0, 1fr);
  }
  .ucm-listing[class*="ucm-cols-"] > .ucm-listing-head { display: none; }
}

/*
 * A table that scrolls sideways instead of stacking.
 *
 * Each column keeps at least 9rem, so a wide table is wider than a phone and
 * its box scrolls; the first column — the row's name — stays in view. Declared
 * after the stacking rules above, at the same weight, so it wins at every
 * width: `ucm-listing-scroll` says "never cards".
 */
.ucm-listing-scroller { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.ucm-listing-scroll { min-width: min-content; }
.ucm-listing-scroll.ucm-cols-1 > .ucm-listing-head, .ucm-listing-scroll.ucm-cols-1 > .ucm-listing-row { grid-template-columns: repeat(1, minmax(9rem, 1fr)); }
.ucm-listing-scroll.ucm-cols-2 > .ucm-listing-head, .ucm-listing-scroll.ucm-cols-2 > .ucm-listing-row { grid-template-columns: repeat(2, minmax(9rem, 1fr)); }
.ucm-listing-scroll.ucm-cols-3 > .ucm-listing-head, .ucm-listing-scroll.ucm-cols-3 > .ucm-listing-row { grid-template-columns: repeat(3, minmax(9rem, 1fr)); }
.ucm-listing-scroll.ucm-cols-4 > .ucm-listing-head, .ucm-listing-scroll.ucm-cols-4 > .ucm-listing-row { grid-template-columns: repeat(4, minmax(9rem, 1fr)); }
.ucm-listing-scroll.ucm-cols-5 > .ucm-listing-head, .ucm-listing-scroll.ucm-cols-5 > .ucm-listing-row { grid-template-columns: repeat(5, minmax(9rem, 1fr)); }
.ucm-listing-scroll.ucm-cols-6 > .ucm-listing-head, .ucm-listing-scroll.ucm-cols-6 > .ucm-listing-row { grid-template-columns: repeat(6, minmax(9rem, 1fr)); }
.ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-1 > .ucm-listing-head, .ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-1 > .ucm-listing-row { grid-template-columns: repeat(1, minmax(9rem, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-2 > .ucm-listing-head, .ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-2 > .ucm-listing-row { grid-template-columns: repeat(2, minmax(9rem, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-3 > .ucm-listing-head, .ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-3 > .ucm-listing-row { grid-template-columns: repeat(3, minmax(9rem, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-4 > .ucm-listing-head, .ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-4 > .ucm-listing-row { grid-template-columns: repeat(4, minmax(9rem, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-5 > .ucm-listing-head, .ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-5 > .ucm-listing-row { grid-template-columns: repeat(5, minmax(9rem, 1fr)) max-content; }
.ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-6 > .ucm-listing-head, .ucm-listing-with-actions.ucm-listing-scroll.ucm-cols-6 > .ucm-listing-row { grid-template-columns: repeat(6, minmax(9rem, 1fr)) max-content; }
.ucm-listing-scroll[class*="ucm-cols-"] > .ucm-listing-head { display: grid; }
.ucm-listing-scroll > .ucm-listing-row { align-items: center; }
.ucm-listing-scroll > .ucm-listing-row > :is([data-label], :not([data-label])) { grid-column: auto; }
.ucm-listing-scroll > .ucm-listing-row > [data-label] { display: unset; }
.ucm-listing-scroll > .ucm-listing-row > [data-label]::before { content: none; }
.ucm-listing-scroll > :is(.ucm-listing-head, .ucm-listing-row) > :first-child {
  position: sticky; inset-inline-start: 0; z-index: 1;
  background: var(--ucm-c-surface, #ffffff);
}

/*
 * A whole row or card that opens its title's link.
 *
 * The title stays the one real link — one tab stop, one name for a screen
 * reader — and its clickable area is stretched over the card. Buttons and other
 * links inside sit above that area, so they still do their own thing.
 */
.ucm-clickable { position: relative; cursor: pointer; }
.ucm-clickable .ucm-stretched::after { content: ""; position: absolute; inset: 0; z-index: 0; }
.ucm-clickable :is(button, input, select, textarea, label, a:not(.ucm-stretched)) { position: relative; z-index: 1; }
.ucm-clickable:hover .ucm-stretched { text-decoration: underline; }
.ucm-clickable:focus-within { outline: 2px solid var(--ucm-color-primary, #2563eb); outline-offset: 2px; }

/* Compact: less room around each row and card, for lists that are read at a glance. */
.ucm-density-compact { --ucm-c-pad: .35rem; --ucm-c-gap: .5rem; }

/* ── Avatar ─────────────────────────────────────────────────────────────── */
.ucm-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.25em; height: 2.25em; flex: 0 0 auto;
  border-radius: 999px;
  background: var(--ucm-color-primary, #2563eb); color: #fff;
  font-size: var(--ucm-c-font, .875rem); font-weight: 700; line-height: 1;
  overflow: hidden;
}
.ucm-avatar img { width: 100%; height: 100%; object-fit: cover; }

/* ── Drawer, for a menu that becomes a burger ───────────────────────────── */
/*
 * The popover API does the work: top layer, light dismiss, no JavaScript. The
 * UA gives a popover a border, padding and a fixed centred box, all of which
 * fight a drawer — so they are reset and the element is positioned itself.
 */
.ucm-drawer[popover] {
  box-sizing: border-box;
  position: fixed; inset: 0 auto 0 0;
  margin: 0; border: 0; padding: 0;
  width: min(20rem, 82vw); max-width: none; max-height: none; height: 100%;
  background: var(--ucm-c-surface, #fff);
  box-shadow: 0 0 2rem rgba(16, 24, 40, .18);
  overflow-y: auto;
}
.ucm-drawer[popover]::backdrop { background: rgba(16, 24, 40, .38); }
/*
 * And it stays shut until the burger opens it.
 *
 * The UA hides a closed popover with `[popover]:not(:popover-open){display:none}`,
 * but that rule lives in the UA origin, so ANY author declaration beats it —
 * and `.ucm-stack` on the same element declares `display:flex`. Without this the
 * drawer is permanently open: a fixed panel over the left of every page it is
 * on, with no way to dismiss it.
 */
.ucm-drawer[popover]:not(:popover-open) { display: none; }

/*
 * In the editor there is no burger to press and no top layer to open into, so a
 * drawer would be a block you cannot see and therefore cannot edit. Inside the
 * editor canvas it is shown where it sits instead. Scoped to the canvas wrapper,
 * so nothing about the front end changes — and specificity ties with the rule
 * above, which is why it comes after it.
 */
/*
 * `:not(:popover-open)` is load-bearing. Without it this claims the element
 * even once the browser has put it in the top layer, so a dialog that was
 * opened for real laid itself out in the document instead — a tall white box
 * in the middle of the content, rather than a dialog centred on the window.
 *
 * And only while the canvas is being edited. Live is there to answer "what will
 * this actually look like", and a drawer that is shut on the page has to be
 * shut in the answer — otherwise Live shows something no visitor will see.
 */
.editor-styles-wrapper:not([data-ucm-mode="live"]) .ucm-drawer[popover]:not(:popover-open) {
  display: flex;
  position: static;
  width: 100%; height: auto;
  box-shadow: none;
}

/* ── The menu a burger opens, across the whole page ─────────────────────── */
/*
 * A sheet is a drawer that takes the window. On a phone a panel down one side
 * leaves a strip of the page beside it that does nothing but get tapped by
 * accident, and the links end up in a column half the width they could be.
 *
 * It lives inside the header that opens it, so a header is one block that
 * works: the burger used to point at the drawer component by id, which meant a
 * header inserted on its own had a burger that opened nothing.
 */
.ucm-sheet[popover] {
  box-sizing: border-box;
  position: fixed; inset: 0;
  margin: 0; border: 0;
  width: 100%; max-width: none; height: 100%; max-height: none;
  padding: var(--ucm-c-pad-x, 1rem);
  background: var(--ucm-c-surface, #ffffff);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  overflow-y: auto;
}
.ucm-sheet[popover]::backdrop { background: rgba(16, 24, 40, .38); }
.ucm-sheet[popover]:not(:popover-open) { display: none; }
/* The row along the top: whose menu this is, and the way out of it. */
.ucm-sheet-head { display: flex; align-items: center; justify-content: space-between; gap: var(--ucm-c-gap, .75rem); }
/* Bigger than a nav link, because a menu across the page is a page of its own
   and these are the only things on it. */
.ucm-sheet .ucm-nav-link { padding-block: .7rem; font-size: calc(var(--ucm-c-font, .875rem) * 1.15); }

/* In the canvas there is no burger to press and no top layer to open into, so
   it is shown where it sits, like every other popover here. */
.editor-styles-wrapper:not([data-ucm-mode="live"]) .ucm-sheet[popover]:not(:popover-open) {
  display: flex; position: static;
  width: 100%; height: auto;
  border: 1px solid var(--ucm-c-border, #e2e5ea);
}

/* ── Media: a face or a mark beside what it is about ────────────────────── */
.ucm-media { display: flex; align-items: flex-start; gap: var(--ucm-c-gap, .75rem); }
.ucm-media-body { min-width: 0; }
.ucm-media-title { margin: 0; font-size: var(--ucm-c-font, .875rem); font-weight: 600; line-height: 1.35; }
.ucm-media-sub { margin: .15rem 0 0; font-size: calc(var(--ucm-c-font, .875rem) * .92); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/* ── Divider ────────────────────────────────────────────────────────────── */
.ucm-divider { border: 0; border-top: 1px solid var(--ucm-c-border, #e2e5ea); margin: var(--ucm-c-gap, .75rem) 0; }

/* ── Key and value, for a record you are reading rather than editing ────── */
/* The label takes the width of the label. A floor of six rems is fine on a page
   and wrong in a sidebar: in a 280px column it claimed a third of the room and
   left "Ada Lovelace" wrapping mid-word beside it. */
.ucm-dl { margin: 0; display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: .1rem var(--ucm-c-gap, .75rem); }
.ucm-dl dt { padding: var(--ucm-c-pad, .625rem) 0 0; font-size: calc(var(--ucm-c-font, .875rem) * .92); font-weight: 600; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
/* `break-word`, not `anywhere`: both break a word that would otherwise overflow,
   and only `anywhere` also tells the grid this column can be one character
   wide — which is how the column ended up narrower than the words in it. */
.ucm-dl dd { margin: 0; padding: var(--ucm-c-pad, .625rem) 0 0; font-size: var(--ucm-c-font, .875rem); overflow-wrap: break-word; }
/* Below this width a two-column grid is two cramped columns, not a layout. */
@media (max-width: 560px) { .ucm-dl { grid-template-columns: 1fr; } .ucm-dl dd { padding-top: 0; } }

/* ── Nothing here yet ───────────────────────────────────────────────────── */
.ucm-empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--ucm-c-gap, .75rem);
  padding: calc(var(--ucm-c-pad, .625rem) * 3) calc(var(--ucm-c-pad-x, 1rem) * 1.5);
  border: 1px dashed var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}
.ucm-empty-title { margin: 0; font-size: calc(var(--ucm-c-font, .875rem) * 1.15); font-weight: 700; color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
.ucm-empty-body { margin: 0; max-width: 42ch; }

/* ── Spinner ────────────────────────────────────────────────────────────── */
.ucm-spinner {
  display: inline-block; width: 1.15em; height: 1.15em; vertical-align: -.15em;
  border: 2px solid var(--ucm-c-border, #e2e5ea);
  border-top-color: var(--ucm-color-primary, #2563eb);
  border-radius: 50%;
  animation: ucm-spin .7s linear infinite;
}
@keyframes ucm-spin { to { transform: rotate(360deg); } }
/* Somebody who asked not to be moved should not be. */
@media (prefers-reduced-motion: reduce) { .ucm-spinner { animation-duration: 2.4s; } }

/* ── Breadcrumbs ────────────────────────────────────────────────────────── */
.ucm-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; font-size: calc(var(--ucm-c-font, .875rem) * .95); }
.ucm-crumbs a, .ucm-crumbs a:link, .ucm-crumbs a:visited, .ucm-crumbs a:hover { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); text-decoration: none !important; }
.ucm-crumbs a:hover { color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
/* The separator is drawn, not typed, so it is never read aloud or copied. */
.ucm-crumbs li + li::before { content: "/"; margin-right: .35rem; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-crumbs ol, .ucm-crumbs { list-style: none; margin: 0; padding: 0; }
.ucm-crumbs li { display: flex; align-items: center; }
.ucm-crumbs [aria-current="page"] { color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); font-weight: 600; }

/* ── Pagination ─────────────────────────────────────────────────────────── */
.ucm-pager { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.ucm-pager a, .ucm-pager span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 2.25em; padding: .4em .6em;
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  font-size: var(--ucm-c-font, .875rem); line-height: 1;
  text-decoration: none !important;
}
.ucm-pager a, .ucm-pager a:link, .ucm-pager a:visited { color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
.ucm-pager a:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }
.ucm-pager [aria-current="page"] { background: var(--ucm-color-primary, #2563eb); border-color: transparent; color: #fff; font-weight: 600; }

/* ── Tabs ───────────────────────────────────────────────────────────────── */
.ucm-tabs { display: flex; flex-wrap: wrap; gap: .15rem; border-bottom: 1px solid var(--ucm-c-border, #e2e5ea); }
.ucm-tab {
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border: 0; border-bottom: 2px solid transparent; background: none;
  font: inherit; font-size: var(--ucm-c-font, .875rem); font-weight: 600;
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
  cursor: pointer; text-decoration: none !important;
}
.ucm-tab:hover { color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
/* aria-selected is what a screen reader is told, so it is what drives the ink. */
.ucm-tab[aria-selected="true"] { color: var(--ucm-color-primary, #2563eb); border-bottom-color: var(--ucm-color-primary, #2563eb); }

/* ── Steps ──────────────────────────────────────────────────────────────── */
.ucm-steps { display: flex; flex-wrap: wrap; gap: var(--ucm-c-gap, .75rem); list-style: none; margin: 0; padding: 0; }
.ucm-step { display: flex; align-items: center; gap: .5rem; font-size: var(--ucm-c-font, .875rem); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-step-dot {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.7em; height: 1.7em; flex: none;
  border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: 50%;
  font-size: .8em; font-weight: 700; line-height: 1;
}
.ucm-step[aria-current="step"] { color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); font-weight: 600; }
.ucm-step[aria-current="step"] .ucm-step-dot { background: var(--ucm-color-primary, #2563eb); border-color: transparent; color: #fff; }
.ucm-step[data-done] .ucm-step-dot { background: var(--ucm-color-success, #16a34a); border-color: transparent; color: #fff; }

/* ── Timeline ───────────────────────────────────────────────────────────── */
.ucm-timeline { list-style: none; margin: 0; padding: 0 0 0 1.1rem; border-left: 2px solid var(--ucm-c-border, #e2e5ea); }
.ucm-tl-item { position: relative; padding: 0 0 var(--ucm-c-gap, .75rem) var(--ucm-c-pad-x, 1rem); }
.ucm-tl-item::before {
  content: ""; position: absolute; left: calc(-1.1rem - 5px); top: .35em;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--ucm-color-primary, #2563eb);
}
.ucm-tl-when { display: block; font-size: calc(var(--ucm-c-font, .875rem) * .88); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-tl-what { margin: .1rem 0 0; font-size: var(--ucm-c-font, .875rem); }

/* ── Board ──────────────────────────────────────────────────────────────────
 *
 * Columns of cards — a kanban. Four columns on a desk and a horizontal scroll
 * on a phone, because squeezing four lanes into 360px gives four unreadable
 * ones: a board is a comparison, and a comparison you cannot see side by side
 * is a list with extra steps. `scroll-snap` makes the narrow case feel like
 * pages rather than a drift.
 *
 * The body is a drop target: `.is-over` is the only state a script has to set,
 * and everything else about dragging is the browser's.
 */
.ucm-board {
  display: grid; grid-template-columns: repeat(var(--ucm-board-cols, 4), minmax(0, 1fr));
  gap: var(--ucm-c-gap, .75rem); align-items: start;
}
@media (max-width: 720px) {
  .ucm-board {
    display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch; padding-bottom: .25rem;
  }
  .ucm-board-col { flex: 0 0 82vw; scroll-snap-align: start; }
}
.ucm-board-col {
  background: var(--ucm-c-wash, rgba(0,0,0,.03));
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  padding: var(--ucm-c-pad, .625rem);
}
.ucm-board-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: var(--ucm-c-gap, .75rem); }
.ucm-board-name { font-size: calc(var(--ucm-c-font, .875rem) * .95); font-weight: 700; color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
.ucm-board-count {
  min-width: 1.5em; padding: 0 .4em; border-radius: 999px;
  background: var(--ucm-c-surface, #fff); border: 1px solid var(--ucm-c-border, #e2e5ea);
  font-size: calc(var(--ucm-c-font, .875rem) * .8); line-height: 1.6;
  text-align: center; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}
.ucm-board-body { display: flex; flex-direction: column; gap: .5rem; min-height: 3.5rem; border-radius: var(--ucm-c-radius, .5rem); transition: background .12s ease; }
/* The board behaviour sets `data-over` on the column it is above; `.is-over`
   is here for anything driving a board by hand. */
.ucm-board-col[data-over] .ucm-board-body, .ucm-board-body[data-over], .ucm-board-body.is-over { background: var(--ucm-color-primary-light, rgba(37,99,235,.08)); outline: 2px dashed var(--ucm-color-primary, #2563eb); outline-offset: 2px; }
.ucm-board-card {
  display: flex; flex-direction: column; gap: .35rem;
  background: var(--ucm-c-surface, #fff);
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  font-size: var(--ucm-c-font, .875rem);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  text-decoration: none !important; cursor: grab;
}
.ucm-board-card:active { cursor: grabbing; }
.ucm-board-card:hover { border-color: var(--ucm-color-primary, #2563eb); }
.ucm-board-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: .5rem; }
.ucm-board-card-title { font-weight: 600; line-height: 1.35; }
/* Priority as a dot: a word per card is four words of noise in a column of ten. */
.ucm-board-dot { flex: none; width: 8px; height: 8px; margin-top: .35em; border-radius: 50%; background: var(--ucm-c-border, #cbd5e1); }
.ucm-board-dot-high   { background: var(--ucm-color-danger, #ef4444); }
.ucm-board-dot-medium { background: var(--ucm-color-warning, #f59e0b); }
.ucm-board-dot-low    { background: var(--ucm-color-success, #10b981); }
.ucm-board-meta { display: flex; align-items: center; gap: .4rem; font-size: calc(var(--ucm-c-font, .875rem) * .85); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/* ── Modal, on the same native footing as the drawer ────────────────────── */
.ucm-modal[popover] {
  /* The UA gives a popover its own padding and border; both are reset, so the
     dialog has to state its own or its title sits against the corner. */
  margin: auto; border: 0;
  /* Border-box, or the padding is added to the 32rem and the dialog grows past
     the width it was asked for. */
  box-sizing: border-box;
  padding: calc(var(--ucm-c-pad, .625rem) * 2.2) calc(var(--ucm-c-pad-x, 1rem) * 1.4);
  width: min(32rem, 92vw); max-width: none; max-height: 85vh;
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-surface, #ffffff);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  box-shadow: 0 1.5rem 3rem rgba(16, 24, 40, .22);
  overflow: auto;
}
.ucm-modal[popover]::backdrop { background: rgba(16, 24, 40, .45); }
.ucm-modal[popover]:not(:popover-open) { display: none; }
/* Shown while editing, hidden in Live — see the drawer above. */
.editor-styles-wrapper:not([data-ucm-mode="live"]) .ucm-modal[popover]:not(:popover-open) { display: block; position: static; width: 100%; max-height: none; box-shadow: none; border: 1px solid var(--ucm-c-border, #e2e5ea); }

/* ── Toast ──────────────────────────────────────────────────────────────── */
.ucm-toast {
  display: flex; align-items: flex-start; gap: var(--ucm-c-gap, .75rem);
  padding: calc(var(--ucm-c-pad, .625rem) * 1.2) var(--ucm-c-pad-x, 1rem);
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-surface, #ffffff);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  box-shadow: 0 .5rem 1.5rem rgba(16, 24, 40, .14);
  font-size: var(--ucm-c-font, .875rem);
}

/* ── Accordion, from <details> so it opens without JavaScript ───────────── */
.ucm-accordion { border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem); overflow: hidden; }
.ucm-accordion > details + details { border-top: 1px solid var(--ucm-c-border, #e2e5ea); }
.ucm-accordion summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--ucm-c-gap, .75rem);
  padding: calc(var(--ucm-c-pad, .625rem) * 1.3) var(--ucm-c-pad-x, 1rem);
  font-size: var(--ucm-c-font, .875rem); font-weight: 600;
  cursor: pointer; list-style: none;
}
.ucm-accordion summary::-webkit-details-marker { display: none; }
.ucm-accordion summary::after { content: "+"; font-weight: 400; font-size: 1.2em; line-height: 1; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-accordion details[open] summary::after { content: "\2212"; }
.ucm-accordion summary:hover { background: var(--ucm-c-wash, rgba(0,0,0,.03)); }
.ucm-accordion-body { padding: 0 var(--ucm-c-pad-x, 1rem) calc(var(--ucm-c-pad, .625rem) * 1.4); font-size: var(--ucm-c-font, .875rem); line-height: 1.6; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/* ── The name of the thing, at the top of a header ──────────────────────── */
/*
 * Nearly always a link home, which is why it needs saying: a theme styling
 * `body a` is (0,1,1) and would otherwise make the company name blue and
 * underlined in the middle of its own header.
 */
.ucm-brand,
a.ucm-brand, a.ucm-brand:link, a.ucm-brand:visited,
a.ucm-brand:hover, a.ucm-brand:focus, a.ucm-brand:active {
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  font-size: calc(var(--ucm-c-font, .875rem) * 1.2);
  font-weight: 700; letter-spacing: -.01em; line-height: 1.2;
  text-decoration: none !important;
}

/* ── A card's picture, before there is a picture ────────────────────────── */
.ucm-card-media {
  display: block; width: 100%; aspect-ratio: 16 / 9;
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-media, #eceef2);
  object-fit: cover;
}
/* Sixteen by nine is a picture. A chart across two columns of a dashboard at
   that ratio is four hundred pixels tall and everything beside it is margin. */
.ucm-media-short { aspect-ratio: 3 / 1; }

/* ── Quote, for a testimonial ───────────────────────────────────────────── */
.ucm-quote { margin: 0; }
.ucm-quote-text { margin: 0; font-size: calc(var(--ucm-c-font, .875rem) * 1.12); line-height: 1.6; }
.ucm-quote-by { display: flex; align-items: center; gap: var(--ucm-c-gap, .75rem); margin-top: var(--ucm-c-gap, .75rem); }

/* ── Price ──────────────────────────────────────────────────────────────── */
.ucm-price-amount { font-size: calc(var(--ucm-c-font, .875rem) * 2.4); font-weight: 800; line-height: 1.1; letter-spacing: -.02em; }
.ucm-price-note { font-size: calc(var(--ucm-c-font, .875rem) * .92); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-price-list { list-style: none; margin: var(--ucm-c-gap, .75rem) 0 0; padding: 0; display: flex; flex-direction: column; gap: .35rem; font-size: var(--ucm-c-font, .875rem); }
.ucm-price-list li::before { content: "\2713"; margin-right: .5em; color: var(--ucm-color-success, #16a34a); font-weight: 700; }
/* The one being recommended, said with a border rather than a shout. */
.ucm-price-featured { border-color: var(--ucm-color-primary, #2563eb); border-width: 2px; }

/* ── Logos ──────────────────────────────────────────────────────────────── */
.ucm-logos { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: calc(var(--ucm-c-gap, .75rem) * 2); }
.ucm-logo { font-size: calc(var(--ucm-c-font, .875rem) * 1.15); font-weight: 700; letter-spacing: .02em; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); opacity: .75; }

/* ── Footer ─────────────────────────────────────────────────────────────── */
.ucm-footer { border-top: 1px solid var(--ucm-c-border, #e2e5ea); padding: calc(var(--ucm-c-pad, .625rem) * 3) 0; color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
.ucm-footer-title { margin: 0 0 .5rem; font-size: .72rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
/*
 * A footer link is a link, not a menu item. The nav treatment gives it the
 * weight and padding of something you click to move the page, which in three
 * columns of small print reads as a wall of buttons.
 */
.ucm-footer .ucm-nav-link {
  padding-inline: 0; padding-block: .2rem;
  font-weight: 400;
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}
.ucm-footer .ucm-nav-link:hover { background: none; color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
.ucm-footer-note { font-size: calc(var(--ucm-c-font, .875rem) * .92); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }


/*
 * Anything here that HTML has already hidden stays hidden.
 *
 * `hidden` is the browser's own way of saying "not now", and it is what markup
 * uses for a slot filled in later — an error that has not happened yet, a panel
 * that opens on a press. It sets `display: none` at the weight of a UA rule,
 * which every component's own `display` beats, so an alert with `hidden` on it
 * showed anyway. Said once, for everything.
 */
[hidden].ucm-alert, [hidden].ucm-stack, [hidden].ucm-cluster, [hidden].ucm-split,
[hidden].ucm-field, [hidden].ucm-card, [hidden].ucm-choice, [hidden].ucm-nav { display: none; }

/* ── A chart ────────────────────────────────────────────────────────────── */
/*
 * A canvas has no size of its own worth having, and Chart.js reads the box it is
 * given. So the block sets a height as a custom property and this makes the
 * figure that box — which is also why the canvas is told to fill it rather than
 * being sized directly: Chart.js writes its own width and height attributes, and
 * a rule on the canvas would fight them on every resize.
 */
.ucm-chart { margin: 0; }
/* The box is what has the height. Chart.js told not to keep a ratio measures the
   element it is in, so a canvas whose parent is sized by its contents grows
   every frame — this is the parent that does not. */
.ucm-chart-box { position: relative; height: var(--ucm-chart-h, 320px); }
.ucm-chart-box > canvas { display: block; }
.ucm-chart-cap {
  margin-top: .5rem; text-align: center;
  font-size: calc(var(--ucm-c-font, .875rem) * .92);
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}

/* ── Signing in ─────────────────────────────────────────────────────────── */
/*
 * The three things an auth form needs that a card does not already give it: a
 * page to sit in the middle of, a width a form is comfortable at, and a line
 * under it for the way out. Everything else is the ordinary components.
 */
.ucm-auth-wrap {
  display: flex; align-items: center; justify-content: center;
  min-height: 60vh; padding: var(--ucm-c-pad-x, 1rem);
}
.ucm-auth-box { width: 100%; max-width: 28rem; }
/* A one-time code: wide-set, centred, and big enough to read back from a phone
   while typing it. It is still an input, so everything else about it is the
   input's. Not `ucm-code` — that is the code sample, and two things with one
   name is one of them not working. */
.ucm-onetime {
  width: 11rem; margin-inline: auto;
  font-size: calc(var(--ucm-c-font, .875rem) * 2.2);
  letter-spacing: .25em; text-align: center;
}
.ucm-auth-foot {
  margin: var(--ucm-c-gap, .75rem) 0 0; text-align: center;
  font-size: calc(var(--ucm-c-font, .875rem) * .92);
}

/* ── Choice controls ────────────────────────────────────────────────────── */
/*
 * The box keeps the browser's own control rather than replacing it with a span:
 * a real checkbox is focusable, announced, and works before the CSS loads.
 * `accent-color` is the one line that tints it without giving any of that up.
 */
.ucm-choice { display: flex; align-items: flex-start; gap: .6rem; font-size: var(--ucm-c-font, .875rem); line-height: 1.45; cursor: pointer; }
.ucm-choice input[type="checkbox"], .ucm-choice input[type="radio"] {
  width: 1.05em; height: 1.05em; margin: .18em 0 0; flex: none;
  accent-color: var(--ucm-color-primary, #2563eb);
}
.ucm-choice-body { min-width: 0; }
.ucm-choice-label { font-weight: 600; }
.ucm-choice-note { display: block; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-size: calc(var(--ucm-c-font, .875rem) * .9); }
/* A boxed choice is a bigger target, which is most of why it exists. */
.ucm-choice-box {
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
}
.ucm-choice-box:hover { background: var(--ucm-c-wash, rgba(0,0,0,.03)); }
.ucm-choice-box:has(:checked) { border-color: var(--ucm-color-primary, #2563eb); }

/* ── Switch ─────────────────────────────────────────────────────────────── */
/*
 * Still a checkbox underneath. The track is drawn on the label, so the control
 * keeps its keyboard behaviour and its announcement.
 */
.ucm-switch { display: inline-flex; align-items: center; gap: .6rem; font-size: var(--ucm-c-font, .875rem); cursor: pointer; }
/* Doubled: WordPress's own editor form styles state a width and a height for
   `input[type="checkbox"]`, which is (0,1,1) — the same as `.ucm-switch input`
   — so in the canvas the hidden checkbox took a box back and the switch sat
   beside a phantom. */
.ucm-switch.ucm-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.ucm-switch-track {
  position: relative; flex: none;
  width: 2.5em; height: 1.4em; border-radius: 999px;
  background: var(--ucm-c-track, #cbd1d9);
  transition: background-color .15s ease;
}
.ucm-switch-track::after {
  content: ""; position: absolute; top: .15em; left: .15em;
  width: 1.1em; height: 1.1em; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(16,24,40,.25);
  transition: transform .15s ease;
}
.ucm-switch.ucm-switch input:checked + .ucm-switch-track { background: var(--ucm-color-primary, #2563eb); }
.ucm-switch.ucm-switch input:checked + .ucm-switch-track::after { transform: translateX(1.1em); }
.ucm-switch.ucm-switch input:focus-visible + .ucm-switch-track { outline: 2px solid var(--ucm-color-primary, #2563eb); outline-offset: 2px; }

/* ── Range ──────────────────────────────────────────────────────────────── */
.ucm-range { width: 100%; accent-color: var(--ucm-color-primary, #2563eb); }

/* ── Quantity ───────────────────────────────────────────────────────────── */
.ucm-qty { display: inline-flex; align-self: flex-start; align-items: stretch; border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem); overflow: hidden; }
.ucm-qty button {
  padding: 0 .75em; border: 0; background: var(--ucm-c-raised, var(--ucm-c-surface, #ffffff));
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  font: inherit; font-size: 1.1em; line-height: 1; cursor: pointer;
}
.ucm-qty button:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }
.ucm-qty input {
  width: 3.5em; border: 0; border-inline: 1px solid var(--ucm-c-border, #e2e5ea);
  padding: var(--ucm-c-pad, .625rem) .25rem; text-align: center;
  background: var(--ucm-c-raised, var(--ucm-c-surface, #ffffff));
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  font: inherit; font-size: var(--ucm-c-font, .875rem);
}

/* ── File ───────────────────────────────────────────────────────────────── */
.ucm-file {
  display: flex; flex-direction: column; align-items: center; gap: .35rem;
  padding: calc(var(--ucm-c-pad, .625rem) * 2.4) var(--ucm-c-pad-x, 1rem);
  border: 1px dashed var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  text-align: center; cursor: pointer;
  font-size: var(--ucm-c-font, .875rem);
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}
.ucm-file:hover { border-color: var(--ucm-color-primary, #2563eb); }
.ucm-file input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.ucm-file-strong { font-weight: 600; color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }

/* ── A field with something attached to it ──────────────────────────────── */
.ucm-joined { display: flex; align-items: stretch; }
.ucm-joined > .ucm-input { border-radius: 0; }
.ucm-joined > :first-child { border-start-start-radius: var(--ucm-c-radius, .5rem); border-end-start-radius: var(--ucm-c-radius, .5rem); }
.ucm-joined > :last-child  { border-start-end-radius: var(--ucm-c-radius, .5rem); border-end-end-radius: var(--ucm-c-radius, .5rem); }
.ucm-joined > * + * { margin-inline-start: -1px; }
.ucm-joined-part {
  display: inline-flex; align-items: center;
  padding: 0 var(--ucm-c-pad-x, 1rem);
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  background: var(--ucm-c-wash-soft, #f6f7f9);
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
  font-size: var(--ucm-c-font, .875rem); white-space: nowrap;
}

/* ── Progress ───────────────────────────────────────────────────────────── */
/*
 * The native element, not a div with an inline width. It carries the value
 * itself — announced, and readable without CSS — and the width cannot be
 * expressed as a class anyway, which is the practical half of the argument.
 */
.ucm-progress {
  appearance: none; -webkit-appearance: none;
  display: block; width: 100%; height: .5rem;
  border: 0; border-radius: 999px; overflow: hidden;
  background: var(--ucm-c-track, #e6e9ee);
  color: var(--ucm-color-primary, #2563eb);
}
.ucm-progress::-webkit-progress-bar { background: var(--ucm-c-track, #e6e9ee); border-radius: 999px; }
.ucm-progress::-webkit-progress-value { background: var(--ucm-color-primary, #2563eb); border-radius: 999px; }
.ucm-progress::-moz-progress-bar { background: var(--ucm-color-primary, #2563eb); border-radius: 999px; }

/* ── Divider with something written on it ───────────────────────────────── */
.ucm-divider-label { display: flex; align-items: center; gap: var(--ucm-c-gap, .75rem); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-size: calc(var(--ucm-c-font, .875rem) * .9); }
.ucm-divider-label::before, .ucm-divider-label::after { content: ""; flex: 1; border-top: 1px solid var(--ucm-c-border, #e2e5ea); }

/* ── Announcement ───────────────────────────────────────────────────────── */
.ucm-announce {
  display: flex; align-items: center; justify-content: center; gap: var(--ucm-c-gap, .75rem);
  padding: calc(var(--ucm-c-pad, .625rem) * .9) var(--ucm-c-pad-x, 1rem);
  background: var(--ucm-color-primary, #2563eb); color: #fff;
  font-size: calc(var(--ucm-c-font, .875rem) * .95); text-align: center;
}
.ucm-announce a, .ucm-announce a:link, .ucm-announce a:visited, .ucm-announce a:hover { color: #fff; text-decoration: underline; }

/* ── Chips ──────────────────────────────────────────────────────────────── */
.ucm-chip {
  display: inline-flex; align-items: center; gap: .4em;
  padding: .25em .7em;
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: 999px;
  background: var(--ucm-c-surface, #ffffff);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  font-size: calc(var(--ucm-c-font, .875rem) * .9); line-height: 1.5;
  text-decoration: none !important;
}
a.ucm-chip:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }
.ucm-chip-x { border: 0; background: none; padding: 0; color: inherit; opacity: .55; cursor: pointer; font: inherit; line-height: 1; }
.ucm-chip-x:hover { opacity: 1; }

/* ── Rating ─────────────────────────────────────────────────────────────── */
.ucm-rating { display: inline-flex; align-items: center; gap: .1em; color: var(--ucm-color-warning, #f59e0b); }
.ucm-rating-note { margin-inline-start: .5em; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-size: calc(var(--ucm-c-font, .875rem) * .9); }

/* ── A row of faces ─────────────────────────────────────────────────────── */
.ucm-avatars { display: flex; align-items: center; }
.ucm-avatars > * { margin-inline-start: -.55em; box-shadow: 0 0 0 2px var(--ucm-c-surface, #ffffff); }
.ucm-avatars > :first-child { margin-inline-start: 0; }

/* ── Dropdown menu ──────────────────────────────────────────────────────── */
/*
 * A menu belongs under the button that opened it, and a popover is in the top
 * layer — where the only thing that knows where that button is, without a line
 * of script, is anchor positioning. The invoker is the implicit anchor, so
 * nothing has to be named: `popovertarget` already tied the two together.
 *
 * `margin: 0` on its own put the menu in the top left corner of the window,
 * because that is what the UA's `inset: 0; margin: auto` centring becomes once
 * the margin is taken away. Where anchor positioning is not supported the
 * centring is left alone instead — a sheet in the middle of the window is a
 * place somebody can read; the corner of the page is not.
 */
.ucm-menu[popover] {
  box-sizing: border-box;
  border: 1px solid var(--ucm-c-border, #e2e5ea); padding: .25rem;
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-surface, #ffffff);
  box-shadow: 0 .75rem 2rem rgba(16, 24, 40, .16);
  min-width: 12rem;
  /* Down, never across: a sideways scrollbar under a menu is a menu that has
     been squeezed, which is what happened when it was shown in place beside
     its own trigger. */
  overflow-x: hidden; overflow-y: auto; max-height: 20rem;
}
@supports (anchor-name: --ucm-a) {
  .ucm-menu[popover] {
    margin: 0;
    /* Under the button and lined up with its start edge, and flipped to the
       other side of either axis when there is no room — which is what a menu
       near the bottom of the window needs and what a script would have had to
       work out. */
    position-area: block-end span-inline-end;
    position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
    margin-block-start: .35rem;
  }
}

.ucm-menu[popover]:not(:popover-open) { display: none; }
.ucm-menu-item {
  display: flex; align-items: center; gap: .55rem; width: 100%;
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border: 0; border-radius: calc(var(--ucm-c-radius, .5rem) - .15rem);
  background: none; font: inherit; font-size: var(--ucm-c-font, .875rem);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  text-align: left; text-decoration: none !important; cursor: pointer;
}
.ucm-menu-item:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }
/*
 * Shown in place beside its trigger, at the width it will really have.
 *
 * Putting it on its own line needs `flex-basis: 100%`, and a max-width clamps
 * that basis so the line never breaks — the only way to force the wrap is to
 * let the menu be full width, which would show a menu nothing like the one that
 * opens. Beside the button at its true width is the more honest preview.
 */
.editor-styles-wrapper:not([data-ucm-mode="live"]) .ucm-menu[popover]:not(:popover-open),
.ucm-preview-item .ucm-menu[popover]:not(:popover-open) {
  display: block; position: static; box-shadow: none; max-width: 18rem;
}


/* ── Selling ────────────────────────────────────────────────────────────── */
/*
 * A price is three facts in one line: what it costs, what it cost, and how much
 * that is off. They are three elements rather than one string because only the
 * first is always there, and a currency in a sentence cannot be styled.
 */
.ucm-price { display: flex; align-items: baseline; flex-wrap: wrap; gap: .5rem; }
.ucm-price-now { font-size: calc(var(--ucm-c-font, .875rem) * 1.5); font-weight: 800; letter-spacing: -.02em; }
.ucm-price-was { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); text-decoration: line-through; }

/* A line of a basket: picture, what it is, and what it comes to. */
.ucm-cartline {
  display: grid; align-items: center; gap: var(--ucm-c-gap, .75rem);
  grid-template-columns: 4.5rem minmax(0, 1fr) auto;
  padding: var(--ucm-c-pad, .625rem) 0;
  border-bottom: 1px solid var(--ucm-c-border, #e2e5ea);
}
.ucm-cartline > * { min-width: 0; }
.ucm-cartline .ucm-card-media { aspect-ratio: 1; }
@media (max-width: 560px) {
  /* The picture keeps its column and the price moves under the name, rather
     than three columns each too narrow to read. */
  .ucm-cartline { grid-template-columns: 3.5rem minmax(0, 1fr); }
  .ucm-cartline > :last-child { grid-column: 2; }
}

/*
 * What it comes to, with the total set apart from what makes it up. A table
 * would say these are rows of the same kind; the last one is the answer.
 */
.ucm-total { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .35rem var(--ucm-c-gap, .75rem); }
.ucm-total-label { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-size: var(--ucm-c-font, .875rem); }
.ucm-total-value { text-align: right; font-size: var(--ucm-c-font, .875rem); font-variant-numeric: tabular-nums; }
.ucm-total-sum {
  margin-top: .35rem; padding-top: .6rem;
  border-top: 1px solid var(--ucm-c-border, #e2e5ea);
  font-weight: 800; font-size: calc(var(--ucm-c-font, .875rem) * 1.15);
}

/* ── A figure with its change ───────────────────────────────────────────── */
/*
 * The number is the point and the change is the context, so the change is
 * smaller and coloured by direction. `up` and `down` rather than good and bad:
 * whether a rise is good is the page's business, not the component's.
 */
.ucm-kpi-value { font-size: calc(var(--ucm-c-font, .875rem) * 2.2); font-weight: 800; line-height: 1.1; letter-spacing: -.02em; }
.ucm-kpi-change { display: inline-flex; align-items: center; gap: .3rem; font-size: calc(var(--ucm-c-font, .875rem) * .92); font-weight: 700; }
.ucm-kpi-up   { color: var(--ucm-color-success, #16a34a); }
.ucm-kpi-down { color: var(--ucm-color-danger, #dc2626); }

/* ── Rows ───────────────────────────────────────────────────────────────── */
/*
 * A list of things you can open, which is a table with one column and no
 * header. Separated rather than boxed: a card around each makes a list of six
 * look like six decisions.
 */
.ucm-rows { display: flex; flex-direction: column; border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem); overflow: hidden; }
.ucm-row {
  display: flex; align-items: center; gap: var(--ucm-c-gap, .75rem);
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  font-size: var(--ucm-c-font, .875rem);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  text-decoration: none !important;
}
.ucm-row + .ucm-row { border-top: 1px solid var(--ucm-c-border, #e2e5ea); }
/*
 * A row on its own is a card; rows in a list share one edge.
 *
 * `.ucm-rows` draws the border and clips its corners, so the rows inside it
 * must stay square or the clip fights a radius of their own. A row used by
 * itself — one line of a template, a row inside a card — has no container to
 * take that from, and without this it is a rectangle with hard corners in a
 * page where nothing else has them.
 */
.ucm-row { border-radius: var(--ucm-c-radius, .5rem); }
.ucm-rows > .ucm-row { border-radius: 0; }
a.ucm-row:hover, .ucm-row:hover { background: var(--ucm-c-wash, rgba(0,0,0,.03)); }
/* The name over the note, not beside it: two inline spans with nothing between
   them came out as "NorthwindUpdated this morning". */
.ucm-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .1rem; }
.ucm-row-note { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-size: calc(var(--ucm-c-font, .875rem) * .9); }
/* The chevron at the end of a row you can open. */
.ucm-row-go {
  flex: none; width: .45rem; height: .45rem; margin-inline-start: auto;
  border-top: 2px solid currentColor; border-right: 2px solid currentColor;
  transform: rotate(45deg); opacity: .45;
}

/* A list that is a list to a screen reader and a stack of rows to everyone
   else. `ul` carries the meaning; the bullets carry nothing. */
.ucm-plain-list { list-style: none; margin: 0; padding: 0; }

/* ── What somebody said, and what was said back ─────────────────────────── */
.ucm-comment { display: flex; gap: var(--ucm-c-gap, .75rem); }
.ucm-comment-body { flex: 1; min-width: 0; }
.ucm-comment-who { font-weight: 700; font-size: var(--ucm-c-font, .875rem); }
.ucm-comment-when { margin-inline-start: .5em; font-weight: 400; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-size: calc(var(--ucm-c-font, .875rem) * .88); }
.ucm-comment-text { margin: .25rem 0 0; font-size: var(--ucm-c-font, .875rem); line-height: 1.6; }
/* A reply is the same thing, indented to the width of the face above it. */
.ucm-comment-replies { margin-top: var(--ucm-c-gap, .75rem); padding-inline-start: calc(2.25rem + var(--ucm-c-gap, .75rem)); display: flex; flex-direction: column; gap: var(--ucm-c-gap, .75rem); }

/* ── The shape of something still loading ───────────────────────────────── */
/*
 * Grey bars in the shape of what is coming, rather than a spinner in the middle
 * of nothing. The shimmer stands down for anyone who has asked for less motion.
 */
.ucm-skeleton { display: flex; flex-direction: column; gap: .5rem; }
.ucm-skeleton-line, .ucm-skeleton-block {
  border-radius: var(--ucm-c-radius, .5rem);
  background: linear-gradient(90deg,
    var(--ucm-c-media, #eceef2) 25%,
    var(--ucm-c-track, #f4f5f7) 37%,
    var(--ucm-c-media, #eceef2) 63%);
  background-size: 400% 100%;
  animation: ucm-shimmer 1.4s ease infinite;
}
.ucm-skeleton-line  { height: .85em; }
.ucm-skeleton-block { aspect-ratio: 16 / 9; width: 100%; }
.ucm-skeleton-short { width: 60%; }
@keyframes ucm-shimmer { from { background-position: 100% 50%; } to { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) {
  .ucm-skeleton-line, .ucm-skeleton-block { animation: none; }
}

/* ── A picture that is making a point ───────────────────────────────────── */
.ucm-figure { margin: 0; display: flex; flex-direction: column; gap: .5rem; }
.ucm-figure-cap { font-size: calc(var(--ucm-c-font, .875rem) * .92); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/* ── A label above a heading, a list of ticks, a plain link ─────────────── */
/*
 * Small print above a heading. It is the site's primary colour rather than the
 * muted one: an eyebrow is there to be read first, and grey above black reads
 * as something switched off.
 */
.ucm-eyebrow {
  margin: 0; font-size: calc(var(--ucm-c-font, .875rem) * .8); font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--ucm-color-primary, #2563eb);
}
/*
 * The mark is a pseudo-element and the list keeps its bullets stripped: a tick
 * typed into every item is three characters somebody has to delete when the
 * list changes, and a screen reader reads each one out.
 */
.ucm-ticks { display: grid; gap: .4rem; margin: 0; padding: 0; list-style: none; }
.ucm-ticks > li { position: relative; padding-inline-start: 1.5rem; }
.ucm-ticks > li::before {
  content: ""; position: absolute; inset-inline-start: 0; top: .42em;
  width: .5rem; height: .85rem;
  border: solid var(--ucm-color-primary, #2563eb);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translateY(-.2em);
}
/* A link that is a link: underlined, and far enough off the text to read. */
.ucm-link {
  color: var(--ucm-color-primary, #2563eb); font-weight: 600;
  text-decoration: underline; text-underline-offset: .2em;
}
.ucm-link:hover { color: var(--ucm-color-primary-dark, #1d4ed8); }
/* A link beside a control — "Forgotten it?" under a password — in the brand's
   colour, and underlined only when pointed at, so it does not outshout the form. */
.ucm-link-subtle, .ucm-link-subtle:link, .ucm-link-subtle:visited {
  color: var(--ucm-color-primary, #2563eb); font-weight: 600; text-decoration: none;
}
.ucm-link-subtle:hover, .ucm-link-subtle:focus-visible { text-decoration: underline; text-underline-offset: .2em; }

/*
 * Except inside a row, where a link is a target rather than prose.
 *
 * A table of twenty records whose first cell is a link becomes twenty
 * underlines, which reads as emphasis on nothing. The row is already the
 * affordance — it highlights under the pointer — so the name inside it stays
 * plain until you are actually on it.
 */
.ucm-listing-row a, .ucm-row a, .ucm-listing-row .ucm-link, .ucm-row .ucm-link {
  text-decoration: none; color: inherit;
}
.ucm-listing-row a:hover, .ucm-row a:hover,
.ucm-listing-row .ucm-link:hover, .ucm-row .ucm-link:hover {
  color: var(--ucm-color-primary, #2563eb);
}

/* ── A picture of the thing, framed ─────────────────────────────────────── */
/*
 * A screenshot under a headline. The border and the shadow are what stop it
 * dissolving into the page when the picture is mostly white, which a screenshot
 * of an interface usually is.
 */
.ucm-shot {
  aspect-ratio: 16 / 10;
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  box-shadow: 0 18px 40px rgba(16, 24, 40, .16);
}

/* ── Something on show ──────────────────────────────────────────────────── */
/*
 * A component with its name above it and a stage under it, for a page that is
 * about the components rather than made of them — the library page, a style
 * guide, a page of patterns for a team.
 *
 * The stage is a surface rather than a card: what is on it usually has a card of
 * its own, and a card inside a card reads as a mistake.
 */
.ucm-showcase { padding-block: calc(var(--ucm-c-pad, .625rem) * 2); border-top: 1px solid var(--ucm-c-border, #e2e5ea); }
.ucm-showcase-stage {
  padding: calc(var(--ucm-c-pad, .625rem) * 2);
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-page, #f7f8fa);
  /* A stage is as wide as the page and what is on it can be wider — a bar, a
     table, a row that scrolls — so it clips rather than spilling. */
  overflow-x: auto;
}
/* What is on a stage is a block on show. The bar along the bottom of a phone is
   fixed to the window, and one page showing twenty components would otherwise
   have one of them stuck across it. */
.ucm-showcase-stage .ucm-dock { position: static; border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem); }
@media (min-width: 768px) { .ucm-showcase-stage .ucm-dock { display: grid; } }

/* ── A band across the window ───────────────────────────────────────────── */
/*
 * The full-width hero: a strip of colour or a picture that runs edge to edge
 * with the words back in a column inside it. `ucm-bleed` is what takes it out
 * of the column; this is what it looks like once it is out.
 */
.ucm-band {
  background: var(--ucm-c-page, #f7f8fa);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
}
/* Over a picture the cover's rules already say white; what changes across the
   window is that there are no corners to round and the words sit in the middle
   of it rather than at the bottom. */
.ucm-bleed.ucm-cover { border-radius: 0; }
/* Centred by the column inside it, not by shrinking the column to its words:
   `justify-items: center` on the grid makes every item as wide as its longest
   line, which is a heading centred over a paragraph of a different width. */
.ucm-band.ucm-cover {
  background: none;
  align-items: center;
  min-height: 26rem;
}

/* ── A picture behind the words ─────────────────────────────────────────── */
/*
 * Three layers in one box: the picture, a tint over it, and the words over
 * that. The tint is an element rather than a pseudo so it is a block like
 * anything else — recolour it, or delete it when the picture is dark enough.
 *
 * The words are white in both themes on purpose. A cover is words over a
 * photograph, and a photograph is not light or dark in a way the theme knows
 * about; the tint is what guarantees they can be read. The theme class still
 * does its work on the buttons inside.
 */
.ucm-cover {
  position: relative; isolation: isolate;
  display: grid; align-items: end;
  min-height: 22rem; overflow: hidden;
  padding: calc(var(--ucm-c-pad, .625rem) * 4) calc(var(--ucm-c-pad-x, 1rem) * 2);
  border-radius: var(--ucm-c-radius, .5rem);
  color: #ffffff;
}
/*
 * Both layers name themselves twice.
 *
 * An empty block in the canvas carries a stand-in that says "empty", and the
 * editor positions the block to put it there — `:has( > .ucm-html-empty )` sets
 * `position: relative`, at the same weight as a plain descendant rule and
 * injected later, so it won. The tint stopped being a layer and became a row of
 * the grid with nothing in it, which is a cover that looks right on the page and
 * wrong in the canvas. Three classes beats it wherever it is injected.
 */
.ucm-cover .ucm-cover-media.ucm-cover-media {
  position: absolute; inset: 0; z-index: -2; display: block;
  width: 100%; height: 100%; object-fit: cover;
  background: var(--ucm-c-media, #eceef2);
  border-radius: 0;
}
.ucm-cover .ucm-cover-tint.ucm-cover-tint {
  position: absolute; inset: 0; z-index: -1; display: block;
  width: 100%; height: 100%;
  background: linear-gradient(to top, rgba(8, 12, 20, .82), rgba(8, 12, 20, .30));
}
/* Wide enough for a headline, narrow enough that it does not run the width of a
   photograph and turn into a paragraph. */
.ucm-cover-body { position: relative; max-width: 40rem; }
.ucm-cover .ucm-card-title { color: inherit; }
.ucm-cover .ucm-card-sub { color: rgba(255, 255, 255, .84); }

/* ── The ways you can pay ───────────────────────────────────────────────── */
/*
 * Words in a box rather than the card companies' logos: those are trademarks
 * with rules about size and clear space, and a plugin cannot ship them. Replace
 * a box with an image and the row still works.
 */
.ucm-pay {
  display: inline-flex; align-items: center;
  min-height: 2rem; padding: .25rem .6rem;
  border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: calc(var(--ucm-c-radius, .5rem) - .15rem);
  background: var(--ucm-c-raised, #ffffff);
  font-size: calc(var(--ucm-c-font, .875rem) * .85); font-weight: 600;
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}

/* ── The big picture and the rest ───────────────────────────────────────── */
.ucm-thumbs { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: var(--ucm-c-gap, .75rem); margin: 0; padding: 0; list-style: none; }
.ucm-thumb {
  display: block; width: 100%; aspect-ratio: 1; padding: 0;
  border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-media, #eceef2);
  cursor: pointer;
}
.ucm-thumb:hover { border-color: var(--ucm-c-border-hover, #cbd1d9); }

/* ── How the scores are spread ──────────────────────────────────────────── */
.ucm-score { display: grid; grid-template-columns: 1.5rem minmax(0, 1fr) 3rem; gap: .5rem; align-items: center; }
.ucm-score-n, .ucm-score-count { font-size: calc(var(--ucm-c-font, .875rem) * .85); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-variant-numeric: tabular-nums; }
.ucm-score-count { text-align: end; }
.ucm-score-bar {
  display: block; height: .5rem; border-radius: 999px;
  background: linear-gradient(to right,
    var(--ucm-color-warning, #f59e0b) calc(var(--ucm-pct, 0) * 1%),
    var(--ucm-c-track, #e5e7eb) 0);
}
/* Same steps as the dial, and the same reason: a number is what the thing is,
   so it goes in an attribute rather than a style nothing else can reach. */
.ucm-score-bar[data-pct="0"]{--ucm-pct:0}.ucm-score-bar[data-pct="5"]{--ucm-pct:5}
.ucm-score-bar[data-pct="10"]{--ucm-pct:10}.ucm-score-bar[data-pct="15"]{--ucm-pct:15}
.ucm-score-bar[data-pct="20"]{--ucm-pct:20}.ucm-score-bar[data-pct="25"]{--ucm-pct:25}
.ucm-score-bar[data-pct="30"]{--ucm-pct:30}.ucm-score-bar[data-pct="35"]{--ucm-pct:35}
.ucm-score-bar[data-pct="40"]{--ucm-pct:40}.ucm-score-bar[data-pct="45"]{--ucm-pct:45}
.ucm-score-bar[data-pct="50"]{--ucm-pct:50}.ucm-score-bar[data-pct="55"]{--ucm-pct:55}
.ucm-score-bar[data-pct="60"]{--ucm-pct:60}.ucm-score-bar[data-pct="65"]{--ucm-pct:65}
.ucm-score-bar[data-pct="70"]{--ucm-pct:70}.ucm-score-bar[data-pct="75"]{--ucm-pct:75}
.ucm-score-bar[data-pct="80"]{--ucm-pct:80}.ucm-score-bar[data-pct="85"]{--ucm-pct:85}
.ucm-score-bar[data-pct="90"]{--ucm-pct:90}.ucm-score-bar[data-pct="95"]{--ucm-pct:95}
.ucm-score-bar[data-pct="100"]{--ucm-pct:100}

/* ── The bar that stays while the page scrolls ──────────────────────────── */
.ucm-cta-bar {
  position: sticky; bottom: 0; z-index: 20;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--ucm-c-gap, .75rem);
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-surface, #ffffff);
  box-shadow: 0 -.5rem 1.5rem rgba(16, 24, 40, .1);
}

/* ── Somewhere to drop a file ───────────────────────────────────────────── */
/*
 * A label round the input, so the whole square is the button: clicking anything
 * inside it opens the picker, and a keyboard reaches it through the input.
 */
.ucm-dropzone {
  display: flex; flex-direction: column; align-items: center; gap: .4rem;
  padding: calc(var(--ucm-c-pad, .625rem) * 2.5) var(--ucm-c-pad-x, 1rem);
  border: 2px dashed var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-wash-soft, rgba(0,0,0,.028));
  text-align: center; cursor: pointer;
}
.ucm-dropzone:hover { border-color: var(--ucm-color-primary, #2563eb); }
/* Not hidden: a hidden input cannot be focused, and then the only way to the
   picker is a mouse. Kept where it is and made small. */
.ucm-dropzone-input { width: 1px; height: 1px; opacity: 0; }
.ucm-dropzone:focus-within { border-color: var(--ucm-color-primary, #2563eb); outline: 2px solid var(--ucm-color-primary, #2563eb); outline-offset: 2px; }

/*
 * The three things a dropzone can be doing, once there is a runtime behind it.
 *
 * `is-over` has to be obvious at a glance — somebody is holding a file over the
 * square and needs to know it will land — and `is-busy` has to say that
 * something is happening without moving the layout, because a box that changes
 * height mid-drop pushes the rest of the form around.
 */
.ucm-dropzone.is-over {
  border-color: var(--ucm-color-primary, #2563eb);
  background: var(--ucm-c-wash, rgba(0,0,0,.05));
}
.ucm-dropzone.is-busy { opacity: .6; cursor: progress; }
.ucm-dropzone.has-file { border-style: solid; }

/* What is attached, and the way to change your mind about it. */
.ucm-file-list:empty { display: none; }
.ucm-file-item {
  display: flex; align-items: center; gap: .5rem;
  margin-top: .4rem; padding: .35rem .5rem;
  border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-surface, #fff);
  font-size: var(--ucm-c-font, .875rem);
}
.ucm-file-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ucm-file-size { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-variant-numeric: tabular-nums; }
.ucm-file-remove {
  flex: none; width: 1.5rem; height: 1.5rem; padding: 0;
  border: 0; border-radius: 999px; cursor: pointer;
  background: var(--ucm-c-wash, rgba(0,0,0,.05));
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
  font-size: 1rem; line-height: 1;
}
.ucm-file-remove:hover { background: var(--ucm-color-danger, #dc2626); color: #fff; }

/* ── What is done and what is left ──────────────────────────────────────── */
.ucm-tasks { display: grid; gap: .3rem; margin: 0; padding: 0; list-style: none; }
.ucm-task { cursor: pointer; }
/* A line through it, and the words step back. Done is still readable, because
   "what did I already do" is the other question this list answers. */
.ucm-task:has( input:checked ) { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); text-decoration: line-through; }

/* ── What was said ──────────────────────────────────────────────────────── */
/*
 * Two columns, and which one the avatar is in says who is talking. Mine is the
 * same markup the other way round, so a message does not have to know whether
 * it is a reply.
 */
.ucm-chat { display: grid; gap: var(--ucm-c-gap, .75rem); margin: 0; padding: 0; list-style: none; }
.ucm-said { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .6rem; align-items: end; }
.ucm-said-mine { grid-template-columns: minmax(0, 1fr) auto; }
.ucm-said-mine > .ucm-avatar { order: 2; }
.ucm-said-body {
  justify-self: start; max-width: 42ch;
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-wash, rgba(0,0,0,.045));
}
.ucm-said-mine .ucm-said-body {
  justify-self: end;
  background: var(--ucm-color-primary, #2563eb); color: #ffffff;
}
.ucm-said-text { margin: 0; font-size: var(--ucm-c-font, .875rem); line-height: 1.5; }
.ucm-said-when { display: block; margin-top: .2rem; font-size: calc(var(--ucm-c-font, .875rem) * .8); opacity: .7; }

/* ── How long is left ───────────────────────────────────────────────────── */
.ucm-count {
  display: flex; flex-direction: column; align-items: center; gap: .15rem;
  min-width: 4rem; padding: var(--ucm-c-pad, .625rem);
  border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-raised, #ffffff);
}
/* Tabular figures, so the number does not jump about as it counts down. */
.ucm-count-n { font-size: calc(var(--ucm-c-font, .875rem) * 1.9); font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.ucm-count-label { font-size: calc(var(--ucm-c-font, .875rem) * .8); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/* ── The bar along the bottom of a phone ────────────────────────────────── */
/*
 * Fixed to the bottom, and out of the way above that width: a dock beside a
 * sidebar is two navigations for one page.
 */
.ucm-dock {
  position: fixed; inset: auto 0 0 0; z-index: 25;
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  border-top: 1px solid var(--ucm-c-border, #e2e5ea);
  background: var(--ucm-c-surface, #ffffff);
  padding-bottom: env(safe-area-inset-bottom, 0);
}
.ucm-dock-stop {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .15rem;
  padding: .55rem .25rem;
  font-size: calc(var(--ucm-c-font, .875rem) * .78);
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
  text-decoration: none !important;
}
.ucm-dock-here { color: var(--ucm-color-primary, #2563eb); }
@media (min-width: 768px) { .ucm-dock { display: none; } }
/* While it is being edited it is a block like any other, not something stuck to
   the bottom of the canvas floating over the thing you are working on. Live is
   the page, so Live leaves it where the page puts it. */
.editor-styles-wrapper:not([data-ucm-mode="live"]) .ucm-dock, .ucm-preview-item .ucm-dock {
  position: static; display: grid;
  border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem);
}

/* ── A mark with a number on the corner ─────────────────────────────────── */
.ucm-indicator { position: relative; display: inline-flex; }
.ucm-indicator-dot {
  position: absolute; top: -.15rem; inset-inline-end: -.15rem;
  min-width: 1.15rem; height: 1.15rem; padding: 0 .3rem;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: var(--ucm-c-danger, #dc2626); color: #ffffff;
  font-size: .68rem; font-weight: 700; line-height: 1;
}

/* ── Keys ───────────────────────────────────────────────────────────────── */
.ucm-keys { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; margin: 0; font-size: var(--ucm-c-font, .875rem); }
.ucm-kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.6rem; padding: .1rem .4rem;
  border: 1px solid var(--ucm-c-border, #e2e5ea); border-bottom-width: 2px;
  border-radius: .35rem;
  background: var(--ucm-c-raised, #ffffff);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: calc(var(--ucm-c-font, .875rem) * .85); line-height: 1.5;
}

/* ── How far through, as a circle ───────────────────────────────────────── */
/*
 * One conic gradient and a hole punched in the middle of it. The number is a
 * custom property on the element, so it is set where anybody would look for it
 * and the text inside says the same thing for anybody who cannot see a circle.
 */
.ucm-dial {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 5.5rem; aspect-ratio: 1; border-radius: 999px;
  background: conic-gradient(var(--ucm-color-primary, #2563eb) calc(var(--ucm-pct, 0) * 1%), var(--ucm-c-track, #e5e7eb) 0);
}
.ucm-dial::before {
  content: ""; position: absolute; inset: .55rem;
  border-radius: 999px; background: var(--ucm-c-surface, #ffffff);
}
.ucm-dial-n { position: relative; font-weight: 700; font-variant-numeric: tabular-nums; }
/*
 * Twenty-one steps rather than a number typed into a style attribute. The
 * attribute says what the thing is and the block panel shows it; a style
 * attribute is the one place the Designer cannot reach, and a component that
 * draws itself with one is a component nobody can restyle.
 */
.ucm-dial[data-pct="0"]{--ucm-pct:0}.ucm-dial[data-pct="5"]{--ucm-pct:5}
.ucm-dial[data-pct="10"]{--ucm-pct:10}.ucm-dial[data-pct="15"]{--ucm-pct:15}
.ucm-dial[data-pct="20"]{--ucm-pct:20}.ucm-dial[data-pct="25"]{--ucm-pct:25}
.ucm-dial[data-pct="30"]{--ucm-pct:30}.ucm-dial[data-pct="35"]{--ucm-pct:35}
.ucm-dial[data-pct="40"]{--ucm-pct:40}.ucm-dial[data-pct="45"]{--ucm-pct:45}
.ucm-dial[data-pct="50"]{--ucm-pct:50}.ucm-dial[data-pct="55"]{--ucm-pct:55}
.ucm-dial[data-pct="60"]{--ucm-pct:60}.ucm-dial[data-pct="65"]{--ucm-pct:65}
.ucm-dial[data-pct="70"]{--ucm-pct:70}.ucm-dial[data-pct="75"]{--ucm-pct:75}
.ucm-dial[data-pct="80"]{--ucm-pct:80}.ucm-dial[data-pct="85"]{--ucm-pct:85}
.ucm-dial[data-pct="90"]{--ucm-pct:90}.ucm-dial[data-pct="95"]{--ucm-pct:95}
.ucm-dial[data-pct="100"]{--ucm-pct:100}

/* ── A line that appears when you point at something ────────────────────── */
/*
 * The words are a sibling element rather than an attribute, so they are a block
 * you can edit and a screen reader can read. Shown on hover and on focus, because
 * a hint only a mouse can reach is a hint half the people never see.
 */
.ucm-hint { position: relative; display: inline-flex; }
.ucm-hint-text {
  position: absolute; bottom: calc(100% + .4rem); left: 50%; translate: -50% 0;
  z-index: 20; width: max-content; max-width: 16rem;
  padding: .35rem .6rem;
  border-radius: calc(var(--ucm-c-radius, .5rem) - .15rem);
  background: #111827; color: #ffffff;
  font-size: calc(var(--ucm-c-font, .875rem) * .85); line-height: 1.4;
  opacity: 0; visibility: hidden;
}
.ucm-hint:hover .ucm-hint-text,
.ucm-hint:focus-within .ucm-hint-text { opacity: 1; visibility: visible; }

/* ── A month ────────────────────────────────────────────────────────────── */
/* A calendar is about the size of a calendar wherever it is put: stretched
   across a wide column it is seven columns of enormous circles. */
.ucm-cal-box { max-width: 22rem; }
.ucm-cal { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: .15rem; text-align: center; }
.ucm-cal-day { padding: .3rem 0; font-size: calc(var(--ucm-c-font, .875rem) * .75); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-cal-cell {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 1; border-radius: 999px;
  font-size: var(--ucm-c-font, .875rem); font-variant-numeric: tabular-nums;
}
.ucm-cal-cell:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }
/* Today is filled; a day with something on it is underlined by a dot, so the
   two can be the same day without either disappearing. */
.ucm-cal-now { background: var(--ucm-color-primary, #2563eb); color: #ffffff; font-weight: 700; }
.ucm-cal-marked { position: relative; font-weight: 600; }
.ucm-cal-marked::after {
  content: ""; position: absolute; bottom: .2rem; left: 50%; translate: -50% 0;
  width: .25rem; height: .25rem; border-radius: 999px;
  background: var(--ucm-color-primary, #2563eb);
}
.ucm-cal-now.ucm-cal-marked::after { background: #ffffff; }

/* ── Buttons with one border between them ───────────────────────────────── */
.ucm-btn-group { display: inline-flex; }
.ucm-btn-group > .ucm-btn { border-radius: 0; }
.ucm-btn-group > .ucm-btn:first-child { border-start-start-radius: var(--ucm-c-radius, .5rem); border-end-start-radius: var(--ucm-c-radius, .5rem); }
.ucm-btn-group > .ucm-btn:last-child { border-start-end-radius: var(--ucm-c-radius, .5rem); border-end-end-radius: var(--ucm-c-radius, .5rem); }
.ucm-btn-group > .ucm-btn + .ucm-btn { margin-inline-start: -1px; }
/* The one that is on sits above its neighbours, or its border disappears under
   theirs at the seam. */
.ucm-btn-group > .ucm-btn[aria-pressed="true"] {
  position: relative; z-index: 1;
  background: var(--ucm-c-wash, rgba(0,0,0,.045));
  font-weight: 700;
}

/* ── Something shown on the thing it runs on ────────────────────────────── */
/*
 * A screenshot in a device, drawn rather than photographed: a picture of a
 * laptop is a file to load, a licence to check and a shape that ages, and all
 * anybody wants is "this is a screen". Borders, corners and a bar are enough.
 *
 * The picture inside is an ordinary `<img>` with no `src` until somebody
 * chooses one, so an empty frame is a grey screen rather than a broken icon.
 */
.ucm-screen { margin: 0; display: block; }
.ucm-screen-shot {
  display: block; width: 100%; aspect-ratio: 16 / 10;
  background: var(--ucm-c-media, #eceef2);
  object-fit: cover;
}

/* A browser window: the bar, the three dots, the address. */
.ucm-screen-browser {
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  overflow: hidden;
  background: var(--ucm-c-surface, #ffffff);
  box-shadow: 0 14px 40px rgba(16, 24, 40, .14);
}
.ucm-screen-bar {
  display: flex; align-items: center; gap: .6rem;
  padding: .55rem .75rem;
  border-bottom: 1px solid var(--ucm-c-border, #e2e5ea);
  background: var(--ucm-c-page, #f7f8fa);
}
/* A dark window: the bar goes dark with the rest of it, and the address reads
   against it. A browser chrome is not a surface the page owns. */
.ucm-dark .ucm-screen-bar, .ucm-screen-browser.ucm-dark .ucm-screen-bar {
  background: #22252e; border-bottom-color: #2f333d;
}
.ucm-dark .ucm-screen-url, .ucm-screen-browser.ucm-dark .ucm-screen-url {
  background: #171a21; color: #9aa3b2;
}
.ucm-screen-dots { display: inline-flex; gap: .35rem; flex: none; }
/*
 * The three lights, in the order every window manager puts them. Read from the
 * theme's own danger, warning and success so a site with its own palette gets
 * its own window — and each with a fallback, because these three colours are
 * the one thing about a browser window everybody recognises and a row of grey
 * circles is not it.
 */
.ucm-screen-dots > span { width: .6rem; height: .6rem; border-radius: 999px; background: var(--ucm-c-border-hover, #cbd1d9); }
.ucm-screen-dots > :nth-child(1) { background: var(--ucm-color-danger,  #e05c4a); }
.ucm-screen-dots > :nth-child(2) { background: var(--ucm-color-warning, #eab33c); }
.ucm-screen-dots > :nth-child(3) { background: var(--ucm-color-success, #5fbd83); }
/* The address is a real line of text, so it can say which page this is. */
.ucm-screen-url {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: .15rem .6rem;
  border-radius: 999px;
  background: var(--ucm-c-surface, #ffffff);
  font-size: calc(var(--ucm-c-font, .875rem) * .8);
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}
.ucm-screen-cap {
  margin-top: .6rem;
  font-size: calc(var(--ucm-c-font, .875rem) * .92);
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}

/* A laptop: a lid with a screen in it, and a base drawn under it. */
.ucm-screen-laptop { display: flex; flex-direction: column; align-items: center; }
/* Narrower than the figure, so the base below can be wider than the lid without
   either of them reaching past the box the component was given. */
.ucm-screen-lid {
  width: 89%;
  padding: .6rem .6rem .4rem;
  border: 1px solid var(--ucm-c-border-hover, #cbd1d9);
  border-radius: .75rem .75rem 0 0;
  background: #1f2430;
}
.ucm-screen-lid > .ucm-screen-shot { border-radius: .2rem; }
/* Wider than the lid and rounded at the bottom, which is the whole illusion. */
.ucm-screen-base {
  display: block; width: 100%; height: .7rem;
  border-radius: 0 0 .5rem .5rem;
  background: linear-gradient(#cbd1d9, #aeb6c2);
}

/* A tablet and a phone: a bezel, a screen, and the two marks that say which. */
.ucm-screen-tablet, .ucm-screen-phone {
  position: relative;
  margin-inline: auto;
  border: 1px solid var(--ucm-c-border-hover, #cbd1d9);
  background: #1f2430;
  box-shadow: 0 14px 40px rgba(16, 24, 40, .18);
}
.ucm-screen-tablet { max-width: 22rem; padding: 1.1rem .8rem; border-radius: 1.1rem; }
.ucm-screen-phone  { max-width: 15rem; padding: 1.6rem .55rem 1.5rem; border-radius: 1.8rem; }
.ucm-screen-tablet > .ucm-screen-shot,
.ucm-screen-phone > .ucm-screen-shot { aspect-ratio: 3 / 4; border-radius: .3rem; }
.ucm-screen-phone > .ucm-screen-shot { aspect-ratio: 9 / 19.5; }
/* The notch and the bar are the two marks that say "phone" without a photo. */
.ucm-screen-notch {
  position: absolute; top: .55rem; left: 50%; translate: -50% 0;
  width: 38%; height: .7rem; border-radius: 999px;
  background: #0d1017;
}
.ucm-screen-home {
  position: absolute; bottom: .55rem; left: 50%; translate: -50% 0;
  width: 34%; height: .25rem; border-radius: 999px;
  background: rgba(255, 255, 255, .55);
}

/* Both at once: the phone over the corner of the browser. */
.ucm-screens { position: relative; padding-bottom: 2rem; padding-inline-end: 3rem; }
.ucm-screens-over {
  position: absolute; inset-inline-end: 0; bottom: 0;
  max-width: 9rem; margin: 0;
}
/* On a phone the two of them side by side is two small pictures. One is
   enough, and the one that fits is the one already the shape of the screen. */
@media (max-width: 560px) {
  .ucm-screens { padding: 0; }
  .ucm-screens-over { position: static; max-width: 12rem; margin: var(--ucm-c-gap, .75rem) auto 0; }
}

/* ── A box with something laid over it ──────────────────────────────────── */
/*
 * A frame is a picture with one thing on top: a play mark, a card of words. It
 * is the positioning and nothing else, so what goes over it is whatever block
 * you put there.
 */
.ucm-frame { position: relative; display: grid; }
.ucm-frame > .ucm-card-media { grid-area: 1 / 1; }
/* Centred over the picture, and big enough to hit on a phone. */
.ucm-play {
  grid-area: 1 / 1; place-self: center;
  display: inline-flex; align-items: center; justify-content: center;
  width: 4rem; height: 4rem; padding: 0;
  border: 0; border-radius: 999px;
  background: rgba(255, 255, 255, .92);
  color: var(--ucm-c-ink, #1f2430);
  box-shadow: 0 8px 24px rgba(16, 24, 40, .24);
  cursor: pointer;
}
.ucm-play:hover { background: #ffffff; transform: scale(1.04); }
/* A map is a wide picture: the address card sits in the corner of it. */
.ucm-map > .ucm-card-media { aspect-ratio: 21 / 9; min-height: 16rem; }
.ucm-frame-card {
  grid-area: 1 / 1; place-self: end start;
  margin: var(--ucm-c-pad-x, 1rem);
  max-width: min(22rem, calc(100% - var(--ucm-c-pad-x, 1rem) * 2));
}
/* On a phone the card covers the map it is describing. Under it instead. */
@media (max-width: 560px) {
  .ucm-map { display: block; }
  .ucm-frame-card { margin: 0; margin-top: var(--ucm-c-gap, .75rem); max-width: none; }
}

/* ── A number people count with ─────────────────────────────────────────── */
.ucm-numeral {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.25rem; height: 2.25rem; flex: none;
  border-radius: 999px;
  background: var(--ucm-color-primary, #2563eb); color: #ffffff;
  font-size: calc(var(--ucm-c-font, .875rem) * 1.05); font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ── Boxes of different sizes that still line up ────────────────────────── */
/*
 * Six columns and a named shape, rather than five cells each told where to go:
 * the first is a square, the second is tall, and the rest fill the row. Adding
 * a sixth cell puts it on a new row instead of breaking the pattern, which is
 * the thing hand-placed bento grids do not survive.
 */
.ucm-bento {
  display: grid; gap: var(--ucm-c-gap, .75rem);
  grid-template-columns: repeat(6, minmax(0, 1fr));
  grid-auto-rows: minmax(8rem, auto);
}
/* Two across the top — a wide one and a tall one — and threes after that. Five
   cells come out a rectangle, and so does eight; the count that matters is the
   one the component ships with. */
.ucm-bento > * { grid-column: span 2; }
.ucm-bento > :first-child { grid-column: span 4; grid-row: span 2; }
.ucm-bento > :nth-child(2) { grid-column: span 2; grid-row: span 2; }
@media (max-width: 720px) {
  .ucm-bento { grid-template-columns: minmax(0, 1fr); grid-auto-rows: auto; }
  .ucm-bento > *, .ucm-bento > :first-child, .ucm-bento > :nth-child(2) {
    grid-column: auto; grid-row: auto;
  }
}

/* ── Who wrote it ───────────────────────────────────────────────────────── */
/* Tighter than a cluster: an avatar and two lines are one thing, and the gap a
   row of buttons wants reads as three things here. */
.ucm-byline { gap: .6rem; align-items: center; }
.ucm-byline-name { margin: 0; font-weight: 600; color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
.ucm-byline-meta {
  display: flex; flex-wrap: wrap; gap: .35rem; margin: .1rem 0 0;
  font-size: calc(var(--ucm-c-font, .875rem) * .92);
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}

/* ── Elsewhere ──────────────────────────────────────────────────────────── */
/*
 * A round link with a mark in it. The label is on the link rather than beside
 * it, so the row is four icons to look at and four names to hear.
 */
.ucm-social {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.5rem; height: 2.5rem; flex: none;
  border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: 999px;
  background: var(--ucm-c-raised, #ffffff);
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  text-decoration: none !important;
}
.ucm-social:hover { border-color: var(--ucm-c-border-hover, #cbd1d9); background: var(--ucm-c-raised-hover, #f3f4f6); }

/* A line across, as an element: a border on the thing above it belongs to that
   thing, and gets carried around when the block moves. */
.ucm-rule { display: block; height: 1px; background: var(--ucm-c-border, #e2e5ea); }

/* ── Scrolls sideways, settles where it stops ───────────────────────────── */
/*
 * A carousel with no script. The row scrolls, snapping stops it between two
 * things, and a swipe on a phone is the same gesture people already use. The
 * row is focusable, so a keyboard scrolls it with the arrow keys.
 *
 * The dots and the arrows are the browser's, not ours: `::scroll-marker` and
 * `::scroll-button()` are a scroll container's own controls, so a dot knows
 * which slide is showing without anybody counting, an arrow at the end of the
 * row disables itself, and both are real buttons a keyboard and a screen reader
 * already understand. Every hand-written version of this is a script that has
 * to be told where the scroll position is, and that is the part that breaks.
 *
 * Where the browser has not got them — Safari and Firefox, at the time of
 * writing — there are no dots and no arrows, and what is left is a row that
 * scrolls and snaps, which is what this was before. The line above it says so
 * in words, and it is hidden again wherever the controls appear.
 */
.ucm-carousel {
  position: relative;
  display: grid; grid-auto-flow: column;
  grid-auto-columns: minmax(min(18rem, 78%), 1fr);
  gap: var(--ucm-c-gap, .75rem);
  margin: 0; padding: 0 0 .5rem; list-style: none;
  overflow-x: auto; overscroll-behavior-inline: contain;
  scroll-snap-type: x mandatory;
  /* Hidden rather than thin: a scrollbar under a row of cards is a second
     horizontal line that means nothing to anybody who is going to swipe. The
     dots below are what says there is more, and they say it better. */
  scrollbar-width: none;
}
.ucm-carousel::-webkit-scrollbar { display: none; }
/* A grid row stretches, so four cards of different lengths end up the same
   height — which is the thing hand-written carousels usually get wrong. */
.ucm-carousel > li { display: grid; min-width: 0; scroll-snap-align: start; }
/* One to a screen, edge to edge, and centred when it stops. */
.ucm-carousel-wide { grid-auto-columns: 100%; gap: 0; padding-bottom: 0; }
.ucm-carousel-wide > li { scroll-snap-align: center; }
/* Said in the markup, so it is there to read before the stylesheet loads and
   can be deleted like any other block. */
.ucm-carousel-hint { margin: 0; font-size: calc(var(--ucm-c-font, .875rem) * .92); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

@supports selector(::scroll-marker-group) {
  /* `after` puts the dots below the row in the flow, so nothing has to be
     positioned and the row keeps its own height. */
  .ucm-carousel { scroll-marker-group: after; scroll-behavior: smooth; }
  .ucm-carousel::scroll-marker-group {
    display: flex; justify-content: center; align-items: center;
    gap: .45rem; padding-block: .7rem;
  }
  .ucm-carousel > li::scroll-marker {
    content: ""; display: block; width: .55rem; height: .55rem;
    border: 1px solid var(--ucm-c-border-hover, #cbd1d9); border-radius: 999px;
    background: transparent; cursor: pointer;
  }
  .ucm-carousel > li::scroll-marker:hover { background: var(--ucm-c-border-hover, #cbd1d9); }
  /* The one showing. This is the part a script would have had to work out. */
  .ucm-carousel > li::scroll-marker:target-current {
    background: var(--ucm-color-primary, #2563eb);
    border-color: var(--ucm-color-primary, #2563eb);
    transform: scale(1.25);
  }
  .ucm-carousel > li::scroll-marker:focus-visible {
    outline: 2px solid var(--ucm-color-primary, #2563eb); outline-offset: 2px;
  }

  /*
   * Arrows, over the row rather than beside it, and out of the way until a
   * pointer is over the carousel or something inside it has focus.
   *
   * Anchored rather than absolutely positioned. A scroll button is the
   * scroller's own pseudo-element and is not laid out inside it, so `position:
   * absolute` in a relatively positioned scroller resolved against the window
   * instead — `top: 50%` came out half the viewport down, which put both arrows
   * somewhere else on the page. `position-anchor` is what these are for, and
   * the anchor resolves per scroller in tree order, so two carousels on a page
   * each get their own pair.
   */
  @supports (anchor-name: --ucm-a) {
    .ucm-carousel { anchor-name: --ucm-carousel; }
    .ucm-carousel::scroll-button(*) {
      position: fixed; position-anchor: --ucm-carousel;
      /* Gone when the row it belongs to is not on screen. */
      position-visibility: anchors-visible;
      top: anchor(center); translate: 0 -50%;
      z-index: 2;
      display: flex; align-items: center; justify-content: center;
      width: 2.5rem; height: 2.5rem; padding: 0;
      border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: 999px;
      background: var(--ucm-c-raised, #ffffff);
      color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
      font-size: 1.25rem; line-height: 1;
      box-shadow: 0 6px 18px rgba(16, 24, 40, .18);
      opacity: 0; cursor: pointer;
    }
    .ucm-carousel:hover::scroll-button(*),
    .ucm-carousel:focus-within::scroll-button(*) { opacity: 1; }
    /* At the end of the row there is nowhere to go, and a button that does
       nothing is worse than no button. */
    .ucm-carousel::scroll-button(*):disabled { opacity: 0; }
    .ucm-carousel::scroll-button(*):focus-visible {
      opacity: 1; outline: 2px solid var(--ucm-color-primary, #2563eb); outline-offset: 2px;
    }
    .ucm-carousel::scroll-button(inline-start) {
      content: "\2039" / "Previous"; left: calc(anchor(left) + .6rem);
    }
    .ucm-carousel::scroll-button(inline-end) {
      content: "\203A" / "Next"; right: calc(anchor(right) + .6rem);
    }
  }

  /* The controls are the affordance now, so the sentence saying to swipe is
     one line of instructions nobody needs. */
  .ucm-carousel-hint { display: none; }
}

/* Smooth scrolling is motion, and somebody who asked for less of it meant
   this too. */
@media (prefers-reduced-motion: reduce) {
  .ucm-carousel { scroll-behavior: auto; }
  .ucm-carousel > li::scroll-marker:target-current { transform: none; }
}

/* A sentence lifted out of the text, at the size it deserves. */
.ucm-pullquote {
  margin: 0; padding-inline-start: var(--ucm-c-pad-x, 1rem);
  border-inline-start: 3px solid var(--ucm-color-primary, #2563eb);
  font-size: calc(var(--ucm-c-font, .875rem) * 1.35); line-height: 1.45; font-weight: 600;
}

/* Code, in the font code is written in, scrolling sideways rather than wrapping. */
.ucm-code {
  margin: 0; overflow-x: auto;
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-raised, var(--ucm-c-surface, #ffffff));
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: calc(var(--ucm-c-font, .875rem) * .92); line-height: 1.6;
  white-space: pre;
}

/* ── A note that is not an alarm ────────────────────────────────────────── */
/*
 * Quieter than an alert on purpose: an alert says something happened, a callout
 * says something is worth knowing while you read.
 */
.ucm-callout {
  display: flex; gap: var(--ucm-c-gap, .75rem);
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border-inline-start: 3px solid var(--ucm-color-primary, #2563eb);
  border-radius: 0 var(--ucm-c-radius, .5rem) var(--ucm-c-radius, .5rem) 0;
  background: var(--ucm-c-wash, rgba(0,0,0,.03));
  font-size: var(--ucm-c-font, .875rem); line-height: 1.6;
}
.ucm-callout-mark { flex: none; color: var(--ucm-color-primary, #2563eb); }

/* ── Yes, no, and how much ──────────────────────────────────────────────── */
.ucm-yes { color: var(--ucm-color-success, #16a34a); font-weight: 700; }
.ucm-no  { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/* ── Where something stands ─────────────────────────────────────────────── */
/*
 * A dot and a word. The dot alone is a colour somebody has to learn, and the
 * word alone is a row of grey text — together they are readable at a glance and
 * still readable to somebody who cannot tell the colours apart.
 */
.ucm-status { display: inline-flex; align-items: center; gap: .45rem; font-size: var(--ucm-c-font, .875rem); font-weight: 600; }
.ucm-status-dot { flex: none; width: .55em; height: .55em; border-radius: 50%; background: currentColor; }
.ucm-status-live { color: var(--ucm-color-success, #16a34a); }
.ucm-status-wait { color: var(--ucm-color-warning, #d97706); }
.ucm-status-stop { color: var(--ucm-color-danger, #dc2626); }
.ucm-status-off  { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/* ── A row of controls above what they control ──────────────────────────── */
.ucm-toolbar {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--ucm-c-gap, .75rem);
  padding: calc(var(--ucm-c-pad, .625rem) * .8) var(--ucm-c-pad-x, 1rem);
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-surface, #ffffff);
}
.ucm-toolbar-grow { flex: 1 1 12rem; min-width: 0; }

/* ── The headings of this page, down the side ───────────────────────────── */
.ucm-anchors { display: flex; flex-direction: column; gap: .1rem; font-size: calc(var(--ucm-c-font, .875rem) * .95); }
.ucm-anchors a {
  padding: .3rem 0 .3rem var(--ucm-c-pad-x, 1rem);
  border-inline-start: 2px solid var(--ucm-c-border, #e2e5ea);
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); text-decoration: none !important;
}
.ucm-anchors a:hover { color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430)); }
.ucm-anchors a[aria-current="true"] { border-inline-start-color: var(--ucm-color-primary, #2563eb); color: var(--ucm-color-primary, #2563eb); font-weight: 700; }

/* ── The bar nobody wants but everybody needs ───────────────────────────── */
/*
 * Not `position: fixed`. A component that pinned itself would be unusable in
 * the editor and in a preview both, and where a consent bar sits is the page's
 * decision — one utility class away, and the author's to make.
 */
.ucm-consent {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--ucm-c-gap, .75rem);
  padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem);
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-surface, #ffffff);
  box-shadow: 0 .75rem 2rem rgba(16, 24, 40, .12);
  font-size: var(--ucm-c-font, .875rem);
}
.ucm-consent-text { flex: 1 1 18rem; min-width: 0; line-height: 1.55; }

/* ── A code typed in from a phone ───────────────────────────────────────── */
/*
 * One box per digit. `inputmode` and a one-character width are what make a
 * phone show the number pad and what stop somebody pasting six digits into the
 * first box and wondering why the rest are empty.
 */
.ucm-otp { display: flex; gap: .5rem; max-width: 100%; }
/*
 * 2.75rem each where there is room, narrower where there is not. A fixed width
 * made six boxes and their gaps a flat 19rem, which is wider than a card on a
 * 360px phone: the first box's focus ring poked out of one side and the sixth
 * box was clipped by the other. `min-width: 0` is what lets an input shrink at
 * all — without it an input keeps its intrinsic width and flex cannot touch it.
 */
.ucm-otp .ucm-input {
  flex: 0 1 2.75rem; min-width: 0;
  padding-inline: 0; text-align: center;
  font-size: calc(var(--ucm-c-font, .875rem) * 1.25); font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ── The preview page's own chrome ──────────────────────────────────────── */
/*
 * Only used by the page that shows every component at once. It belongs here
 * rather than in that page's markup so the page stays a list of components
 * with nothing of its own to go stale.
 */
.ucm-preview-group {
  margin: 2.5rem 0 .25rem; padding-bottom: .4rem;
  border-bottom: 2px solid var(--ucm-c-border, #e2e5ea);
  font-size: 1.1rem; font-weight: 800; letter-spacing: .02em; text-transform: uppercase;
}
.ucm-preview-item { margin: 0 0 2.5rem; }
/*
 * A closed popover shows nothing, which is right on a page and useless on the
 * one page whose whole job is showing what a component looks like. Opened in
 * place here, the same way the editor canvas does it.
 */
.ucm-preview-item .ucm-drawer[popover]:not(:popover-open),
.ucm-preview-item .ucm-modal[popover]:not(:popover-open),
.ucm-preview-item .ucm-sheet[popover]:not(:popover-open),
.ucm-preview-item .ucm-menu[popover]:not(:popover-open) {
  display: block; position: static;
  width: auto; max-width: 22rem; height: auto; max-height: none;
  box-shadow: none; border: 1px solid var(--ucm-c-border, #e2e5ea);
}
.ucm-preview-item .ucm-drawer[popover]:not(:popover-open),
.ucm-preview-item .ucm-sheet[popover]:not(:popover-open) { display: flex; }
/*
 * Live is the page, whatever page is being edited.
 *
 * Every rule that shows a closed popover is scoped so it cannot reach Live —
 * except the preview page's own, written for a front-end page and matching just
 * as well when the canvas is showing that page. Editing the preview page in
 * Live showed a menu hanging open under its button, which is the one thing Live
 * exists to rule out. Scoped rather than removed: on the preview page itself,
 * opened in place is the whole point.
 *
 * Scoped to that chrome, and not to every popover. A blanket rule also hid the
 * one popover a page deliberately shows — an app sidebar is a popover so a
 * phone can open it over the page, and above `md` it is simply the column. Live
 * has to show what the page shows, and the page's own stylesheet is what says
 * so; this is only here to say the preview page's reveal is not part of that.
 *
 * `!important` because it has to beat that reveal, which has more classes in it
 * than this has.
 */
.editor-styles-wrapper[data-ucm-mode="live"] .ucm-preview-item [popover]:not(:popover-open) { display: none !important; }
.ucm-preview-name {
  margin: 0 0 .6rem; font-family: Menlo, Consolas, monospace; font-size: .7rem;
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}

/* ── Pickers: a select with room for more than text ─────────────────────── */
/*
 * The native <select> stays in the markup and stays the value that submits —
 * these are drawn beside it and it is hidden, so the form works the same
 * whether or not the script ever runs.
 */
.ucm-picker { position: relative; display: flex; flex-direction: column; min-width: 0; }
.ucm-picker-source { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/*
 * Until the script runs the select is simply itself: the class above is added by
 * the script, at the same moment it draws the control that replaces it. A pair
 * of rules here used to restore a "source" select that was on its own or last in
 * the field, on the theory that it was the fallback — neither could ever match,
 * because a select with that class always has the control beside it.
 */

.ucm-picker-trigger {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  width: 100%; min-height: 2.6em;
  padding: calc(var(--ucm-c-pad, .625rem) * .8) var(--ucm-c-pad-x, 1rem);
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-raised, var(--ucm-c-surface, #ffffff));
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
  font: inherit; font-size: var(--ucm-c-font, .875rem); text-align: left; cursor: pointer;
}
.ucm-picker-trigger:hover { border-color: var(--ucm-c-border-hover, #cbd1d9); }
.ucm-picker.is-open .ucm-picker-trigger,
.ucm-picker-trigger:focus-visible {
  outline: none; border-color: var(--ucm-color-primary, #2563eb);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ucm-color-primary, #2563eb) 18%, transparent);
}
/* Tags sit closer together than a single value and its caret do, and a row of
   them needs room to wrap onto a second line without the box jumping. */
.ucm-picker-multi { gap: .35rem; align-items: center; }
.ucm-picker-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ucm-picker-value.is-empty { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-picker-caret {
  flex: none; width: 0; height: 0; margin-inline-start: auto;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor; opacity: .55;
}

.ucm-picker-panel {
  position: absolute; z-index: 40; top: 100%; left: 0; right: 0; margin-top: .25rem;
  box-sizing: border-box;
  border: 1px solid var(--ucm-c-border, #e2e5ea);
  border-radius: var(--ucm-c-radius, .5rem);
  background: var(--ucm-c-surface, #ffffff);
  box-shadow: 0 .75rem 2rem rgba(16, 24, 40, .16);
  overflow: hidden;
}
.ucm-picker-search {
  box-sizing: border-box; width: 100%;
  padding: calc(var(--ucm-c-pad, .625rem) * .8) var(--ucm-c-pad-x, 1rem);
  border: 0; border-bottom: 1px solid var(--ucm-c-border, #e2e5ea);
  background: transparent; color: inherit;
  font: inherit; font-size: var(--ucm-c-font, .875rem);
}
.ucm-picker-search:focus { outline: none; }
.ucm-picker-list { max-height: 16rem; overflow-y: auto; padding: .25rem; }
.ucm-picker-group {
  margin: .5rem .5rem .2rem; font-size: .68rem; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280));
}
.ucm-picker-empty { margin: 0; padding: .9rem 1rem; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-size: var(--ucm-c-font, .875rem); }

.ucm-option {
  display: flex; align-items: flex-start; gap: .6rem; width: 100%;
  padding: calc(var(--ucm-c-pad, .625rem) * .75) var(--ucm-c-pad-x, 1rem);
  border: 0; border-radius: calc(var(--ucm-c-radius, .5rem) - .15rem);
  background: none; color: inherit; font: inherit; font-size: var(--ucm-c-font, .875rem);
  text-align: left; cursor: pointer;
}
.ucm-option:hover { background: var(--ucm-c-wash, rgba(0,0,0,.045)); }
.ucm-option.is-chosen { background: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 10%, transparent); }
/* A tick rather than a colour alone, so the choice is not carried by hue. */
.ucm-option.is-chosen::after { content: "\2713"; margin-inline-start: auto; color: var(--ucm-color-primary, #2563eb); font-weight: 700; }
.ucm-option-icon { display: inline-flex; flex: none; margin-top: .1em; color: var(--ucm-color-primary, #2563eb); }
.ucm-option-icon svg { display: block; }
.ucm-option-text { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.ucm-option-label { font-weight: 600; }
.ucm-option-note { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-size: calc(var(--ucm-c-font, .875rem) * .9); }

/* ── Icons ──────────────────────────────────────────────────────────────── */
/*
 * An icon is sized by what holds it, not by a number it was born with.
 *
 * `em` is the whole trick: 1em is the font size of the element the icon is in,
 * so the same icon is small in a caption and large in a headline, and a size
 * class on a wrapper reaches it like everything else. A width in pixels is a
 * decision made once at insert time that nothing afterwards can change — and it
 * is the reason hand-placed icons end up slightly wrong beside the text they
 * belong to.
 *
 * Two shapes, because an icon arrives two ways: inside a wrapper that centres
 * it, and on its own as the svg. The same class name works for both.
 */
.ucm-icon { display: inline-flex; flex: none; align-items: center; justify-content: center; }
.ucm-icon svg { display: block; width: 1.15em; height: 1.15em; }
/* The svg itself carrying the class — what the HTML block's picker inserts.
   After the rule above, because the two tie on weight and an icon inside a
   wrapper should take the wrapper's sizing. `-.15em` sits it on the text's
   baseline rather than hanging it below. */
svg.ucm-icon {
  display: inline-block; flex: none;
  width: 1.15em; height: 1.15em;
  vertical-align: -.15em;
}
/* A button that is only an icon still needs to be square and still needs a name,
   which is why every one of these carries an aria-label. */
.ucm-btn-icon { padding: var(--ucm-c-pad, .625rem); aspect-ratio: 1; }
.ucm-icon-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--ucm-c-gap, .75rem); }
.ucm-icon-list li { display: flex; align-items: flex-start; gap: .7rem; font-size: var(--ucm-c-font, .875rem); line-height: 1.5; }
.ucm-icon-list .ucm-icon { margin-top: .1em; color: var(--ucm-color-primary, #2563eb); }
/* A tinted square behind an icon, for a feature tile. */
.ucm-icon-tile {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.5em; height: 2.5em;
  border-radius: var(--ucm-c-radius, .5rem);
  background: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 12%, transparent);
  color: var(--ucm-color-primary, #2563eb);
}
.ucm-icon-tile svg { width: 1.35em; height: 1.35em; }


/* ── Dark ───────────────────────────────────────────────────────────────── */
/*
 * One class retones everything inside it, because the components read their
 * colours from tokens rather than stating them. Put `ucm-dark` on a section and
 * the cards, inputs, badges and tables within it follow.
 *
 * It sets tokens and nothing else. A `background` or `color` here would land on
 * the component's own root — the class goes on the component, not only on a
 * wrapper — and at equal specificity it would fight the component's own paint,
 * with source order deciding which wins. That is how a "dark theme" quietly
 * turns a primary button's label grey.
 */
/*
 * Light, stated rather than assumed.
 *
 * Light is what the tokens fall back to, so most of the time nothing needs to
 * say it. What needs saying is a light component *inside* a dark one — a white
 * card in a dark section — because the tokens cascade and the card would take
 * the dark ones. This puts them back, so `ucm-light` is not the absence of
 * `ucm-dark` but the opposite of it, and either can be chosen anywhere.
 */
.ucm-light {
  --ucm-c-surface: #ffffff;
  --ucm-c-page: #f7f8fa;
  --ucm-c-ink: #1f2430;
  --ucm-c-muted: #6b7280;
  --ucm-c-border: #e2e5ea;
  --ucm-c-border-hover: #cbd1d9;
  --ucm-c-wash: rgba(0,0,0,.045);
  --ucm-c-wash-soft: rgba(0,0,0,.028);
  --ucm-c-raised: #ffffff;
  --ucm-c-raised-hover: #f3f4f6;
  --ucm-c-media: #eceef2;
  --ucm-c-track: #e5e7eb;
}

.ucm-dark {
  --ucm-c-surface: #171a21;
  --ucm-c-page: #0f1218;
  --ucm-c-ink: #e8eaee;
  --ucm-c-muted: #9aa3b2;
  --ucm-c-border: #2d333d;
  --ucm-c-border-hover: #3d4552;
  --ucm-c-wash: rgba(255,255,255,.07);
  --ucm-c-wash-soft: rgba(255,255,255,.045);
  /* Controls sit above the surface rather than in it. In light this stays unset
     and everything falls back to the surface, which is already lighter than the
     page; in dark it is the only thing separating a field from the card. */
  --ucm-c-raised: #252b35;
  --ucm-c-raised-hover: #313945;
  --ucm-c-media: #232830;
  --ucm-c-track: #343b46;
}

/*
 * The exception: a bare layout root has no surface of its own, so a dark
 * section would otherwise put light text on whatever the page is — which on a
 * light page is nothing at all. Painted here, and only where the component is
 * not already a surface of its own.
 */
/*
 * The page's own ground, which is a different question from what the components
 * on it are.
 *
 * A dark app on a white page has a white gutter down one side and under the
 * footer, because the body is still whatever the theme painted it. These two
 * say what the page is; each component still says what it is. Both together is
 * the usual answer, but a light page holding one dark section — or the reverse
 * — is a thing somebody means, and nothing here stops them.
 *
 * Their own two tokens, not the component ones. Setting `--ucm-c-ink` on the
 * body would have every component inherit it, which is exactly the coupling
 * these two exist to avoid: a white card on a dark page would have gone on
 * taking the page's light text.
 */
body.ucm-page-dark  { background-color: var(--ucm-page-bg, #0f1218); color: var(--ucm-page-ink, #e8eaee); }
body.ucm-page-light { background-color: var(--ucm-page-bg, #ffffff); color: var(--ucm-page-ink, #1f2430); }

/*
 * A layout root retones; it does not paint.
 *
 * `ucm-dark` says "the things in here are dark ones". A stack, a cluster or a
 * field is a way of arranging what is inside it and has no surface of its own,
 * so painting one put a rectangle of some other dark on whatever dark the page
 * already was — a near-black box on a purple page, drawn by a wrapper that is
 * not supposed to be visible at all. What it sets is the ink, so text directly
 * inside it reads; the tokens it carries do the rest for the components within.
 *
 * Where the ground does need to change, that is the page's own business:
 * `ucm-page-dark` above, which the Page creator asks about separately for
 * exactly this reason.
 */
.ucm-dark:is(.ucm-stack, .ucm-autofit, .ucm-cluster, .ucm-field, .ucm-nav-vertical):not(.ucm-card):not(.ucm-alert) {
  color: var(--ucm-c-ink, #e8eaee);
}

/*
 * Tinted things state their own colours, so each needs a dark counterpart. Both
 * selectors every time: the class lands on a nested badge as a descendant, and
 * on an alert as the component's own root.
 */
.ucm-dark .ucm-badge-neutral, .ucm-badge-neutral.ucm-dark { background: #232830; color: #c6cdd8; border-color: #39404b; }
.ucm-dark .ucm-badge-success, .ucm-badge-success.ucm-dark { background: #0f2a1a; color: #86efac; border-color: #1c5233; }
.ucm-dark .ucm-badge-warning, .ucm-badge-warning.ucm-dark { background: #2c2109; color: #fcd34d; border-color: #5a4212; }
.ucm-dark .ucm-badge-danger,  .ucm-badge-danger.ucm-dark  { background: #2d1416; color: #fca5a5; border-color: #5c2326; }
.ucm-dark .ucm-badge-info,    .ucm-badge-info.ucm-dark    { background: #121f3d; color: #93c5fd; border-color: #1e3a72; }

.ucm-dark .ucm-alert-info,    .ucm-alert-info.ucm-dark    { background: #121b30; border-color: #1e3a72; border-left-color: #3b82f6; color: #cbd9f7; }
.ucm-dark .ucm-alert-success, .ucm-alert-success.ucm-dark { background: #10241a; border-color: #1c5233; border-left-color: #16a34a; color: #bbf7d0; }
.ucm-dark .ucm-alert-warning, .ucm-alert-warning.ucm-dark { background: #2a1f0c; border-color: #5a4212; border-left-color: #f59e0b; color: #fde68a; }
.ucm-dark .ucm-alert-danger,  .ucm-alert-danger.ucm-dark  { background: #2a1316; border-color: #5c2326; border-left-color: #dc2626; color: #fecaca; }

/* The select arrow is a data URI, so its colour cannot be a token. */
.ucm-dark .ucm-select, .ucm-select.ucm-dark {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239aa3b2' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

/* A board in the dark: the column is the recess and the card is the surface,
   which is the same relationship as in the light and the opposite of what
   leaving both to inherit would give. */
.ucm-dark .ucm-board-col, .ucm-board-col.ucm-dark { background: rgba(255,255,255,.03); border-color: #39404b; }
.ucm-dark .ucm-board-card, .ucm-board-card.ucm-dark { background: #1b1f26; border-color: #39404b; color: #e6e9ef; }
.ucm-dark .ucm-board-count, .ucm-board-count.ucm-dark { background: #1b1f26; border-color: #39404b; }

/* ── Looks for data views ───────────────────────────────────────────────────
 *
 * One class on a view's wrapper — `ucm-look-striped`, `ucm-look-outline` — and
 * its table, cards, rows, lanes, figures or feed take that look. Every rule is
 * `:where(.ucm-look-…) .ucm-thing`, (0,1,0): the same weight as the component
 * and later in the file, so it wins over the component's default — and a
 * utility on the element itself, (0,1,1), still wins over it.
 */

/* Table — Lines is the default. */
:where(.ucm-look-striped) .ucm-listing-row { border-bottom-color: transparent; }
:where(.ucm-look-striped) .ucm-listing-row:nth-child(even of .ucm-listing-row) { background: var(--ucm-c-wash, rgba(0,0,0,.035)); }
:where(.ucm-look-boxed) .ucm-listing { border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem); overflow: hidden; background: var(--ucm-c-surface, #fff); }
:where(.ucm-look-boxed) .ucm-listing-head { background: var(--ucm-c-wash, rgba(0,0,0,.035)); }
:where(.ucm-look-boxed) .ucm-listing-row { border-radius: 0; }

/* Cards — Raised is the default border; these change it. */
:where(.ucm-look-raised) .ucm-card { border-color: transparent; box-shadow: 0 1px 2px rgba(16,24,40,.06), 0 4px 12px rgba(16,24,40,.07); }
:where(.ucm-look-outline) .ucm-card { box-shadow: none; background: transparent; }
:where(.ucm-look-flat) .ucm-card { border-color: transparent; box-shadow: none; background: var(--ucm-c-wash, rgba(0,0,0,.035)); }
:where(.ucm-look-accent) .ucm-card { border-top: 3px solid var(--ucm-color-primary, #2563eb); }

/* List — Divided is the default. */
:where(.ucm-look-separate) .ucm-rows { border: 0; overflow: visible; gap: calc(var(--ucm-c-gap, .75rem) * .7); }
:where(.ucm-look-separate) .ucm-row { border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem); background: var(--ucm-c-surface, #fff); }
:where(.ucm-look-plain) .ucm-rows { border: 0; border-radius: 0; }
:where(.ucm-look-plain) .ucm-row { border-top-color: transparent; padding-inline: 0; }

/* Board — Tinted lanes are the default. */
:where(.ucm-look-outline) .ucm-board-col { background: transparent; }
:where(.ucm-look-outline) .ucm-board-card { box-shadow: none; }

/* Figures — Cards are the default. */
:where(.ucm-look-accent) .ucm-kpi-value { color: var(--ucm-color-primary, #2563eb); }
:where(.ucm-look-plain) .ucm-card { border: 0; box-shadow: none; background: transparent; padding-inline: 0; }
:where(.ucm-look-plain) .ucm-card + .ucm-card { border-inline-start: 1px solid var(--ucm-c-border, #e2e5ea); padding-inline-start: calc(var(--ucm-c-pad-x, 1rem) * 1.2); }

/* Feed — a Timeline is the default. */
:where(.ucm-look-cards) .ucm-timeline { border-inline-start: 0; padding-inline-start: 0; display: flex; flex-direction: column; gap: calc(var(--ucm-c-gap, .75rem) * .8); }
:where(.ucm-look-cards) .ucm-tl-item { border: 1px solid var(--ucm-c-border, #e2e5ea); border-radius: var(--ucm-c-radius, .5rem); padding: var(--ucm-c-pad, .625rem) var(--ucm-c-pad-x, 1rem); background: var(--ucm-c-surface, #fff); }
:where(.ucm-look-cards) .ucm-tl-item::before { content: none; }
/* A sticky first cell needs a background of its own; on a striped row it is the stripe. */
:where(.ucm-look-striped) .ucm-listing-scroll > .ucm-listing-row:nth-child(even of .ucm-listing-row) > :first-child {
  background: linear-gradient(var(--ucm-c-wash, rgba(0,0,0,.035)), var(--ucm-c-wash, rgba(0,0,0,.035))), var(--ucm-c-surface, #ffffff);
}
/* Figures are short: two to a row on a phone rather than one tall card each. */
@media (max-width: 640px) {
  .ucm-figures { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Form rows ──────────────────────────────────────────────────────────────
 *
 * Fields side by side. A row is a line of fields, each taking its share —
 * `ucm-fw-1-2`, `-1-3`, `-1-4` — minus its part of the gaps, so shares that add
 * up to one fill the line exactly. A row that is not full sits left, or at the
 * centre or the end (`ucm-form-row-center`, `ucm-form-row-end`). On a phone
 * every field takes the whole line.
 */
.ucm-form-row {
  --ucm-form-gap: calc(var(--ucm-c-gap, .75rem) * 1.3);
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--ucm-form-gap);
}
.ucm-form-row > * { flex: 1 1 100%; min-width: 0; }
.ucm-form-row > .ucm-fw-1-2 { flex: 0 0 calc(50% - var(--ucm-form-gap) / 2); }
.ucm-form-row > .ucm-fw-1-3 { flex: 0 0 calc(100% / 3 - var(--ucm-form-gap) * 2 / 3); }
.ucm-form-row > .ucm-fw-1-4 { flex: 0 0 calc(25% - var(--ucm-form-gap) * 3 / 4); }
.ucm-form-row-center { justify-content: center; }
.ucm-form-row-end { justify-content: flex-end; }
@media (max-width: 640px) {
  .ucm-form-row > [class*="ucm-fw-"] { flex-basis: 100%; }
}

/* ── Table columns of different widths ──────────────────────────────────────
 *
 * `ucm-listing-sized`: each title and cell grows by its column's weight —
 * `ucm-cw-1` narrow, `-2` normal, `-3` wide, `-4` wider — and the head uses the
 * same weights, so the columns stay in line. Flex rather than a grid template,
 * because the template would have to be written per table. Below 761px the rows
 * stack as ever; a table that scrolls sideways keeps its columns at every width.
 */
@media (min-width: 761px) {
  .ucm-listing-sized > :is(.ucm-listing-head, .ucm-listing-row) { display: flex; }
}
.ucm-listing-scroll.ucm-listing-sized > :is(.ucm-listing-head, .ucm-listing-row) { display: flex; }
.ucm-listing-sized > :is(.ucm-listing-head, .ucm-listing-row) > * { flex: 2 1 0; min-width: 0; }
.ucm-listing-sized > :is(.ucm-listing-head, .ucm-listing-row) > .ucm-cw-1 { flex-grow: 1; }
.ucm-listing-sized > :is(.ucm-listing-head, .ucm-listing-row) > .ucm-cw-3 { flex-grow: 3; }
.ucm-listing-sized > :is(.ucm-listing-head, .ucm-listing-row) > .ucm-cw-4 { flex-grow: 4; }
/* Row buttons: the same width in the head and every row, or the columns before
   them would not line up. */
.ucm-listing-sized > :is(.ucm-listing-head, .ucm-listing-row) > :last-child:not([class*="ucm-cw-"]) { flex: 0 0 var(--ucm-actions-w, 9rem); }
.ucm-listing-scroll.ucm-listing-sized > :is(.ucm-listing-head, .ucm-listing-row) > [class*="ucm-cw-"] { min-width: 8rem; }
.ucm-listing-scroll.ucm-listing-sized > :is(.ucm-listing-head, .ucm-listing-row) > .ucm-cw-1 { min-width: 5rem; }

/* A column set to the centre (`ucm-cell-center`) or the right (`ucm-num`): its title and its cells. */
.ucm-cell-center { text-align: center; }
.ucm-listing-head :where(button.ucm-cell-center) { justify-content: center; }
.ucm-listing-row > :where(.ucm-cell-center) { justify-self: center; }
/* A right-hand column needs nothing more: `ucm-num` already sets the text to the
   end, and moving the cell itself would take it out from under its title. */

/* ── Detail values side by side ─────────────────────────────────────────────
 * Each row is its own `dl.ucm-dl.ucm-form-row` and each value its share of it,
 * label above value. */
.ucm-dl.ucm-form-row { display: flex; }
.ucm-dl.ucm-form-row > * > dt { padding-top: 0; }
.ucm-dl.ucm-form-row > * > dd { padding-top: .15rem; }
.ucm-dl-rows { gap: calc(var(--ucm-c-gap, .75rem) * 1.1); }

/* ── A header that lies over the top of the page ──────────────────────────────
 *
 * Every setting is a class on the header, set from its Header panel:
 *
 *   ucm-stick-top             stays at the top while the page scrolls
 *   ucm-header-overlay        transparent, over whatever the page starts with
 *                             (a picture, a dark hero), until the page moves
 *   ucm-header-on-dark        what is on top of the page is dark, so the
 *                             header's words and icons are light while it is
 *                             transparent
 *   ucm-header-scrolled-dark  once scrolled it takes a dark background rather
 *                             than a light one
 *
 * behaviours.js adds `is-scrolled` once the page has moved. Inside the brand,
 * `ucm-logo-on-light` holds the logo drawn for a light background and
 * `ucm-logo-on-dark` the one for a dark background; the header shows whichever
 * fits the state it is in. A header with none of these classes is unchanged.
 *
 * `--ucm-header-h` is the header's height, set on the page by the same script,
 * for the first section to keep its content clear of it: `ucm-under-header`.
 */
.ucm-stick-top { top: var(--wp-admin--admin-bar--height, 0px); }
.ucm-header-overlay {
  position: absolute; inset-inline: 0; top: var(--wp-admin--admin-bar--height, 0px); z-index: 30;
  background: transparent; border-color: transparent; box-shadow: none;
  transition: background-color .2s ease, box-shadow .2s ease, color .2s ease;
}
.ucm-header-overlay.ucm-stick-top { position: fixed; }
.ucm-header-overlay.is-scrolled {
  background: var(--ucm-c-surface, #ffffff);
  box-shadow: 0 1px 0 var(--ucm-c-border, #e2e5ea), 0 .5rem 1.25rem rgba(16, 24, 40, .06);
}
.ucm-stick-top.is-scrolled:not(.ucm-header-overlay) { box-shadow: 0 .5rem 1.25rem rgba(16, 24, 40, .06); }
/* Light words over a dark top: the dark tone's ink, on no surface at all. */
.ucm-header-on-dark:not(.is-scrolled) {
  --ucm-c-ink: #ffffff; --ucm-c-muted: rgba(255, 255, 255, .78);
  --ucm-c-wash: rgba(255, 255, 255, .12); --ucm-c-border: rgba(255, 255, 255, .28);
  color: #ffffff;
}
/* Scrolled onto a dark bar: the whole dark tone. */
.ucm-header-scrolled-dark.is-scrolled {
  --ucm-c-surface: #171a21; --ucm-c-ink: #e8eaee; --ucm-c-muted: #9aa3b2;
  --ucm-c-border: #2d333d; --ucm-c-wash: rgba(255, 255, 255, .07);
  background: var(--ucm-c-surface); color: var(--ucm-c-ink);
}
.ucm-header-overlay :is(.ucm-brand, strong) { color: var(--ucm-c-ink); }
/*
 * Two logos, one shown — the one for the ground it is on. Each tone says which
 * as a custom property, so the nearest one wins: a Dark header shows its logo for
 * dark, a Light header inside a dark page its logo for light, and a header over a
 * dark top swaps back when it becomes a light bar.
 */
.ucm-light { --ucm-logo-light: inline-flex; --ucm-logo-dark: none; }
.ucm-dark  { --ucm-logo-light: none; --ucm-logo-dark: inline-flex; }
.ucm-header-on-dark:not(.is-scrolled), .ucm-header-scrolled-dark.is-scrolled { --ucm-logo-light: none; --ucm-logo-dark: inline-flex; }
.ucm-logo-on-light { display: var(--ucm-logo-light, inline-flex); }
.ucm-logo-on-dark { display: var(--ucm-logo-dark, none); }
.ucm-logo-on-light, .ucm-logo-on-dark { align-items: center; gap: .5rem; }
.ucm-logo-on-light img, .ucm-logo-on-dark img { display: block; max-height: 2.25rem; width: auto; }
.ucm-under-header { padding-top: calc(var(--ucm-header-h, 4.5rem) + var(--ucm-c-pad, .625rem) * 4); }
/* The page creator's mark for a page under a header that lies over it: the first
   thing on the page keeps its content clear, and its ground runs up behind the
   header, which is the point of the header being see-through. */
.ucm-clear-header:not(.ucm-under-header) > :first-child:not(.ucm-under-header) {
  padding-top: calc(var(--ucm-header-h, 4.5rem) + var(--ucm-c-pad, .625rem) * 4);
}

/* A hero made to have a see-through header over it: a dark picture, the room
   for the header, and the words in the middle of what is left. */
.ucm-hero-under {
  position: relative; display: grid; place-items: center; text-align: center;
  min-height: min(38rem, 80vh);
  padding-bottom: calc(var(--ucm-c-pad, .625rem) * 6);
  background: linear-gradient(160deg, #1f2937, #111827 70%); color: #fff;
  overflow: hidden;
  /* Always a dark picture, whichever tone the page asked for, so the words are
     light here rather than taken from the tone. */
  --ucm-c-ink: #ffffff; --ucm-c-muted: rgba(255, 255, 255, .78); --ucm-c-border: rgba(255, 255, 255, .25);
  --ucm-c-wash: rgba(255, 255, 255, .1);
}
.ucm-hero-under > .ucm-card-media {
  position: absolute; inset: 0; width: 100%; height: 100%; aspect-ratio: auto;
  border-radius: 0; opacity: .45; background: transparent;
}
.ucm-hero-under > :not(.ucm-card-media) { position: relative; }

/* In Edit the header stays in the page, where it can be worked on (Live shows it
   as a visitor would), and a
   transparent one shows a dark ground under light words so they can be read. */
.editor-styles-wrapper:not([data-ucm-mode="live"]) .ucm-header-overlay { position: relative; top: auto; }
.editor-styles-wrapper:not([data-ucm-mode="live"]) :is(.ucm-header-on-dark:not(.is-scrolled), .ucm-header-overlay.ucm-dark) {
  background: repeating-linear-gradient(135deg, #1f2937 0 10px, #273244 10px 20px);
}
.editor-styles-wrapper:not([data-ucm-mode="live"]) .ucm-logo-on-dark { display: inline-flex; outline: 1px dashed currentColor; outline-offset: 2px; }
.editor-styles-wrapper:not([data-ucm-mode="live"]) .ucm-logo-on-light { outline: 1px dashed currentColor; outline-offset: 2px; }

/* ── Lists ─────────────────────────────────────────────────────────────────
 *
 * `ucm-list` is a list whose marks are drawn, not typed: a tick or an arrow in
 * every item is a character somebody deletes when the list changes, and a
 * screen reader reads it out. Each mark is `::before` with an empty alternative
 * text (`content: "✓" / ""`), so it is seen and not heard.
 *
 *   ucm-list                 dots in the theme colour
 *   ucm-list-checks          ticks, for what is included
 *   ucm-list-crosses         crosses, for what is not
 *   ucm-list-numbers         numbered, in the theme colour
 *   ucm-list-arrows          arrows, for a list of next steps or links
 *   ucm-list-dashes          dashes, quiet
 *   ucm-list-divided         a line between items; an item may be a row
 *   ucm-list-inline          on one line, dots between
 *   ucm-list-cols-2 / -3     in columns, one on a phone
 */
.ucm-list {
  margin: 0; padding: 0; list-style: none;
  display: grid; gap: calc(var(--ucm-c-gap, .75rem) * .6);
  font-size: var(--ucm-c-font, .875rem); line-height: 1.55;
  color: var(--ucm-c-ink, var(--ucm-color-text, #1f2430));
}
.ucm-list > li { position: relative; padding-inline-start: 1.7em; }
.ucm-list > li::before {
  content: ""; position: absolute; inset-inline-start: .45em; top: .62em;
  width: .42em; height: .42em; border-radius: 50%;
  background: var(--ucm-color-primary, #2563eb);
}
.ucm-list-checks > li, .ucm-list-crosses > li, .ucm-list-numbers > li { padding-inline-start: 2em; }
/* The mark's own font is smaller than the item's, so its circle is sized in the
   mark's units to come out a little taller than a capital of the item. */
.ucm-list-checks > li::before, .ucm-list-crosses > li::before, .ucm-list-numbers > li::before {
  inset-inline-start: 0; top: .08em; width: 1.75em; height: 1.75em;
  display: grid; place-items: center; border-radius: 50%;
  font-size: .78em; font-weight: 800; line-height: 1;
}
.ucm-list-checks > li::before {
  content: "\2713"; content: "\2713" / "";
  color: var(--ucm-color-success, #16a34a);
  background: color-mix(in srgb, var(--ucm-color-success, #16a34a) 14%, transparent);
}
.ucm-list-crosses > li::before {
  content: "\2715"; content: "\2715" / "";
  color: var(--ucm-color-danger, #dc2626);
  background: color-mix(in srgb, var(--ucm-color-danger, #dc2626) 12%, transparent);
}
.ucm-list-crosses > li { color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-list-numbers { counter-reset: ucm-list; }
.ucm-list-numbers > li { counter-increment: ucm-list; }
.ucm-list-numbers > li::before {
  content: counter(ucm-list); content: counter(ucm-list) / "";
  color: #fff; background: var(--ucm-color-primary, #2563eb);
}
.ucm-list-arrows > li::before, .ucm-list-dashes > li::before {
  inset-inline-start: 0; top: 0; width: auto; height: auto; background: none; border-radius: 0;
  font-weight: 700;
}
.ucm-list-arrows > li::before { content: "\2192"; content: "\2192" / ""; color: var(--ucm-color-primary, #2563eb); }
.ucm-list-dashes > li::before { content: "\2013"; content: "\2013" / ""; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/* Divided: a rule between items, no marks, and an item may be a row — a name and
   a value, a link and its arrow. */
.ucm-list-divided { gap: 0; }
.ucm-list-divided > li { padding: calc(var(--ucm-c-pad, .625rem) * 1.1) 0; }
.ucm-list-divided > li::before { content: none; }
.ucm-list-divided > li + li { border-top: 1px solid var(--ucm-c-border, #e2e5ea); }
.ucm-list-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--ucm-c-gap, .75rem); }
.ucm-list-row > :last-child { flex: none; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); font-variant-numeric: tabular-nums; }
a.ucm-list-row, a.ucm-list-row:link, a.ucm-list-row:visited { color: inherit; text-decoration: none; font-weight: 600; }
a.ucm-list-row:hover { color: var(--ucm-color-primary, #2563eb); }
a.ucm-list-row > :last-child { transition: transform .15s ease; }
a.ucm-list-row:hover > :last-child { transform: translateX(3px); color: var(--ucm-color-primary, #2563eb); }

/* Inline: one line that wraps, a dot between items. */
.ucm-list-inline { display: flex; flex-wrap: wrap; gap: .25rem 0; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }
.ucm-list-inline > li { padding: 0; }
.ucm-list-inline > li::before { content: none; }
.ucm-list-inline > li + li::before {
  content: "\00B7"; content: "\00B7" / ""; position: static; display: inline; background: none;
  margin-inline: .6em; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #9aa3b2));
}

/* In columns. */
.ucm-list-cols-2, .ucm-list-cols-3 { display: block; column-gap: calc(var(--ucm-c-gap, .75rem) * 2.5); }
.ucm-list-cols-2 > li, .ucm-list-cols-3 > li { break-inside: avoid; margin-bottom: calc(var(--ucm-c-gap, .75rem) * .6); }
.ucm-list-cols-2 { columns: 2; }
.ucm-list-cols-3 { columns: 3; }
@media (max-width: 640px) { .ucm-list-cols-2, .ucm-list-cols-3 { columns: 1; } }

/* Numbered steps with words: a larger number beside a title and a line. */
.ucm-list-steps { counter-reset: ucm-list; gap: calc(var(--ucm-c-gap, .75rem) * 1.5); }
.ucm-list-steps > li { counter-increment: ucm-list; padding-inline-start: 3.1em; min-height: 2.2em; }
.ucm-list-steps > li::before {
  content: counter(ucm-list); content: counter(ucm-list) / "";
  inset-inline-start: 0; top: 0; width: 2.2em; height: 2.2em; display: grid; place-items: center;
  border-radius: 50%; font-weight: 800; color: var(--ucm-color-primary, #2563eb);
  background: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 12%, transparent);
}
.ucm-list-title { display: block; margin: 0; font-weight: 700; }
.ucm-list-text { display: block; margin: .15rem 0 0; color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); }

/* ── More cards ────────────────────────────────────────────────────────────── */
/* Picture beside the words: stacked on a phone, side by side from 640px. */
.ucm-card-row { display: grid; gap: var(--ucm-c-gap, .75rem); }
@media (min-width: 640px) {
  .ucm-card-row { grid-template-columns: minmax(8rem, 15rem) minmax(0, 1fr); align-items: center; }
  .ucm-card-row > .ucm-card-media { aspect-ratio: 4 / 3; }
}
/* Words over the picture: the card is the picture, the words a band across it. */
.ucm-card-over {
  position: relative; overflow: hidden; display: flex; align-items: flex-end;
  min-height: 16rem; padding: 0; border: 0;
  background: var(--ucm-c-media, #3b4252); color: #fff;
}
.ucm-card-over > .ucm-card-media { position: absolute; inset: 0; width: 100%; height: 100%; aspect-ratio: auto; border-radius: 0; }
.ucm-card-over-body {
  position: relative; width: 100%;
  padding: calc(var(--ucm-c-pad, .625rem) * 1.8);
  background: linear-gradient(to top, rgba(8, 10, 16, .82), rgba(8, 10, 16, .35) 70%, transparent);
}
.ucm-card-over .ucm-card-title, .ucm-card-over .ucm-card-sub { color: #fff; }
.ucm-card-over .ucm-card-sub { opacity: .82; }
/* A date on a card: the day large, the month small, in a tinted block. */
.ucm-card-date {
  flex: none; display: grid; place-items: center; align-content: center;
  width: 3.6rem; height: 3.6rem; border-radius: var(--ucm-c-radius, .5rem);
  background: color-mix(in srgb, var(--ucm-color-primary, #2563eb) 12%, transparent);
  color: var(--ucm-color-primary, #2563eb); line-height: 1;
}
.ucm-card-date-day { font-size: 1.35rem; font-weight: 800; }
.ucm-card-date-month { margin-top: .2rem; font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
/* A person: centred, the picture large. */
.ucm-card-person { text-align: center; justify-items: center; }
.ucm-card-person .ucm-avatar { width: 4.5rem; height: 4.5rem; font-size: 1.3rem; }
/* A card that is one link: the title carries it, and an arrow says so. */
.ucm-card-go { display: inline-block; margin-top: auto; font-weight: 700; color: var(--ucm-color-primary, #2563eb); transition: transform .15s ease; }
.ucm-clickable:hover .ucm-card-go { transform: translateX(4px); }
.ucm-card.ucm-clickable:hover { border-color: var(--ucm-color-primary, #2563eb); }

/* Features as a list: a tile for the icon, then a title and a line. */
.ucm-list-features { gap: calc(var(--ucm-c-gap, .75rem) * 1.4); }
.ucm-list-features > li { display: flex; align-items: flex-start; gap: var(--ucm-c-gap, .75rem); padding: 0; }
.ucm-list-features > li::before { content: none; }
.ucm-list-features .ucm-icon-tile { flex: none; }

/* A badge that is only an outline, and one with a dot before its word. */
.ucm-badge-outline { background: transparent; border: 1px solid currentColor; }
.ucm-badge-dot::before {
  content: ""; display: inline-block; width: .45em; height: .45em; margin-inline-end: .4em;
  border-radius: 50%; background: currentColor; vertical-align: .1em;
}

/* Notifications: a row each, a dot on the ones not read yet. */
.ucm-notice-row { position: relative; align-items: flex-start; }
.ucm-notice-row[data-unread]::after {
  content: ""; position: absolute; inset-inline-end: var(--ucm-c-pad-x, 1rem); top: calc(var(--ucm-c-pad, .625rem) + .45em);
  width: .5rem; height: .5rem; border-radius: 50%; background: var(--ucm-color-primary, #2563eb);
}
.ucm-notice-row[data-unread] .ucm-row-main > :first-child { font-weight: 700; }
.ucm-notice-when { flex: none; margin-inline-end: 1.1rem; font-size: calc(var(--ucm-c-font, .875rem) * .88); color: var(--ucm-c-muted, var(--ucm-color-text-muted, #6b7280)); white-space: nowrap; }

/* A roadmap: a column per stage, the stage's name above its cards. */
.ucm-roadmap-col { display: grid; gap: calc(var(--ucm-c-gap, .75rem) * .8); align-content: start; }
.ucm-roadmap-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }

/* Choices as tiles: a radio each, the whole tile the label, the chosen one outlined. */
.ucm-choice-tile { flex-direction: column; gap: .35rem; }
.ucm-choice-tile input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
.ucm-choice-tile { position: relative; }
.ucm-choice-tile:has(:checked) { box-shadow: 0 0 0 1px var(--ucm-color-primary, #2563eb); }
.ucm-choice-tile:has(:focus-visible) { outline: 2px solid var(--ucm-color-primary, #2563eb); outline-offset: 2px; }
.ucm-choice-value { font-weight: 800; font-size: calc(var(--ucm-c-font, .875rem) * 1.15); }
/* On the components page a header meant to lie over a hero stays on its stage,
   on a dark ground standing in for the picture it would lie over. */
.ucm-showcase-stage .ucm-header-overlay { position: relative; top: auto; }
.ucm-showcase-stage :is(.ucm-header-on-dark:not(.is-scrolled), .ucm-header-overlay.ucm-dark:not(.is-scrolled)) { background: linear-gradient(135deg, #1f2937, #374151); }

/* A band of big numbers for a marketing page: centred, each with its label. */
.ucm-stats-band { text-align: center; }
.ucm-stats-band .ucm-kpi-value { font-size: calc(var(--ucm-c-font, .875rem) * 3); color: var(--ucm-color-primary, #2563eb); }
.ucm-stats-band .ucm-card-sub { margin-top: .25rem; }
@media (max-width: 640px) { .ucm-even-4.ucm-stats-band { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.ucm-stats-band .ucm-kpi-value, .ucm-stats-band .ucm-card-sub { margin-block: 0; }

/* ── A picture behind a block ──────────────────────────────────────────── */
/*
 * Set from the Designer's Background group (UCM_Blocks::with_background).
 *
 * The picture itself is written inline on the element — `background-image`
 * with a shade layer over `--ucm-bg-image` — because two hundred rules in this
 * file use the `background` shorthand, and the first one tried, the hero's
 * `ucm-band`, reset the picture to nothing. Inline, no component rule can.
 *
 * How it sits stays here, last in the file: after every component rule of the
 * same weight, so a band's shorthand cannot put it back to `auto` and the
 * top-left corner, and still below the utilities (`ucm-bg-top`, `ucm-bg-contain`
 * — `body .ucm-x`, 0,1,1) that the same Designer group writes.
 */
.ucm-bg-image {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
/* Light or Dark on a block with a picture says what colour its words are —
   on a plain section the switch only sets the tokens. */
.ucm-bg-image:is(.ucm-light, .ucm-dark) { color: var(--ucm-c-ink); }
