/* ============================================================================
   EquipDash Design System v2 - ui kit styles (P1)
   Styles for the <x-ui.*> blade components. Tokens only - no raw hexes.
   Spec: docs/mockups/design-system-v2.html
   ========================================================================== */

/* ---- <x-ui.button> ---- */
.ui-btn {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	border: none;
	border-radius: var(--ed-r-pill);
	padding: 9px 17px;
	font: 600 13.5px/1.4 var(--ed-font);
	white-space: nowrap;
	cursor: pointer;
	text-decoration: none;
	transition: background var(--ed-fast), color var(--ed-fast);
}

.ui-btn--primary {
	background: var(--ed-navy-700);
	color: var(--ed-on-accent);
}

.ui-btn--primary:hover {
	background: var(--ed-navy-600);
	color: var(--ed-on-accent);
}

/* The border sits INSIDE the primary's box: 9px of padding less the 1px
   border, so a secondary standing beside a primary is exactly as tall. It was
   2px taller on every row the two shared, which is the "two controls built to
   match come out different sizes" shape. */
.ui-btn--secondary {
	background: var(--ed-surface);
	border: 1px solid var(--ed-border);
	padding: 8px 16px;
	color: var(--ed-ink);
}

.ui-btn--secondary:hover {
	background: var(--ed-sunken);
	color: var(--ed-ink);
}

.ui-btn--ghost {
	background: none;
	color: var(--ed-navy-600);
}

.ui-btn--ghost:hover {
	background: var(--ed-navy-100);
	color: var(--ed-navy-700);
}

/* A red outline that fills on hover (owner, 2026-10-27): the action is
   marked without shouting louder than the page's main button. */
.ui-btn--danger {
	background: var(--ed-surface);
	border: 1px solid var(--ed-danger);
	padding: 8px 16px;
	color: var(--ed-danger);
}

.ui-btn--danger:hover {
	background: var(--ed-danger);
	border-color: var(--ed-danger);
	color: var(--ed-on-accent);
}

.ui-btn--ai {
	background: linear-gradient(120deg, var(--ed-ai), var(--ed-ai-2));
	color: var(--ed-on-accent);
}

.ui-btn--sm {
	padding: 6px 13px;
	font-size: 13px;
}

/* Same trick at the small size. This rule has to come after .ui-btn--sm or its
   padding wins and a small secondary is tall again. */
.ui-btn--sm.ui-btn--secondary,
.ui-btn--sm.ui-btn--danger {
	padding: 5px 12px;
}

/* Clear action-button shape for task choices, distinct from filter pills. */
.ui-btn--control { min-height: 44px; border-radius: var(--ed-r-ctl); }
.ui-btn--control.ui-btn--secondary { border-color: var(--ed-muted); }

.ui-btn:disabled,
.ui-btn.disabled {
	opacity: .45;
	cursor: default;
	pointer-events: none;
}

/* Row-action icon button: plain muted glyph, never boxed, never wraps. */
.ui-btn--icon {
	background: none;
	border: none;
	border-radius: var(--ed-r-ctl);
	padding: 4px 5px;
	color: var(--ed-muted);
}

.ui-btn--icon:hover {
	background: var(--ed-sunken);
	color: var(--ed-ink);
}

/* ---- <x-ui.pill> ---- */
.ui-pill {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	border-radius: var(--ed-r-pill);
	padding: 3px 10px;
	font: 600 11.5px/1.4 var(--ed-font);
	white-space: nowrap;
}

.ui-pill--success { background: var(--ed-success-soft); color: var(--ed-success); }
.ui-pill--warning { background: var(--ed-warning-soft); color: var(--ed-warning); }
.ui-pill--danger  { background: var(--ed-danger-soft);  color: var(--ed-danger); }
.ui-pill--info    { background: var(--ed-navy-100);     color: var(--ed-navy-600); }
.ui-pill--ai      { background: var(--ed-sunken);       color: var(--dash); }
.ui-pill--count   { background: var(--ed-navy-100);     color: var(--ed-navy-600); padding: 1px 7px; font-size: 10.5px; }

/* ---- <x-ui.card> ---- */
.ui-card {
	background: var(--ed-surface);
	border-radius: var(--ed-r-card);
	box-shadow: var(--ed-shadow-card);
}

/* A card never touches the card below it (owner, 2026-09-30: "needs to be margin
   under reconnect box otherwise it touches"). Same 16px a settings section keeps
   under itself; sibling margins collapse, so a card that already sets mb-3 is unchanged. */
.ui-card + .ui-card,
.ui-card + .set-section {
	margin-top: 16px;
}

.ui-card__header {
	display: flex;
	align-items: center;
	gap: var(--ed-s2);
	padding: 13px 18px;
	border-bottom: 1px solid var(--ed-border);
	font: 600 13.5px/1.4 var(--ed-font);
	color: var(--ed-ink);
}

.ui-card__action {
	margin-left: auto;
	font: 600 12.5px/1.4 var(--ed-font);
	color: var(--ed-navy-600);
}

.ui-card__body {
	padding: var(--ed-s4) 18px;
}

/* ---- <x-ui.empty-state> ---- */
.ui-empty {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
	padding: var(--ed-s8) var(--ed-s6);
	text-align: center;
}

.ui-empty__art {
	width: 74px;
	height: 74px;
	border-radius: 50%;
	background: var(--ed-navy-100);
	display: grid;
	place-items: center;
	font-size: 28px;
}

.ui-empty__title {
	font: 600 15px/1.4 var(--ed-font);
	color: var(--ed-ink);
}

.ui-empty__body {
	font: 400 12.5px/1.5 var(--ed-font);
	color: var(--ed-muted);
	max-width: 40ch;
}

/* ---- <x-ui.phone-frame> ----
   The bezel only. Everything about the picture inside it - the ground, the
   bubbles, how tall it is - belongs to the page, so one phone can hold a live
   iframe and another a chat thread without the frame knowing either. */
.ui-phone {
	border: 1px solid var(--ed-border);
	border-radius: 26px;
	padding: 9px;
	background: var(--ed-sunken);
	box-shadow: var(--ed-shadow-card);
}

.ui-phone__screen {
	border-radius: 18px;
	overflow: hidden;
	background: var(--ed-surface);
}

/* An iframe screen fills the width and keeps the height the page gives it. */
.ui-phone__screen > iframe {
	display: block;
	width: 100%;
	border: 0;
	background: transparent;
}

/* handset: the phone as a picture of the product, at hero size. Navy bezel,
   rounder corners, and the notch the screen content sits under. */
.ui-phone--handset {
	border: 0;
	border-radius: 36px;
	padding: 9px;
	background: var(--ed-navy-900);
	box-shadow: var(--ed-shadow-pop);
}

.ui-phone--handset .ui-phone__screen { border-radius: 28px; }

.ui-phone__notch {
	width: 76px;
	height: 18px;
	margin: 0 auto 6px;
	border-radius: 0 0 12px 12px;
	background: var(--ed-navy-900);
}

/* ---- <x-ui.browser-frame> ----
   A web page as a customer meets it: three dots, the address, then the page.
   Same contract as the phone - the frame owns the bar and the corners, the
   page owns the picture inside and its height. */
.ui-browser {
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-card);
	overflow: hidden;
	background: var(--ed-surface);
	box-shadow: var(--ed-shadow-pop);
}

.ui-browser__bar {
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 9px 12px;
	background: var(--ed-sunken);
	border-bottom: 1px solid var(--ed-border);
}

.ui-browser__dot {
	flex: 0 0 auto;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--ed-border);
}

.ui-browser__address {
	flex: 0 1 auto;
	min-width: 0;
	margin: 0 auto;
	padding: 4px 14px;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
	font: 400 11.5px/1.2 var(--ed-font);
	color: var(--ed-muted);
	background: var(--ed-surface);
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-pill);
}

.ui-browser__screen { background: var(--ed-surface); }

/* ---- <x-ui.float-card> ----
   A caption floating over a picture of the product. Never a control: no
   button lives in it. The page places it; the card only paints. */
.ui-float-card {
	background: var(--ed-surface);
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-card);
	box-shadow: var(--ed-shadow-pop);
	padding: 12px 14px;
	text-align: left;
}

.ui-float-card__head {
	display: flex;
	align-items: center;
	gap: 8px;
}

.ui-float-card__dot {
	flex: 0 0 auto;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--ed-success);
}

.ui-float-card__title {
	font: 700 13px/1.3 var(--ed-font);
	color: var(--ed-navy-700);
}

.ui-float-card__meta {
	margin-left: auto;
	font: 400 12px/1.3 var(--ed-font);
	color: var(--ed-muted);
	white-space: nowrap;
}

.ui-float-card__body {
	margin-top: 4px;
	font: 400 12.5px/1.5 var(--ed-font);
	color: var(--ed-muted);
}

/* ---- <x-ui.setup-card> (unset feature; no padlock - lock means plan gating) ---- */
.ui-setup {
	display: flex;
	align-items: center;
	gap: 12px;
	border: 1px dashed var(--ed-setup-border);
	border-radius: 10px;
	padding: 12px 15px;
	background: var(--ed-sunken);
	/* Breathing room above/below - these cards sat flush against their
	   neighbors in the detail rails (#4312). Sibling margins collapse. */
	margin: 12px 0;
}

.ui-setup__title {
	font: 600 13px/1.4 var(--ed-font);
	color: var(--ed-ink);
	display: block;
}

.ui-setup__status {
	font: 400 12px/1.45 var(--ed-font);
	color: var(--ed-muted);
	display: block;
}

.ui-setup__action {
	margin-left: auto;
	flex: 0 0 auto;
	font: 600 12.5px/1.4 var(--ed-font);
	color: var(--ed-navy-600);
	white-space: nowrap;
}

.ui-setup__action button {
	background: none;
	border: none;
	padding: 0;
	font: inherit;
	color: inherit;
	cursor: pointer;
	white-space: nowrap;
}

/* ---- <x-ui.check-row> (one required step in a readiness list) ---- */
.ui-check {
	background: var(--ed-surface);
	border: 1px solid var(--ed-border);
	border-radius: 10px;
	transition: border-color var(--ed-fast), box-shadow var(--ed-fast);
}

/* `.ui-check > ...` on the two padded parts: the showcase resets padding on
   `.ds2 *` at class specificity, and the demo must render like the page. */
.ui-check > .ui-check__row {
	display: flex;
	align-items: center;
	/* Narrow hosts (a 330px rail, a three-up list): the title keeps one or two
	   lines and the action cluster drops to its own line, right-aligned. */
	flex-wrap: wrap;
	gap: 12px;
	padding: 12px 14px;
	min-height: 52px;
}

.ui-check__row.is-clickable { position: relative; cursor: pointer; }
.ui-check__hit { position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
button.ui-check__hit { padding: 0; border: 0; background: transparent; }
.ui-check__hit:focus-visible { outline: 2px solid var(--ed-navy-600); outline-offset: -2px; }
.ui-check:has(.ui-check__hit):hover { border-color: var(--ed-navy-600); box-shadow: 0 0 0 1px var(--ed-navy-600); }
.ui-check__row.is-clickable .ui-check__actions { position: relative; z-index: 2; }

.ui-check__mark {
	flex: 0 0 auto;
	width: 24px;
	height: 24px;
	border-radius: 50%;
	border: 1.5px solid var(--ed-border);
	display: grid;
	place-items: center;
	font: 600 11.5px/1 var(--ed-font);
	color: var(--ed-muted);
	font-variant-numeric: tabular-nums;
}

.ui-check__text {
	flex: 1 1 150px;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 1px;
}

.ui-check__title {
	font: 600 13.5px/1.4 var(--ed-font);
	color: var(--ed-ink);
}

.ui-check__status {
	font: 400 12px/1.45 var(--ed-font);
	color: var(--ed-muted);
}

.ui-check__actions {
	flex: 0 0 auto;
	margin-left: auto;
	display: flex;
	flex-wrap: wrap;
	max-width: 100%;
	align-items: center;
	gap: 6px;
}

.ui-check__actions:empty {
	display: none;
}

.ui-check > .ui-check__panel {
	border-top: 1px solid var(--ed-border);
	padding: 14px;
	background: var(--ed-sunken);
	border-radius: 0 0 10px 10px;
}

/* Next: the one row asking for attention. */
.ui-check.is-next {
	border-color: var(--ed-navy-600);
	box-shadow: 0 0 0 1px var(--ed-navy-600);
}

.ui-check.is-next .ui-check__mark {
	border-color: var(--ed-navy-600);
	color: var(--ed-navy-600);
}

/* Done: ticked, quiet. */
.ui-check.is-done .ui-check__mark {
	background: var(--ed-success-soft);
	border-color: var(--ed-success-soft);
	color: var(--ed-success);
}

.ui-check.is-done .ui-check__title {
	color: var(--ed-muted);
	font-weight: 500;
}

/* With an icon: the old Getting started row, merged (issue 4895). A 40px tile
   that says what the step is about, a roomier row, and the tile turns navy on
   the step that is next and green once it is done. */
.ui-check.has-icon > .ui-check__row {
	gap: 14px;
	padding: 14px 18px;
	min-height: 72px;
}

.ui-check.has-icon .ui-check__mark {
	width: 40px;
	height: 40px;
	border: 0;
	border-radius: 10px;
	background: var(--ed-sunken);
	color: var(--ed-muted);
	font-size: 15px;
}

.ui-check.has-icon .ui-check__title { font-size: 14.5px; }
.ui-check.has-icon .ui-check__status { font-size: 13px; }

.ui-check.has-icon.is-next .ui-check__mark {
	background: var(--ed-navy-700);
	color: var(--ed-on-accent);
}

.ui-check.has-icon.is-done .ui-check__mark {
	background: var(--ed-success-soft);
	color: var(--ed-success);
}

.ui-check.has-icon:not(.is-done):hover {
	border-color: var(--ed-navy-600);
}

.ui-check.has-icon:not(.is-done):not(.is-next):hover .ui-check__mark {
	background: var(--ed-navy-100);
	color: var(--ed-navy-700);
}

.ui-check__guide { white-space: nowrap; }

/* Later: not counted yet - same dashed register as the setup card. */
.ui-check.is-later {
	border-style: dashed;
	border-color: var(--ed-setup-border);
	background: var(--ed-sunken);
}

@media (max-width: 767px) {
	.ui-check__actions {
		width: 100%;
		justify-content: flex-end;
	}

	.ui-check__actions .ui-btn {
		min-height: 44px;
	}
}

/* ---- <x-ui.choice-cards> ---- */
.ui-choice {
	display: flex;
	gap: 10px;
}

.ui-choice__card {
	flex: 1;
	position: relative;
	border: 1.5px solid var(--ed-border);
	border-radius: 10px;
	padding: 12px 14px;
	background: var(--ed-surface);
	cursor: pointer;
	margin: 0;
	transition: border-color var(--ed-fast), background var(--ed-fast);
}

/* A choice you can hear: the play button sits in the card's corner and never
   picks the card, so a voice is auditioned before it is chosen. */
.ui-choice__play {
	position: absolute;
	top: 8px;
	right: 8px;
	width: 26px;
	height: 26px;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	border: 1px solid var(--ed-border);
	border-radius: 50%;
	background: var(--ed-canvas);
	color: var(--ed-navy-600);
	cursor: pointer;
	transition: background var(--ed-fast), border-color var(--ed-fast);
}

.ui-choice__play:hover,
.ui-choice__play.is-playing {
	background: var(--ed-navy-100);
	border-color: var(--ed-navy-600);
}

.ui-choice__card:has(.ui-choice__play) .ui-choice__title {
	padding-right: 26px;
}

.ui-choice__card input {
	position: absolute;
	opacity: 0;
	pointer-events: none;
}

.ui-choice__thumb {
	display: block;
	background: var(--ed-canvas);
	border-radius: 8px;
	padding: 8px;
	margin-bottom: 9px;
}

.ui-choice__thumb svg {
	display: block;
	width: 100%;
	height: auto;
	fill: var(--ed-muted);
	opacity: 0.32;
}

.ui-choice__card:has(input:checked) .ui-choice__thumb svg {
	fill: var(--ed-navy-600);
	opacity: 0.42;
}

.ui-choice__title {
	font: 600 13px/1.4 var(--ed-font);
	color: var(--ed-ink);
	display: block;
	margin-bottom: 2px;
}

.ui-choice__desc {
	font: 400 12px/1.45 var(--ed-font);
	color: var(--ed-muted);
	display: block;
}

/* Optional glyph above the title (a Font Awesome class) - for pathway-style
   cards that have no drawing. Optional struck former price before the desc. */
.ui-choice__icon {
	display: block;
	font-size: 16px;
	color: var(--ed-navy-600);
	margin-bottom: 8px;
}

.ui-choice__was {
	color: var(--ed-faint);
	margin-right: 4px;
}

/* The radio itself is visually hidden, so the card carries the focus ring -
   without this a keyboard user cannot tell which option they are on. */
.ui-choice__card:hover,
.ui-choice__card:has(input:focus-visible) {
	border-color: var(--ed-navy-600);
	background: var(--ed-navy-100);
}

.ui-choice__card:has(input:focus-visible) {
	outline: 2px solid var(--ed-navy-600);
	outline-offset: 2px;
}

.ui-choice__card.is-selected,
.ui-choice__card:has(input:checked) {
	border-color: var(--ed-navy-600);
	background: var(--ed-navy-100);
}

/* More cards than fit a row: grid, not flex-wrap. A wrapped flex row lets the
   last row's cards grow to fill it, so two leftovers end up several times the
   size of the rest. auto-fill keeps the track count - and every card - the same
   width on every row. */
.ui-choice--wrap {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
	align-items: stretch;
}

.ui-choice--wrap .ui-choice__card {
	min-width: 0;
}

/* Same drawing size in every card, whatever the option count. */
.ui-choice--wrap .ui-choice__thumb {
	aspect-ratio: 16 / 10;
	display: flex;
	align-items: center;
	justify-content: center;
}

.ui-choice--wrap .ui-choice__thumb svg {
	max-height: 100%;
}

/* Guided-job choices: a compact action row, with a quiet completed state. */
.ui-choice--tour .ui-choice__card {
	display: grid;
	grid-template-columns: 52px minmax(0, 1fr) 16px;
	align-content: center;
	column-gap: var(--ed-s3);
	min-height: 116px;
	padding: var(--ed-s4);
	border-radius: var(--ed-r-card);
	box-shadow: var(--ed-shadow-card);
}
.ui-choice--tour .ui-choice__icon {
	grid-row: 1 / 3;
	display: grid;
	place-items: center;
	width: 52px;
	height: 52px;
	margin: 0;
	border-radius: var(--ed-r-ctl);
	background: var(--ed-navy-100);
	font-size: 24px;
}
.ui-choice--tour .ui-choice__title { grid-column: 2; font-size: 15px; align-self: end; }
.ui-choice--tour .ui-choice__desc { grid-column: 2; font-size: 13px; }
.ui-choice--tour .ui-choice__card::after {
	content: '›';
	grid-column: 3;
	grid-row: 1 / 3;
	align-self: center;
	color: var(--ed-navy-600);
	font-size: 28px;
}
.ui-choice--tour .ui-choice__card,
.ui-choice--tour .ui-choice__card * { cursor: pointer; }
.ui-choice--tour .ui-choice__card--done {
	border-color: var(--ed-success-border);
	background: var(--ed-success-soft);
	opacity: .8;
}
.ui-choice--tour .ui-choice__card--done:hover,
.ui-choice--tour .ui-choice__card--done:has(input:focus-visible) {
	background: var(--ed-success-soft);
	border-color: var(--ed-success-border);
	opacity: 1;
}
.ui-choice--tour .ui-choice__card--done .ui-choice__icon { background: var(--ed-success-soft); color: var(--ed-success-ink); }
.ui-choice--tour .ui-choice__done { grid-column: 2; color: var(--ed-success-ink); font: 600 12px/1.4 var(--ed-font); }
@media (max-width: 480px) { .ui-choice--tour .ui-choice__card { min-height: 96px; } }

@media (max-width: 767px) {
	.ui-choice {
		flex-direction: column;
	}

	.ui-choice--wrap {
		grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
	}
}

/* ---- <x-ui.stepper> ---- */
.ui-stepper {
	display: inline-flex;
	align-items: center;
	gap: 12px;
}

.ui-stepper__btn {
	width: 26px;
	height: 26px;
	border-radius: 50%;
	border: 1px solid var(--ed-border);
	background: var(--ed-surface);
	color: var(--ed-muted);
	display: grid;
	place-items: center;
	font-size: 13px;
	line-height: 1;
	cursor: pointer;
}

.ui-stepper__btn--add {
	background: var(--ed-navy-700);
	color: var(--ed-on-accent);
	border-color: var(--ed-navy-700);
}

.ui-stepper__btn:disabled {
	opacity: .4;
	cursor: default;
}

.ui-stepper__value {
	font: 600 14px/1 var(--ed-font);
	min-width: 18px;
	text-align: center;
	font-variant-numeric: tabular-nums;
}

/* ---- <x-ui.field> ---- */
.ui-field { display: flex; flex-direction: column; }
.ui-field__label { font: 600 13px/1.4 var(--ed-font); color: var(--ed-ink); margin-bottom: 2px; }
.ui-field__hint { font: 400 12px/1.45 var(--ed-font); color: var(--ed-muted); margin-bottom: 7px; }
.ui-field__error { font: 400 12px/1.45 var(--ed-font); color: var(--ed-danger); margin-top: 4px; }

/* ---- <x-ui.toggle> row (the switch itself is the global .form-switch spec) ---- */
.ui-toggle-row { display: flex; align-items: center; gap: 12px; }
.ui-toggle-row__label { flex: 1; font: 400 13.5px/1.4 var(--ed-font); color: var(--ed-ink); margin: 0; }

/* ---- <x-ui.tabs> ---- */
.ui-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--ed-border); list-style: none; padding: 0; margin: 0 0 var(--ed-s4); flex-wrap: nowrap; overflow-x: auto; min-width: 0; max-width: 100%; }
.ui-tabs__item { flex: 0 0 auto; }
.ui-tabs__link { display: inline-flex; align-items: center; gap: 6px; background: none; border: none; border-bottom: 2px solid transparent; padding: 10px 14px; font: 600 13px/1.4 var(--ed-font); color: var(--ed-muted); white-space: nowrap; cursor: pointer; }
.ui-tabs__link.is-active { color: var(--ed-navy-700); border-bottom-color: var(--ed-navy-700); }

/* ---- <x-ui.advanced> ---- */
.ui-adv { border-top: 1px solid var(--ed-border); margin-top: var(--ed-s2); }
.ui-adv__header { display: flex; align-items: center; gap: 8px; width: 100%; background: none; border: none; padding: 11px 0; font: 600 13px/1.4 var(--ed-font); color: var(--ed-muted); cursor: pointer; text-align: left; }
.ui-adv__chevron { display: inline-block; transition: transform var(--ed-fast); }
.ui-adv__chevron.is-open { transform: rotate(90deg); }
.ui-adv__summary { font-weight: 400; color: var(--ed-faint); }
.ui-adv__body { display: flex; flex-direction: column; gap: var(--ed-s3); padding: 2px 0 var(--ed-s3); }

/* ---- <x-ui.select> ---- */
/* The background is --ed-sunken, the same as a text input, NOT --ed-surface.
   A white select beside a sunken input reads as two different controls on one
   row - "the new drop-down color should be same as input bg color, like its a
   bit grey otherwise it looks inconsistent" (#4668). Change it here, not per
   page: this is the one rule every dropdown in the app inherits. */
.ui-select { width: 100%; height: 40px; border: 1px solid var(--ed-border); border-radius: var(--ed-r-ctl); padding: 0 34px 0 13px; font: 400 13.5px/1.4 var(--ed-font); color: var(--ed-ink); background: var(--ed-sunken) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2395a2b6' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 12px center / 12px; appearance: none; }
.ui-select:focus { outline: 2px solid var(--ed-navy-600); outline-offset: -1px; }
/* A failed field reads the same as a failed .form-control beside it. */
.ui-select.is-invalid { border-color: var(--ed-danger); }
/* width:100% above is load-bearing, not cosmetic. A native <select> sizes to its
   longest option, so a column of them ends up with a different left edge per
   row - the row has to own the control width, never the control's contents. */
/* A sidebar drawer's fields are the 48px / 16px register (.form-control +
   .font-400-16-22), so a kit select dropped into one has to be the same height
   as the inputs above and below it or the column reads as broken. Scoped to the
   drawer, never to the instance. */
.modal-sidebar .ui-select { height: 48px; font-size: 16px; line-height: 22px; }
/* The compact register (size="sm"), the same everywhere a row of small controls
   sits, drawer included - the same height and text as .ui-money--sm. */
.ui-select.ui-select--sm,
.modal-sidebar .ui-select.ui-select--sm { height: 32px; font-size: 13px; line-height: 1.4; padding: 0 28px 0 10px; background-position: right 9px center; }

/* ---- <x-ui.date-time-input> ----
   The same register as <x-ui.select> beside it: 40px / 13.5px, and the drawer's
   48px / 16px. The class is doubled so a page's form-group input rule cannot
   re-size it. */
.ui-date-time.ui-date-time { box-sizing: border-box; width: 100%; height: 40px; margin: 0; border: 1px solid var(--ed-border); border-radius: var(--ed-r-ctl); padding: 0 13px; font: 400 13.5px/1.4 var(--ed-font); color: var(--ed-ink); background: var(--ed-sunken); }
.ui-date-time.ui-date-time:focus { outline: 2px solid var(--ed-navy-600); outline-offset: -1px; }
.ui-date-time.ui-date-time.is-invalid { border-color: var(--ed-danger); }
.modal-sidebar .ui-date-time.ui-date-time { height: 48px; font-size: 16px; line-height: 22px; }

/* ---- <x-ui.money-input> ----
   Every rule below is scoped to .ui-money, not to the child class alone: a page
   reset (the /design-system showcase has one) outranks a single-class rule and
   would strip the padding, so the primitive would look different in the very
   place agents copy it from.
   The addon and the input are one control (learned rule 0029): both halves share
   one explicit height, zero margin and the flat seam, so no global .form-control
   margin or padding can push the input taller than its addon. Invalid swaps the
   border color only - never an icon that changes the box. */
.ui-money { display: flex; align-items: stretch; width: 100%; margin: 0; }
.ui-money .ui-money__addon,
.ui-money .ui-money__input { box-sizing: border-box; height: 40px; margin: 0; border: 1px solid var(--ed-border); font: 400 13.5px/1 var(--ed-font); color: var(--ed-ink); }
.ui-money .ui-money__addon { display: flex; align-items: center; flex: 0 0 auto; padding: 0 12px; background: var(--ed-sunken); color: var(--ed-muted); white-space: nowrap; }
.ui-money__addon--prefix { border-right: 0; border-radius: var(--ed-r-ctl) 0 0 var(--ed-r-ctl); }
.ui-money__addon--suffix { border-left: 0; border-radius: 0 var(--ed-r-ctl) var(--ed-r-ctl) 0; }
.ui-money .ui-money__input { flex: 1 1 auto; min-width: 0; padding: 0 12px; background: var(--ed-sunken); border-radius: 0 var(--ed-r-ctl) var(--ed-r-ctl) 0; appearance: textfield; -moz-appearance: textfield; }
.ui-money__input::-webkit-outer-spin-button,
.ui-money__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ui-money--suffix .ui-money__input { border-radius: var(--ed-r-ctl) 0 0 var(--ed-r-ctl); }
/* The seam has to win over the drawer's field rule (style.css: .modal-sidebar .form-group input
   { border-radius: 12px; padding: 13px 12px 11px }, specificity 0,2,1), which re-rounded the joined
   edge of every money input placed in a .form-group inside a drawer. Doubled class = 0,3,0. */
.ui-money.ui-money .ui-money__input { border-radius: 0 var(--ed-r-ctl) var(--ed-r-ctl) 0; padding: 0 12px; }
.ui-money.ui-money--suffix .ui-money__input { border-radius: var(--ed-r-ctl) 0 0 var(--ed-r-ctl); }
.ui-money.ui-money--sm .ui-money__input { padding: 0 10px; }
.ui-money:focus-within { border-radius: var(--ed-r-ctl); outline: 2px solid var(--ed-navy-600); outline-offset: -1px; }
.ui-money .ui-money__input:focus { outline: none; }
.ui-money--sm .ui-money__addon,
.ui-money--sm .ui-money__input { height: 32px; font-size: 13px; }
.ui-money--sm .ui-money__addon,
.ui-money--sm .ui-money__input { padding-left: 10px; padding-right: 10px; }
.ui-money--invalid .ui-money__addon,
.ui-money--invalid .ui-money__input { border-color: var(--ed-danger); }
.ui-money__error { margin-top: 4px; font-size: 12px; color: var(--ed-danger); }
/* The sidebar drawer register is 48px / 16px (same as .modal-sidebar .ui-select),
   so a money input in a drawer matches the fields above and below it. sm stays compact. */
.modal-sidebar .ui-money:not(.ui-money--sm) .ui-money__addon,
.modal-sidebar .ui-money:not(.ui-money--sm) .ui-money__input { height: 48px; font-size: 16px; }
/* Settings rows set their text at 16px; a money field there reads at the same size as the fields beside it (#2957). */
.set-section-body .ui-money:not(.ui-money--sm) .ui-money__input { font-size: 16px; }

/* ---- <x-ui.color-field> (#6070) ----
   The code box and the swatch at its end are ONE control, like .ui-money: one
   40px height, zero margin, a flat seam, and a red border on both halves when the
   code is not a color. Every box rule is doubled (.ui-color.ui-color, 0,3,0) so a
   host page's field rules cannot re-round the seam or restyle the box - the
   website builder's `.wb-fld input[type=text]` / `.wb-fld input:focus` (0,2,1)
   and `.wb-fld label` would otherwise give the code box a 10px radius, a grey
   fill and a focus ring of its own. Preset and chip colors are the shop's data,
   set inline by the component; everything else here is a token. */
.ui-color-field { width: 100%; }
.ui-color { position: relative; display: flex; align-items: stretch; width: 100%; max-width: 200px; margin: 0; }
.ui-color.ui-color .ui-color__hex,
.ui-color.ui-color .ui-color__swatch { box-sizing: border-box; height: 40px; min-height: 0; margin: 0; border: 1px solid var(--ed-border); background: var(--ed-sunken); box-shadow: none; }
.ui-color.ui-color .ui-color__hex { flex: 1 1 auto; width: auto; min-width: 0; padding: 0 12px; font: 400 13.5px/1 var(--ed-font); color: var(--ed-ink);
	text-transform: uppercase; letter-spacing: .02em; font-variant-numeric: tabular-nums; outline: none;
	border-radius: var(--ed-r-ctl) 0 0 var(--ed-r-ctl); }
.ui-color.ui-color .ui-color__hex::placeholder { color: var(--ed-faint); text-transform: none; letter-spacing: 0; }
.ui-color.ui-color .ui-color__swatch { position: relative; flex: 0 0 40px; display: flex; align-items: center; justify-content: center;
	padding: 0; cursor: pointer; border-left: 0; border-radius: 0 var(--ed-r-ctl) var(--ed-r-ctl) 0; }
.ui-color__chip { display: block; width: 24px; height: 24px; border-radius: 6px; box-shadow: inset 0 0 0 1px var(--ed-border); }
.ui-color__chip.is-empty,
.ui-color__preset.is-none { background: linear-gradient(135deg, transparent 46%, var(--ed-faint) 46%, var(--ed-faint) 54%, transparent 54%), var(--ed-surface) !important; }
.ui-color__swatch input[type=color] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; border: 0; padding: 0; margin: 0; cursor: pointer; }
.ui-color:focus-within { border-radius: var(--ed-r-ctl); outline: 2px solid var(--ed-navy-600); outline-offset: -1px; }
.ui-color--invalid.ui-color .ui-color__hex,
.ui-color--invalid.ui-color .ui-color__swatch { border-color: var(--ed-danger); }
.ui-color--invalid:focus-within { outline-color: var(--ed-danger); }
.ui-color__error { margin-top: 4px; font: 400 12px/1.45 var(--ed-font); color: var(--ed-danger); }
.ui-color__presets { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.ui-color__preset { width: 24px; height: 24px; padding: 0; border: 0; border-radius: 6px; cursor: pointer; box-shadow: inset 0 0 0 1px var(--ed-border); }
.ui-color__preset:hover { box-shadow: inset 0 0 0 1px var(--ed-border), 0 0 0 2px var(--ed-surface), 0 0 0 3px var(--ed-faint); }
.ui-color__preset.is-on,
.ui-color__preset:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--ed-border), 0 0 0 2px var(--ed-surface), 0 0 0 4px var(--ed-navy-700); }
/* Phone: bigger taps; the primitive owns this, never the page. */
@media (max-width: 767px) {
	.ui-color__presets { gap: 8px; }
	.ui-color__preset { width: 32px; height: 32px; border-radius: 8px; }
}

/* ---- a plain text input inside a kit card ----
   The kit's row register is 40px with no bottom margin: <x-ui.select>,
   <x-ui.money-input> and the toggle rows are all built to it. The global
   .form-control is 54px tall with a 10px bottom margin, so a text input
   dropped beside a kit select sat 14px taller than it, and the button on the
   same flex row stretched to 64px and rendered as a circle ("the ring for and
   own number are different heights", "the add button... it's like a round
   button??", #5084).
   Scoped to the card, never to the instance, and in the same shape as the
   .modal-sidebar register above: correct markup gets the correct height with
   nothing to remember (learned rules 0018, 0029). A textarea keeps its own
   height - it is sized by rows, not by the row it sits on. */
.ui-card__body .form-control {
	height: 40px;
	padding: 0 13px;
	margin-bottom: 0;
	font: 400 13.5px/1.4 var(--ed-font);
}

.ui-card__body textarea.form-control {
	height: auto;
	padding: 9px 13px;
}

/* ---- <x-ui.kbd> ---- */
.ui-kbd { display: inline-flex; align-items: center; border: 1px solid var(--ed-border); background: var(--ed-sunken); border-radius: 5px; padding: 2px 7px; font: 600 11px/1.3 var(--ed-font); color: var(--ed-muted); white-space: nowrap; }

/* ---- <x-ui.hero> ---- */
.ui-hero { display: flex; align-items: center; gap: var(--ed-s4); background: var(--ed-surface); border-radius: var(--ed-r-card); box-shadow: var(--ed-shadow-card); padding: var(--ed-s4) var(--ed-s5); }
.ui-hero__facts { display: flex; gap: 26px; flex-wrap: wrap; align-items: center; }
.ui-hero__k { font: 600 11px/1.3 var(--ed-font); text-transform: uppercase; letter-spacing: .06em; color: var(--ed-faint); }
.ui-hero__v { font: 600 13.5px/1.4 var(--ed-font); color: var(--ed-ink); margin-top: 2px; display: flex; align-items: center; gap: 6px; }
.ui-hero__actions { margin-left: auto; display: flex; gap: var(--ed-s2); flex: 0 0 auto; }
@media (max-width: 767px) {
	.ui-hero { flex-wrap: wrap; }
	.ui-hero__facts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; width: 100%; }
	.ui-hero__actions { margin-left: 0; width: 100%; }
	.ui-hero__actions .ui-btn { flex: 1; justify-content: center; }
}

/* ---- <x-ui.stat> ---- */
.ui-stat { border: 1px solid var(--ed-border); border-radius: 10px; padding: 14px 16px; background: var(--ed-surface); }
.ui-stat__value { font: var(--ed-display-24); color: var(--ed-ink); font-variant-numeric: tabular-nums; }
.ui-stat__label { font: 400 12.5px/1.45 var(--ed-font); color: var(--ed-muted); }
.ui-stat__gauge { height: 8px; border-radius: var(--ed-r-pill); background: var(--ed-border); overflow: hidden; margin-top: 8px; }
.ui-stat__gauge i { display: block; height: 100%; background: var(--ed-navy-600); }
/* A stat that opens the rows behind it. The tile is the link, so it reads as
   one control rather than a number with an anchor inside it. */
.ui-stat--link { display: block; text-decoration: none; color: inherit; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; }
.ui-stat--link:hover, .ui-stat--link:focus-visible { border-color: var(--ed-navy-600); box-shadow: 0 1px 6px rgba(0, 0, 0, .07); text-decoration: none; }

/* ---- <x-ui.bulk-bar> ---- */
.ui-bulk { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; background: var(--ed-ink); color: var(--ed-on-accent); border-radius: 10px; padding: 9px 14px; font: 600 13px/1.4 var(--ed-font); }
.ui-bulk a, .ui-bulk button { color: var(--ed-on-accent); background: none; border: none; font: inherit; cursor: pointer; text-decoration: underline; }
.ui-bulk--touch button { min-height: 44px; }
.ui-bulk > * { white-space: nowrap; }
.ui-bulk__sep { color: var(--ed-faint); }
.ui-bulk__clear { margin-left: auto; color: rgba(255, 255, 255, .8) !important; text-decoration: none !important; }
.ui-bulk__clear:hover { color: var(--ed-on-accent) !important; }

/* ---- <x-ui.filter-bar> ---- */
.ui-filter-bar { display: flex; align-items: center; gap: 9px; margin-bottom: var(--ed-s3); flex-wrap: wrap; }
.ui-filter-bar__trailing { margin-left: auto; display: flex; gap: 9px; }
.ui-search { display: flex; align-items: center; gap: 8px; background: var(--ed-surface); border: 1px solid var(--ed-border); border-radius: var(--ed-r-pill); padding: 0 15px; height: 40px; min-width: 240px; color: var(--ed-faint); }
.ui-search input { border: none; outline: none; background: none; font: 400 13.5px/1.4 var(--ed-font); color: var(--ed-ink); flex: 1; }
.ui-filter-pill { display: inline-flex; align-items: center; gap: 7px; height: 40px; background: var(--ed-surface); border: 1px solid var(--ed-border); border-radius: var(--ed-r-pill); padding: 0 15px; font: 600 13px/1.4 var(--ed-font); color: var(--ed-ink); white-space: nowrap; cursor: pointer; }
.ui-filter-pill i { font-size: 10px; color: var(--ed-faint); }
/* The filter that is on. The markup has always set this class; without a rule
   for it the owner could not see which pill the list was filtered by. */
.ui-filter-pill.is-active { border-color: var(--ed-navy-700); background: color-mix(in srgb, var(--ed-navy-700) 8%, transparent); color: var(--ed-navy-700); }
@media (max-width: 767px) {
	.ui-search { min-width: 100%; order: -1; }
}

/* ---- <x-ui.actions-cell> ---- */
.ui-actions-cell { white-space: nowrap; }

/* ---- <x-ui.code-block> + <x-ui.secret-row> ---- */
.ui-code { border: 1px solid var(--ed-border); border-radius: 10px; overflow: hidden; }
.ui-code__header { display: flex; align-items: center; gap: 8px; padding: 9px 13px; background: var(--ed-sunken); border-bottom: 1px solid var(--ed-border); font: 600 12.5px/1.4 var(--ed-font); color: var(--ed-ink); }
.ui-code__copy { margin-left: auto; background: none; border: none; font: 600 12px/1.4 var(--ed-font); color: var(--ed-navy-600); cursor: pointer; }
.ui-code__pre { margin: 0; padding: 13px 15px; background: var(--ed-ink); color: var(--ed-navy-100); font: 11.5px/1.6 ui-monospace, Menlo, monospace; overflow-x: auto; }
.ui-secret { display: flex; align-items: center; gap: 12px; border: 1px solid var(--ed-border); border-radius: 10px; padding: 10px 15px; background: var(--ed-surface); }
.ui-secret__label { flex: 1; font: 600 13px/1.4 var(--ed-font); color: var(--ed-ink); }
.ui-secret__label small { display: block; font: 400 11px/1.4 var(--ed-font); color: var(--ed-faint); }
.ui-secret__value { font: 600 12px/1.4 ui-monospace, Menlo, monospace; color: var(--ed-muted); }
.ui-secret__action { background: none; border: none; font: 600 12px/1.4 var(--ed-font); color: var(--ed-navy-600); cursor: pointer; white-space: nowrap; }
/* On a phone the label takes its own line above the key and its actions (#5532):
   squeezed beside them it broke a word per line. */
@media (max-width: 575.98px) {
	.ui-secret { flex-wrap: wrap; row-gap: 6px; }
	.ui-secret__label { flex: 1 0 100%; }
	.ui-secret__value { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}


/* ============================================================================
   Kit coverage (#4104): charts, calendar, reorder, builders.
   ========================================================================== */

/* --- KPI row + stat delta --- */
.ui-kpi { display: flex; gap: 12px; flex-wrap: wrap; }
.ui-kpi .ui-stat { flex: 1; min-width: 150px; background: var(--ed-surface); }
.ui-stat__delta { font-size: 11px; font-weight: 800; margin-left: 4px; }
.ui-stat__delta.is-good { color: var(--ed-success); }
.ui-stat__delta.is-bad { color: var(--ed-danger); }

/* --- Comparison bar chart --- */
.ui-chart__header { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.ui-chart__title { font-size: 13.5px; color: var(--ed-ink); }
.ui-chart__toolbar { margin-left: auto; }
.ui-chart__plot { display: flex; align-items: flex-end; gap: 14px; height: 140px; border-bottom: 1px solid var(--ed-border); padding: 0 4px; }
.ui-chart__bar { flex: 1; display: flex; align-items: flex-end; justify-content: center; gap: 3px; height: 100%; }
.ui-chart__bar i { display: block; width: 16px; border-radius: 4px 4px 0 0; }
.ui-chart__prev { background: var(--ed-navy-200); }
.ui-chart__cur { background: var(--ed-navy-600); }
.ui-chart__axis { display: flex; gap: 14px; padding: 6px 4px 0; }
.ui-chart__axis span { flex: 1; text-align: center; font: var(--ed-caption-11); text-transform: uppercase; letter-spacing: .06em; color: var(--ed-faint); }
/* Single series (#1525): one wider bar, and an optional figure above each. */
.ui-chart--single .ui-chart__bar i { width: min(46px, 70%); }
.ui-chart--values .ui-chart__plot { height: 170px; }
.ui-chart--values .ui-chart__bar { flex-direction: column; align-items: center; justify-content: flex-end; }
.ui-chart__value { font: 600 11px/1.2 var(--ed-font); color: var(--ed-muted); margin-bottom: 4px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ui-chart--values .ui-chart__axis span { text-transform: none; letter-spacing: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- Segmented chart toolbar --- */
.ui-chart-seg { display: inline-flex; background: var(--ed-sunken); border: 1px solid var(--ed-border); border-radius: 999px; padding: 2px; gap: 2px; }
.ui-chart-seg button { border: none; background: none; border-radius: 999px; padding: 5px 12px; font: 600 12px/1.2 var(--ed-font); color: var(--ed-muted); cursor: pointer; white-space: nowrap; }
.ui-chart-seg button.is-on { background: var(--ed-surface); color: var(--ed-navy-700); box-shadow: 0 1px 2px rgba(29, 43, 69, .08); }

/* --- Donut + legend --- */
.ui-donut-wrap { display: flex; gap: 16px; align-items: center; }
.ui-donut { width: 96px; height: 96px; border-radius: 50%; display: grid; place-items: center; flex: 0 0 96px; }
.ui-donut i { width: 62px; height: 62px; border-radius: 50%; background: var(--ed-surface); display: grid; place-items: center; font: 700 14px/1 var(--ed-font); color: var(--ed-ink); font-style: normal; text-align: center; }
/* One segment plus a caption reads as a progress ring: the figure, then the
   word that says what it counts. */
.ui-donut i b { font: 700 19px/1 var(--ed-font); letter-spacing: -0.4px; }
.ui-donut__cap { display: block; margin-top: 3px; font: 400 11px/1.2 var(--ed-font); color: var(--ed-muted); }
.ui-donut__legend { display: flex; flex-direction: column; gap: 6px; font: var(--ed-small-12); color: var(--ed-muted); }
.ui-donut__legend span { display: flex; align-items: center; gap: 7px; }
.ui-donut__legend i { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 10px; }
.ui-donut__legend b { color: var(--ed-ink); }

/* --- Date strip --- */
.ui-dstrip { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
.ui-dstrip__day { flex: 1; min-width: 44px; min-height: 44px; border: 1px solid var(--ed-border); background: var(--ed-surface); border-radius: 10px; padding: 6px 4px; text-align: center; cursor: pointer; }
.ui-dstrip__day small { display: block; font: var(--ed-caption-11); text-transform: uppercase; letter-spacing: .06em; color: var(--ed-faint); }
.ui-dstrip__day b { display: block; font-size: 15px; color: var(--ed-ink); margin-top: 2px; }
.ui-dstrip__day.is-selected { border-color: var(--ed-navy-700); background: var(--ed-navy-100); }
.ui-dstrip__day.is-selected b { color: var(--ed-navy-700); }

/* --- Timeline lanes --- */
.ui-lane { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--ed-border); }
.ui-lane:last-child { border-bottom: 0; }
.ui-lane__name { width: 130px; flex: 0 0 130px; font: var(--ed-small-12); color: var(--ed-muted); }
.ui-lane__name b { display: block; color: var(--ed-ink); }
.ui-lane__track { flex: 1; height: 18px; border-radius: 6px; background: var(--ed-sunken); position: relative; overflow: hidden; }
.ui-lane__seg { position: absolute; top: 2px; bottom: 2px; border-radius: 4px; }
.ui-lane__seg.is-available { background: var(--ed-success-soft); border: 1px solid var(--ed-success-border); }
.ui-lane__seg.is-booked { background: var(--ed-navy-100); border: 1px solid var(--ed-navy-200); }
.ui-lane__seg.is-blocked { background: var(--ed-hatch-blocked); }
.ui-lane__now { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--ed-danger); }
.ui-timeline__scroll { overflow-x: auto; }
.ui-lane__tick, .ui-lane__band, .ui-lane__axis { pointer-events: none; }
/* Booking bars: labelled bars stacked in rows, an hour axis, bands where more is needed than owned. */
/* A fixed width per hour: a long day scrolls instead of squeezing its labels together. */
.ui-timeline--bars .ui-timeline__inner { min-width: calc(180px + var(--ui-tl-hours, 8) * 96px); }
.ui-timeline--bars .ui-lane { align-items: stretch; }
.ui-timeline--bars .ui-lane__name { width: 180px; flex-basis: 180px; padding: 2px 10px 2px 0; position: sticky; left: 0; z-index: 3; background: var(--ed-surface); text-align: left; }
.ui-lane__name--link { border: 0; cursor: pointer; font: var(--ed-small-12); color: var(--ed-muted); }
.ui-lane__name--link:hover b { text-decoration: underline; }
.ui-lane__name span { display: block; }
.ui-lane__alert { color: var(--ed-danger); font-weight: 600; }
.ui-timeline--bars .ui-lane__track { height: auto; min-height: 30px; background: none; overflow: visible; padding: 2px 0; }
.ui-lane--axis { padding: 0; border-bottom: 1px solid var(--ed-border); }
.ui-lane__axis { flex: 1; position: relative; height: 20px; font: var(--ed-small-12); color: var(--ed-muted); }
.ui-lane__axis span { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap; }
.ui-lane__axis span.is-first { transform: none; }
.ui-lane__axis span.is-last { transform: translateX(-100%); }
.ui-lane__tick { position: absolute; top: 0; bottom: 0; border-left: 1px dashed var(--ed-border); }
.ui-lane__band { position: absolute; top: 0; bottom: 0; background: repeating-linear-gradient(135deg, var(--ed-danger-soft) 0 6px, transparent 6px 12px); border-left: 2px solid var(--ed-danger); border-right: 2px solid var(--ed-danger); }
.ui-lane__row { position: relative; height: 26px; margin: 3px 0; }
.ui-lane__bar { position: absolute; top: 0; height: 26px; padding: 0 8px; border: 1px solid var(--ed-navy-200); border-radius: 6px; background: var(--ed-navy-100); color: var(--ed-ink); font: var(--ed-small-12); line-height: 24px; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; z-index: 1; }
button.ui-lane__bar { cursor: pointer; }
button.ui-lane__bar:hover { box-shadow: var(--ed-shadow-card); }
button.ui-lane__bar:focus-visible { outline: 2px solid var(--ed-navy-600); outline-offset: 1px; }
.ui-lane__bar.is-out { background: var(--ed-navy-700); border-color: var(--ed-navy-700); color: var(--ed-on-accent); }
.ui-lane__bar.is-session { background: var(--ed-success-soft); border-color: var(--ed-success-border); color: var(--ed-success); }
.ui-lane__bar.is-late { background: var(--ed-danger-soft); border-color: var(--ed-danger); color: var(--ed-danger); }
.ui-lane__after { float: right; margin-left: 8px; opacity: .8; }
.ui-lane__more { font: var(--ed-small-12); color: var(--ed-muted); padding: 2px 0 0 2px; }
.ui-timeline--bars .ui-lane__now { z-index: 0; }
.ui-timeline__legend { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 10px; font: var(--ed-small-12); color: var(--ed-muted); }
.ui-timeline__legend .ui-lane__bar { position: static; display: inline-block; width: 14px; height: 10px; padding: 0; margin-right: 6px; vertical-align: -1px; border-radius: 3px; }
@media (max-width: 600px) { .ui-timeline--bars .ui-lane__name { width: 112px; flex-basis: 112px; } .ui-timeline--bars .ui-timeline__inner { min-width: calc(112px + var(--ui-tl-hours, 8) * 80px); } }

/* --- Sortable row --- */
.ui-srow { display: flex; align-items: center; gap: 10px; background: var(--ed-surface); border: 1px solid var(--ed-border); border-radius: 10px; padding: 9px 12px; }
.ui-srow.is-dragging-row { opacity: .55; }
.ui-srow.sort-drag-over { border-color: var(--ed-navy-600); box-shadow: 0 -2px 0 0 var(--ed-navy-600); }
.ui-srow__grip { cursor: grab; color: var(--ed-faint); font-size: 14px; }
.ui-srow__body { flex: 1; min-width: 0; font-size: 13.5px; color: var(--ed-ink); }
.ui-srow__updn { display: flex; gap: 4px; }
.ui-srow__updn button { width: 28px; height: 28px; border: 1px solid var(--ed-border); background: var(--ed-surface); border-radius: 8px; color: var(--ed-muted); font-size: 10px; cursor: pointer; }
.ui-srow__updn button:disabled { opacity: .35; cursor: default; }

/* --- Builder palette / canvas / dropzone --- */
.ui-bpal { border: 1px solid var(--ed-border); background: var(--ed-surface); border-radius: 9px; padding: 8px 11px; font: 600 12.5px/1.3 var(--ed-font); color: var(--ed-ink); cursor: grab; }
.ui-bblock { position: relative; border: 1px solid var(--ed-border); background: var(--ed-surface); border-radius: 10px; padding: 12px 14px; }
.ui-bblock__tools { position: absolute; top: 8px; right: 8px; display: none; gap: 4px; }
.ui-bblock:hover .ui-bblock__tools, .ui-bblock:focus-within .ui-bblock__tools { display: flex; }
.ui-bblock__tools button { width: 26px; height: 26px; border: 1px solid var(--ed-border); background: var(--ed-surface); border-radius: 7px; color: var(--ed-muted); font-size: 11px; cursor: pointer; }
.ui-bblock__title { font-size: 13px; color: var(--ed-ink); display: block; }
.ui-bblock__meta { font: var(--ed-small-12); color: var(--ed-muted); }
.ui-bdrop { border: 1.5px dashed var(--ed-setup-border); border-radius: 10px; padding: 14px; text-align: center; font: var(--ed-small-12); color: var(--ed-faint); background: var(--ed-sunken); }
.ui-bdrop.is-over { border-color: var(--ed-navy-600); color: var(--ed-navy-600); }

/* ============================================================================
   Responsive contracts (#4105) - every primitive owns its breakpoints; pages
   never write media queries for kit parts. Checkpoints: 375 / 768 / 1440.
   ========================================================================== */

/* A label only a phone needs (it has no hover title); the stacked listing shows it. */
.ui-listing__phone-label { display: none; }

/* Listing tables: a column marked data-priority="low" on its <th> and <td>s
   hides on phone - name, status and price stay; the rest waits for the row. */
@media (max-width: 767.98px) {
	.content .table [data-priority="low"],
	.ui-listing [data-priority="low"] { display: none; }

	/* .ui-listing--stack: a listing whose every column matters on a phone (the
	   delivery run: address, items and the buttons). Each row stacks into one
	   block instead of scrolling sideways. Cells marked .ui-listing__inline
	   share the first line; .ui-listing__actions becomes a row of buttons. */
	.ui-listing--stack .table-responsive { overflow-x: visible; }
	.ui-listing--stack .ui-listing__table,
	.ui-listing--stack .ui-listing__table > tbody { display: block; width: 100%; min-width: 0; }
	.ui-listing--stack .ui-listing__table > thead { display: none; }
	.ui-listing--stack .ui-listing__table > tbody > tr {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: 4px 8px;
		padding: 12px 0;
		border-bottom: 1px solid var(--ed-border);
	}
	.content .ui-listing--stack .ui-listing__table > tbody > tr > td {
		display: block;
		flex: 1 1 100%;
		width: auto;
		height: auto;
		min-width: 0;
		padding: 0;
		border: 0;
		text-align: left !important;
		white-space: normal !important;
		overflow-wrap: anywhere;
	}
	.content .ui-listing--stack .ui-listing__table > tbody > tr > td.ui-listing__inline { flex: 0 0 auto; }
	.content .ui-listing--stack .ui-listing__table > tbody > tr > td[data-priority="low"] { display: none; }
	.content .ui-listing--stack .ui-listing__table > tbody > tr > td.ui-listing__actions {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: 8px;
		padding-top: 6px;
	}
	.ui-listing--stack .ui-listing__actions .btn {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 6px;
		min-width: 44px;
		border: 1px solid var(--ed-border);
		border-radius: 10px;
	}
	.ui-listing--stack .ui-listing__phone-label { display: inline; }

	/* Drawers are full-screen sheets on phone (style.css leaves them at 50%
	   until 576px); footer actions go full-width, primary keeps its place. */
	.modal-sidebar { width: 100%; }
	.modal-sidebar .qb-footer-actions,
	.modal-sidebar .drawer-footer-actions { display: flex; width: 100%; }
	.modal-sidebar .qb-footer-actions > * { flex: 1; justify-content: center; }

	/* Centered modals: near-full-width with a 16px inset. */
	.delete-modal-main .modal-dialog { margin: 16px auto; max-width: calc(100vw - 32px); }

	/* Touch targets: every kit control hits 44px on phone. Every button too,
	   kit or Bootstrap, whatever its size class (#5533): a 33px New bot or
	   Turn it off is a miss under a thumb. Desktop keeps its own sizes. A
	   filter pill stays the height of the search box beside it. */
	.ui-stepper__btn { width: 40px; height: 40px; }
	.ui-srow__updn button { width: 40px; height: 40px; }
	.ui-btn:not([style*="height"]),
	.btn:not(.btn-link):not(.dropdown-toggle):not(.form-icon-button):not([style*="height"]) { min-height: 44px; }
	/* Joined to an input it keeps the input's height (rule 0029), at any
	   depth: a wrapper div inside the group must not stretch it. */
	.input-group .btn,
	.input-group .ui-btn { min-height: 0; }
	/* A button sized on purpose - a small square beside a field, the form
	   builder's icon buttons, one given its own height - keeps its size, so the
	   row around it does not move. An invisible 44px target is laid over it
	   instead, so a thumb still finds it. */
	.btn.form-icon-button:not(.dropdown-toggle),
	.btn[style*="height"]:not(.dropdown-toggle),
	.ui-btn[style*="height"] { position: relative; }
	.btn.form-icon-button:not(.dropdown-toggle)::after,
	.btn[style*="height"]:not(.dropdown-toggle)::after,
	.ui-btn[style*="height"]::after,
	.ui-ph .back-button::after,
	.ui-help__btn::after {
		content: "";
		position: absolute;
		top: 50%;
		left: 50%;
		width: max(100%, 44px);
		height: max(100%, 44px);
		transform: translate(-50%, -50%);
	}
	/* The header's back arrow and help glyph: the same invisible target, so
	   the header keeps its size on a phone. */
	.ui-ph .back-button,
	.ui-help__btn { position: relative; }
	.ui-btn--icon { min-width: 44px; }
	.ui-toggle-row { min-height: 44px; }
	/* A tap answer or follow-up chip is a button too: 36px on the bot draft's
	   "5:00pm / 6:00pm" answers and the run page's What next? (walk 2). */
	.ai-suggestion-chip { min-height: 44px; }
	/* Keep every tab reachable in one tap on a phone (#4719). */
	.ui-tabs { flex-wrap: nowrap; overflow-x: auto; max-width: 100%; min-width: 0; }
	.ui-tabs__item { flex: 0 0 auto; }
	.ui-tabs__link { min-height: 44px; white-space: nowrap; text-align: left; }
	.ui-chart-seg button { min-height: 40px; }

	/* Charts: full width, alternate axis labels drop so the row never wraps;
	   donut legend wraps below the ring. */
	.ui-chart__axis span:nth-child(even) { visibility: hidden; }
	.ui-donut-wrap { flex-direction: column; align-items: flex-start; }

	/* KPI stats stack full-width. */
	.ui-kpi .ui-stat { flex: 1 1 100%; }
}

/* ============================================================================
   Live widget preview card (#4093).
   ========================================================================== */
.ui-wprev-controls { margin-left: auto; }
.ui-wprev-body { display: flex; flex-direction: column; gap: 12px; }
.ui-wprev-frame {
	margin: 0 auto;
	border: 8px solid var(--ed-ink);
	border-radius: 22px;
	overflow: hidden;
	background: var(--ed-surface);
	box-shadow: var(--ed-shadow-card);
	width: 100%;
}
.ui-wprev-frame.is-phone { max-width: 300px; height: 520px; }
.ui-wprev-frame.is-tablet { max-width: 420px; height: 560px; border-radius: 16px; }
.ui-wprev-frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.ui-wprev-qr-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ui-wprev-qr-toggle {
	background: none;
	border: none;
	padding: 0;
	font: 600 12.5px/1.4 var(--ed-font);
	color: var(--ed-navy-600);
	cursor: pointer;
}
.ui-wprev-hint { font: var(--ed-small-12); color: var(--ed-muted); }
.ui-wprev-qr { display: flex; align-items: center; gap: 14px; }
.ui-wprev-qr img { border: 1px solid var(--ed-border); border-radius: 10px; }

/* ---- <x-ui.search-select> (#4271) - searchable single-select ---- */
.ui-search-select { position: relative; }
/* The trigger is a dropdown, so it looks like the other dropdown: same sunken
   fill, same type size and the same gray chevron as .ui-select. It was white
   with a blue chevron, so a picker sitting next to a plain select read as two
   different controls ("times dropdown color need updating to match rest",
   2026-09-11). Fixed here, never per page - rule 0018. */
.ui-search-select__trigger { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; height: 40px; padding: 0 34px 0 13px; background: var(--ed-sunken) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2395a2b6' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 12px center / 12px; border: 1px solid var(--ed-border); border-radius: var(--ed-r-ctl); font: 400 13.5px/1.4 var(--ed-font); color: var(--ed-ink); cursor: pointer; text-align: left; }
.ui-search-select__trigger:focus-visible { outline: 2px solid var(--ed-navy-600); outline-offset: -1px; }
/* The chevron is the background now, so the markup's <img> would be a second one. */
.ui-search-select__trigger img { display: none; }
/* Same drawer register as .ui-select above: a picker and a select sitting in
   the same sidebar column have to be the same height, or the column reads as
   broken. Scoped to the drawer, never to the instance. */
.modal-sidebar .ui-search-select__trigger { height: 48px; font-size: 16px; line-height: 22px; }
.ui-search-select__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-search-select__value.is-placeholder { color: var(--ed-faint); }
.ui-search-select__panel { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 1000; background: var(--ed-surface); border: 1px solid var(--ed-border); border-radius: 10px; box-shadow: var(--ed-shadow-pop); overflow: hidden; }
.ui-search-select__search { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--ed-border); color: var(--ed-faint); }
.ui-search-select__search input { border: none; outline: none; background: none; flex: 1; font: 400 13.5px/1.4 var(--ed-font); color: var(--ed-ink); }
.ui-search-select__list { max-height: 220px; overflow-y: auto; padding: 4px; }
.ui-search-select__option { display: block; width: 100%; text-align: left; padding: 9px 10px; border: none; background: none; border-radius: 7px; font: 400 13.5px/1.4 var(--ed-font); color: var(--ed-ink); cursor: pointer; }
.ui-search-select__option:hover { background: var(--ed-sunken); }
/* Keyboard highlight - where ArrowUp/ArrowDown have got to. The caret stays in the
   search box so the owner can keep typing, so this cannot be a :focus style; it is a
   class the picker moves. It reads stronger than :hover on purpose: when both are on
   screen the keyboard one is the one Enter will pick. */
.ui-search-select__option.is-active,
.ui-search-select__option:focus-visible { background: var(--ed-navy-100); color: var(--ed-navy-700); outline: 2px solid var(--ed-navy-600); outline-offset: -2px; }
.ui-search-select__empty { padding: 12px 10px; font: 400 13px/1.4 var(--ed-font); color: var(--ed-faint); font-style: italic; }

/* ============================================================================
   Dash inside the screens (#4335): the primitives every page reuses so Dash
   looks the same on a dashboard, a listing, a record and a settings page.
   Tokens only. Showcase: /design-system#dash.
   ========================================================================== */

/* ---- <x-ui.dash-line> ---- */
.ui-dash { display: flex; align-items: flex-start; gap: 10px; padding: 10px 14px; border: 1px solid var(--ed-border); border-radius: 10px; background: var(--ed-surface); font: 400 13px/1.45 var(--ed-font); color: var(--ed-ink); }
.ui-dash--ai { background: var(--ed-sunken); border-color: transparent; }
.ui-dash--warn { background: var(--ed-warning-soft); border-color: transparent; }
.ui-dash--ok { background: var(--ed-success-soft); border-color: transparent; }
/* SUPERSEDED by <x-ui.dash-mark>. The orb was a purple gradient square holding a
   glyph that is not the Dash mark, and it appeared on every listing, table header
   and dashboard row in the portal - the single biggest source of "everything is
   purple now" (learned rule 0025). No view uses it; kept only so a stale cached
   page does not render a naked glyph. Delete once the branch has shipped. */
.ui-dash__orb { display: inline-flex; align-items: center; justify-content: center; color: var(--ed-muted); font-size: 11px; }
.ui-dash__body { flex: 1; min-width: 0; }
.ui-dash__text b { font-weight: 600; }
.ui-dash__text a { display: inline; }
.ui-dash__actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }

/* ---- <x-ui.dash-attributes> ---- */
.ui-dash-attrs { display: flex; flex-direction: column; gap: 6px; }
.ui-dash-attrs__title { font: 600 12.5px/1.4 var(--ed-font); color: var(--ed-ink); margin-bottom: 2px; }
.ui-dash-attrs__row { display: grid; grid-template-columns: minmax(96px, 30%) 1fr; gap: 2px 12px; align-items: baseline; font: 400 13px/1.4 var(--ed-font); }
.ui-dash-attrs__label { color: var(--ed-muted); font-size: 12px; }
.ui-dash-attrs__mark { color: var(--ed-muted); margin-right: 4px; font-size: 10px; }
.ui-dash-attrs__value { color: var(--ed-ink); }
.ui-dash-attrs__source { grid-column: 2; color: var(--ed-faint); font-size: 11px; }
.ui-dash-attrs__empty { color: var(--ed-muted); font: 400 12.5px/1.4 var(--ed-font); }

/* ---- <x-ui.dash-recommends> ---- */
.ui-dash-rec__row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--ed-border); }
.ui-dash-rec__row:first-child { padding-top: 0; }
.ui-dash-rec__row:last-child { border-bottom: 0; padding-bottom: 0; }
.ui-dash-rec__main { flex: 1; min-width: 0; }
.ui-dash-rec__title { font: 600 13.5px/1.4 var(--ed-font); color: var(--ed-ink); }
.ui-dash-rec__text { font: 400 12.5px/1.45 var(--ed-font); color: var(--ed-muted); margin-top: 2px; }
.ui-dash-rec__actions { display: flex; gap: 6px; flex-shrink: 0; }
.ui-dash-rec__empty { color: var(--ed-muted); font: 400 12.5px/1.4 var(--ed-font); }

/* ---- <x-ui.dash-cell> + <x-ui.dash-th> (listing column) ---- */
.ui-dash-th { white-space: nowrap; }
.ui-dash-cell { display: inline-block; font: 600 11px/1.5 var(--ed-font); border-radius: 999px; padding: 2px 9px; white-space: nowrap; }
.ui-dash-cell--ai { background: var(--ed-sunken); color: var(--ed-ink); }
.ui-dash-cell--ok { background: var(--ed-success-soft); color: var(--ed-success); }
.ui-dash-cell--warn { background: var(--ed-warning-soft); color: var(--ed-warning); }
.ui-dash-cell--bad { background: var(--ed-danger-soft); color: var(--ed-danger); }
tr.ui-dash-row--needs-me > td { background: var(--ed-sunken); }

/* ---- <x-ui.closed-chip> (a date the shop is not trading) ---- */
.ui-closed-chip { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; background: var(--ed-closed-soft); color: var(--ed-closed); border-radius: 999px; padding: 2px 8px; font: 600 11px/1.5 var(--ed-font); text-decoration: none; }
a.ui-closed-chip:hover { background: var(--ed-closed-line); color: var(--ed-closed); text-decoration: none; }
.ui-closed-chip__dot { flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%; background: var(--ed-closed); }
.ui-closed-chip__text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-closed-chip--compact { padding: 3px; gap: 0; }

/* ---- <x-ui.check-chip> (confirm one line Dash wrote: Keep / Edit, issue 6106) ---- */
.ui-check-chip { display: inline-flex; align-items: center; gap: 8px; max-width: 100%; padding: 6px 6px 6px 12px; background: var(--ed-surface); border: 1px solid var(--ed-border); border-radius: 999px; box-shadow: var(--ed-shadow-pop); font: 500 13px/1.2 var(--ed-font); color: var(--ed-ink); white-space: nowrap; }
.ui-check-chip__dot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; background: var(--ed-warning); }
.ui-check-chip__text { overflow: hidden; text-overflow: ellipsis; }
.ui-check-chip__btn { flex: 0 0 auto; width: auto; margin: 0; padding: 4px 12px; border: 1px solid var(--ed-border); border-radius: 999px; background: var(--ed-surface); color: var(--ed-ink); font: 600 13px/1.2 var(--ed-font); cursor: pointer; pointer-events: auto; }
.ui-check-chip__btn:hover { border-color: var(--ed-navy-700); color: var(--ed-navy-700); }
.ui-check-chip__btn--keep { background: var(--ed-navy-700); border-color: var(--ed-navy-700); color: var(--ed-surface); }
.ui-check-chip__btn--keep:hover { background: var(--ed-navy-900); border-color: var(--ed-navy-900); color: var(--ed-surface); }
.ui-check-chip__btn:focus-visible { outline: 2px solid var(--ed-navy-600); outline-offset: 2px; }
@media (max-width: 480px) { .ui-check-chip { white-space: normal; flex-wrap: wrap; border-radius: 14px; } }

/* ---- <x-ui.needs-me-pill> ---- */
/* ---- <x-ui.dash-mark> ----
   The mark is the ONE thing on an ordinary page allowed to carry the accent, and
   it is the whole purple budget for that screen: the words, the pill, the row and
   the count around it are the page's own navy (learned rule 0025). Pass
   :accent="false" where the mark should take the color it sits in instead. */
.ui-dash-mark { color: var(--ed-ai); }
.ui-dash-mark--plain { color: inherit; }

/* ---- <x-ui.ask-dash-pill> ----
   Floats under highlighted text (public/js/eq-ask-dash.js places it). White on
   the page, the mark its only color, so it never reads as a second Dash surface. */
.ui-ask-dash-pill {
	position: fixed; z-index: var(--ed-z-tooltip);
	display: inline-flex; align-items: center; gap: 6px;
	width: auto; margin: 0; padding: 6px 12px;
	background: var(--ed-surface); color: var(--ed-ink);
	border: 1px solid var(--ed-border); border-radius: var(--ed-r-pill);
	box-shadow: var(--ed-shadow-pop);
	font: 500 13px/1 var(--ed-font); white-space: nowrap; cursor: pointer;
}
.ui-ask-dash-pill[hidden] { display: none; }
.ui-ask-dash-pill:hover { background: var(--ed-sunken); }
.ui-ask-dash-pill:focus-visible { outline: 2px solid var(--ed-navy-600); outline-offset: 2px; }
/* The showcase draws it in place rather than floating over the page. */
.ui-ask-dash-pill--static { position: static; }

.ui-needs-me { gap: 8px; }
/* On, this is still a filter: the page's own ink, like Filters beside it. */
.ui-needs-me--active { border-color: var(--dash); color: var(--dash); background: var(--ed-sunken); }
/* Work waiting is not an error, so the count is a plain number, never a red badge. */
.ui-needs-me__count { color: var(--ed-muted); font: 600 13px/1 var(--ed-font); }

/* ---- <x-ui.grouped-queue> ---- */
.ui-queue { border: 1px solid var(--ed-border); border-radius: 10px; background: var(--ed-surface); overflow: hidden; }
.ui-queue__head { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 8px 12px; background: var(--ed-sunken); border-bottom: 1px solid var(--ed-border); font: 400 12px/1.4 var(--ed-font); color: var(--ed-muted); }
.ui-queue__link { display: inline; font-weight: 600; color: var(--ed-navy-600); }
.ui-queue__row { display: flex; align-items: center; gap: 12px; padding: 9px 12px; border-bottom: 1px solid var(--ed-border); font: 400 12.5px/1.4 var(--ed-font); }
.ui-queue__row:last-child { border-bottom: 0; }
.ui-queue__main { flex: 1; min-width: 0; }
.ui-queue__title { font-weight: 600; color: var(--ed-ink); }
.ui-queue__text { color: var(--ed-muted); }
.ui-queue__actions { display: flex; gap: 6px; flex-shrink: 0; }
/* On a phone the two buttons left the words a one-word column ("a / pay /
   link / each"): the words take the row and the buttons sit under them. */
@media (max-width: 575.98px) {
	.ui-queue__row { flex-wrap: wrap; row-gap: 6px; }
	.ui-queue__main { flex: 1 0 100%; }
}

/* ---- <x-ui.chat-message> + <x-ui.chat-chip> (Ask Dash, docked panel) ---- */
.ai-message { display: flex; flex-direction: column; max-width: 100%; }
.ai-message-content { word-wrap: break-word; overflow-wrap: break-word; }
.ai-message-user { align-items: flex-end; margin: 6px 0; }
.ai-message-user .ai-message-content { background: var(--ed-navy-100); color: var(--ed-ink); border-radius: 18px; padding: 10px 16px; font-size: 15px; line-height: 1.5; max-width: 75%; }
.ai-message-text { white-space: pre-wrap; }
/* A rendered message brings its own line breaks and lists (#4625) - pre-wrap on
   top of them doubles every gap and breaks the bubble's width to min-content. */
.ai-message-text--rich { white-space: normal; }
.ai-message-text--rich > :first-child { margin-top: 0; }
.ai-message-text--rich > :last-child { margin-bottom: 0; }
.ai-message-text--rich ul, .ai-message-text--rich ol { margin: 4px 0; padding-left: 20px; }
.ai-message-text--rich li { margin: 2px 0; }
.ai-message-assistant { align-items: flex-start; }
.ai-message-assistant .ai-message-content { background: transparent; color: var(--ed-ink); padding: 0; font-size: 15.5px; line-height: 1.6; max-width: 100%; }
.ai-message-timestamp { display: none; }
.ai-suggested-prompts { display: flex; flex-wrap: wrap; gap: 8px; }
.ai-suggestion-chip { display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; background: var(--ed-surface); border: 1px solid var(--ed-border); border-radius: 999px; font: 500 13.5px/1.3 var(--ed-font); color: var(--ed-ink); cursor: pointer; text-align: left; max-width: 100%; transition: border-color var(--ed-fast), background var(--ed-fast); }
.ai-suggestion-chip:hover { background: var(--ed-sunken); border-color: var(--dash); color: var(--dash); }
/* The one glyph on a chip carries the accent, the label stays navy (#4630). */
.ai-suggestion-chip__icon { color: var(--ed-ai); font-style: normal; }

/* ---- <x-ui.sequence> (a job as a sequence: chat, Agents, builder) ---- */
.ui-seq { position: relative; }
.ui-seq__step { display: grid; grid-template-columns: 118px 26px 1fr; gap: 10px; align-items: start; padding: 12px 0; position: relative; }
.ui-seq--compact .ui-seq__step { grid-template-columns: 96px 26px 1fr; padding: 8px 0; }
.ui-seq__step::after { content: ""; position: absolute; left: calc(118px + 10px + 12px); top: 38px; bottom: -8px; width: 2px; background: var(--ed-border); }
.ui-seq--compact .ui-seq__step::after { left: calc(96px + 10px + 12px); }
.ui-seq__step--last::after { display: none; }
.ui-seq__step--sub { margin-left: 28px; }
.ui-seq__step--sub::after { left: calc(118px + 10px + 12px); }
.ui-seq--compact .ui-seq__step--sub::after { left: calc(96px + 10px + 12px); }
.ui-seq__when { font: 600 12px/1.4 var(--ed-font); color: var(--ed-muted); padding-top: 3px; text-align: right; }
.ui-seq--compact .ui-seq__when { font-size: 11px; }
.ui-seq__icon { width: 26px; height: 26px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 12px; z-index: 1; background: var(--ed-sunken); color: var(--ed-muted); }
.ui-seq__icon--text { background: var(--ed-navy-100); color: var(--ed-navy-700); }
.ui-seq__icon--email { background: var(--ed-warning-soft); color: var(--ed-warning); }
.ui-seq__icon--chat { background: var(--ed-sunken); color: var(--dash); }
.ui-seq__icon--charge { background: var(--ed-danger-soft); color: var(--ed-danger); }
.ui-seq__icon--check { background: var(--ed-success-soft); color: var(--ed-success); }
/* "Only if..." (#5676): a check made before the bot starts, so it reads like one. */
.ui-seq__icon--filter { background: var(--ed-success-soft); color: var(--ed-success); }
.ui-seq__body { min-width: 0; }
.ui-seq__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ui-seq__title { font: 600 13.5px/1.4 var(--ed-font); color: var(--ed-ink); }
.ui-seq--compact .ui-seq__title { font-size: 12.5px; }
.ui-seq__chip { display: inline-block; font: 700 10.5px/1.5 var(--ed-font); border-radius: 999px; padding: 2px 8px; white-space: nowrap; background: var(--ed-sunken); color: var(--ed-muted); }
.ui-seq__chip--text { background: var(--ed-navy-100); color: var(--ed-navy-700); }
.ui-seq__chip--email { background: var(--ed-warning-soft); color: var(--ed-warning); }
.ui-seq__chip--chat { background: var(--ed-sunken); color: var(--dash); }
.ui-seq__chip--charge { background: var(--ed-danger-soft); color: var(--ed-danger); }
.ui-seq__chip--check { background: var(--ed-success-soft); color: var(--ed-success); }
.ui-seq__chip--filter { background: var(--ed-success-soft); color: var(--ed-success); }
.ui-seq__chip--ask { background: var(--ed-warning-soft); color: var(--ed-warning); }
.ui-seq__chip--auto { background: var(--ed-success-soft); color: var(--ed-success); }
.ui-seq__msg { margin-top: 5px; font-size: 12.5px; color: var(--ed-ink); background: var(--ed-sunken); border: 1px solid var(--ed-border); border-radius: 8px; padding: 7px 10px; line-height: 1.45; white-space: pre-wrap; }
.ui-seq--compact .ui-seq__msg { font-size: 11.5px; }
.ui-seq__msg--empty { color: var(--ed-faint); font-style: italic; }
.ui-seq__note { margin-top: 4px; font-size: 11px; color: var(--ed-faint); }
/* The one tap on a step's note ("Send it on WhatsApp instead", #5555). */
.ui-seq__note-link { display: inline; padding: 0; background: none; border: 0; font: inherit; font-weight: 500; color: var(--ed-navy-700); cursor: pointer; }
.ui-seq__note-link:hover { text-decoration: underline; }
.ui-seq__branch { margin-left: calc(118px + 10px + 26px + 10px); font: 700 11.5px/1.4 var(--ed-font); color: var(--ed-muted); padding: 2px 0; }
.ui-seq--compact .ui-seq__branch { margin-left: calc(96px + 10px + 26px + 10px); }
@media (max-width: 575px) {
	.ui-seq__step, .ui-seq--compact .ui-seq__step { grid-template-columns: 26px 1fr; }
	.ui-seq__when { grid-column: 2; text-align: left; padding-top: 0; order: -1; }
	.ui-seq__step::after, .ui-seq--compact .ui-seq__step::after { left: 12px; }
	.ui-seq__branch, .ui-seq--compact .ui-seq__branch { margin-left: 36px; }
	.ui-seq__step--sub { margin-left: 14px; }
}
.ui-seq-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
.ui-seq-line__step { display: inline-flex; align-items: center; gap: 4px; }
.ui-seq-line__step .ui-seq__chip { display: inline-flex; align-items: center; gap: 4px; max-width: 220px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-seq-line__arrow { color: var(--ed-faint); font-size: 12px; }
.ui-seq-line__ask { font: 700 10px/1.4 var(--ed-font); color: var(--ed-warning); }
.ui-seq-line__more { font: 700 11px/1.4 var(--ed-font); color: var(--ed-muted); }

/* ---- <x-ui.ladder> (Ask me first / Just do it) ---- */
.ui-ladder { display: inline-flex; border: 1px solid var(--ed-border); border-radius: 999px; overflow: hidden; flex-shrink: 0; }
.ui-ladder__step { font: 700 11px/1.5 var(--ed-font); padding: 3px 10px; color: var(--ed-faint); background: transparent; border: 0; cursor: default; font-family: inherit; }
button.ui-ladder__step { cursor: pointer; }
button.ui-ladder__step:hover:not(.ui-ladder__step--on) { color: var(--ed-ink); }
.ui-ladder__step--on { background: var(--ed-ink); color: var(--ed-on-accent); }

/* ---- <x-ui.gates> (stage gates on a job being built) ---- */
.ui-gates { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.ui-gates__step { display: inline-flex; align-items: center; gap: 5px; font: 700 11.5px/1.5 var(--ed-font); border-radius: 999px; padding: 3px 10px; background: var(--ed-sunken); color: var(--ed-faint); }
.ui-gates__step--done { background: var(--ed-success-soft); color: var(--ed-success); }
.ui-gates__step--current { background: var(--ed-navy-600); color: var(--ed-on-accent); }
.ui-gates__sep { color: var(--ed-border); }

/* ---- Turn trace (<x-ui.turn-trace>, #4402): what Dash did on the way to an answer ---- */
.ui-trace { display: flex; flex-direction: column; gap: 2px; margin: 8px 0; }
/* Consecutive containers read as one list. A turn opens a new one whenever prose
   or a card breaks the run, and stacking their margins is what made the gaps
   between tool rows grow mid-turn, then snap back on reload (learned rule 0026). */
.ui-trace + .ui-trace { margin-top: 0; }
.ui-trace__thought { display: flex; flex-direction: column; }
.ui-trace__row { display: flex; align-items: center; gap: 10px; padding: 4px 0; font: 500 13.5px/1.4 var(--ed-font); color: var(--ed-muted); background: none; border: 0; text-align: left; }
button.ui-trace__row { cursor: pointer; }
button.ui-trace__row:hover .ui-trace__label { color: var(--ed-ink); }
.ui-trace__icon { width: 18px; height: 18px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 9px; flex-shrink: 0; border: 1.5px solid var(--ed-border); color: var(--ed-muted); }
.ui-trace__icon--done { border-color: var(--ed-success); color: var(--ed-success); }
.ui-trace__icon--running { border-color: var(--ed-ai-soft); color: var(--ed-ai); }
.ui-trace__icon--failed { border-color: var(--ed-warning); color: var(--ed-warning); }
.ui-trace__icon--thought { border: 0; color: var(--ed-ai); font-size: 12px; }
/* A change Dash staged on an approval card: nothing has happened yet, so no tick
   (#6087). Quiet rather than amber, because amber is the failed state. */
.ui-trace__icon--waiting { border: 1.5px dashed var(--ed-muted); color: var(--ed-muted); font-size: 7px; }
.ui-trace__row--running .ui-trace__label { color: var(--ed-ai); }
.ui-trace__row--waiting .ui-trace__label { color: var(--ed-ink); }
.ui-trace__note { font-size: 12.5px; font-weight: 500; color: var(--ed-muted); }
.ui-trace__chev { font-size: 10px; color: var(--ed-faint); transition: transform 0.15s ease; }
button[aria-expanded="true"] .ui-trace__chev { transform: rotate(90deg); }
.ui-trace__body { margin: 2px 0 6px 8px; padding: 2px 0 2px 14px; border-left: 2px solid var(--ed-border); font-size: 13.5px; line-height: 1.55; color: var(--ed-muted); }
.ui-trace__body p { margin: 0 0 6px; }
.ui-trace__body p:last-child { margin-bottom: 0; }
.ui-trace__body a { display: inline; color: var(--ed-ai); }

/* ---- The dashboard's Dash line ----
   One row, the height of a line of text: the mark, what needs the owner, and the
   way in. The cards it used to stack here live on the page that owns the work. */
.ud-needs-line { padding: 14px 18px; }
.ud-needs-headline { display: flex; align-items: center; gap: 12px; }
.ud-needs-headline > *:first-child { flex: 1 1 auto; min-width: 0; }
.ud-needs-open { display: inline; flex: 0 0 auto; font: 600 14px/1 var(--ed-font); color: var(--dash); }
.ud-needs-open:hover { text-decoration: underline; }

/* ---- <x-ui.bot-mark> - which bot this is, at a glance ----
   The initials square that identifies a bot on its row and in the Dash card's
   strip. The color comes from the bot's id, so it never means "healthy" or
   "broken"; status lives in the chip beside the name. */
.ui-botmark { position: relative; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: var(--ui-botmark-size, 28px); height: var(--ui-botmark-size, 28px); border-radius: 8px; background: var(--ed-mark-1); color: var(--ed-on-accent); font-family: var(--ed-font); font-weight: 700; font-size: calc(var(--ui-botmark-size, 28px) * 0.4); line-height: 1; letter-spacing: 0.02em; }
.ui-botmark--1 { background: var(--ed-mark-1); }
.ui-botmark--2 { background: var(--ed-mark-2); }
.ui-botmark--3 { background: var(--ed-mark-3); }
.ui-botmark--4 { background: var(--ed-mark-4); }
.ui-botmark--5 { background: var(--ed-mark-5); }
.ui-botmark--6 { background: var(--ed-mark-6); }
.ui-botmark__on { position: absolute; right: -2px; bottom: -2px; width: 8px; height: 8px; border-radius: 50%; background: var(--ed-success); border: 1.5px solid var(--ed-surface); }

/* ---- <x-ui.page-header> + <x-ui.help-menu> ----
   The header row every supplier page carries: title, the page's own buttons,
   Search, help. Keeps the legacy .title chrome (white bar, 80px, hairline) and
   only owns what sits inside it.

   What gives when the row runs out of room, in order: the title truncates, then
   the search shrinks to its 180px floor, and the page's buttons never wrap or
   move. Below 992px the search collapses to a magnifier that expands over the
   row, and the help glyph stays where it always is. */
.ui-ph {
	flex-wrap: nowrap;
	/* The row measures itself: a docked Dash panel narrows it on any screen. */
	container: ph / inline-size;
}

/* Title. Shrinks four times faster than the search, so it is the first thing to
   give, and truncates rather than wrapping the 80px bar to two lines. */
.ui-ph__lead {
	flex: 0 4 auto;
	min-width: 0;
	max-width: 34%;
}

/* No search box on the row (:search="false"): the 34% it was holding goes
   to the title, so a page name is never cut short beside empty space. */
.ui-ph:not(:has(.ui-ph__search)) .ui-ph__lead {
	flex: 1 1 auto;
	max-width: none;
}

.ui-ph__title {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* A page opened from another one carries a back arrow before the title and a
   status pill after it. Neither gives up a pixel: the title is what truncates,
   so the pill stays readable and the way back stays tappable. */
.ui-ph__lead-slot,
.ui-ph__meta {
	display: flex;
	align-items: center;
	flex: 0 0 auto;
}

/* A meta line too long to sit beside the title (a delivery summary, a schedule)
   goes under it instead - :meta-below="true". Beside it, the pill keeps every
   pixel and the title is clipped to a letter; stacked, both read. */
.ui-ph__stack {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	justify-content: center;
	min-width: 0;
	max-width: 100%;
	gap: 2px;
}

/* Stacked, the title is a flex-start child and took its full text width, so a
   long bot name ran under the help glyph on a phone (#5533, rule 0046). It
   truncates at the column's edge like the one-line title does. */
.ui-ph__stack > .ui-ph__title {
	max-width: 100%;
}

.ui-ph__lead--stacked {
	max-width: 46%;
}

.ui-ph__meta--below {
	max-width: 100%;
	min-width: 0;
}

/* :wrap="true" - the title never truncates. The row grows from its 80px
   resting height and the search and buttons drop to their own line when the
   name needs the room; on a phone a long name wraps within the row. Mirrors
   the booking header's min-height fix (booking-detail.css, rule 0046). */
.content .title.ui-ph--wrap {
	flex-wrap: wrap;
	height: auto;
	min-height: 80px;
	row-gap: var(--ed-s2);
}

.ui-ph--wrap .ui-ph__lead,
.ui-ph--wrap .ui-ph__lead--stacked {
	flex: 1 0 auto;
	max-width: 100%;
}

.ui-ph--wrap .ui-ph__title {
	white-space: normal;
	overflow: visible;
	overflow-wrap: anywhere;
}

/* Search: 300px, shrinking to 180px before the page's buttons give anything. */
.ui-ph__search {
	flex: 0 1 300px;
	min-width: 180px;
}

.ui-ph__searchbox,
.ui-ph__search .global-search-container {
	width: 100%;
	max-width: 300px;
}

/* Match the 40px of the buttons beside it - one row, one height. */
.ui-ph__search .global-search-input .form-control {
	height: 40px;
	line-height: 40px;
}

/* The icon is centered on the input, not on the wrapper, so the 44px offset the
   AI search bar pins it at lands 2px low in a 40px box. */
.ui-ph__search .global-search-input.has-ai .global-search-icon {
	top: 50%;
}

/* Navy focus, not the AI purple: the header search is search, and the Dash card
   under it is where Dash speaks. */
.ui-ph__search .global-search-input.has-ai .form-control:focus {
	border-color: var(--ed-navy-600);
	box-shadow: 0 0 0 3px var(--ed-navy-100);
}

/* The magnifier that stands in for the search below 992px. */
.ui-ph__searchbtn {
	display: none;
	width: 32px;
	height: 32px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: var(--ed-faint);
	font-size: 15px;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	transition: background var(--ed-fast), color var(--ed-fast);
}

.ui-ph__searchbtn:hover,
.ui-ph__searchbtn:focus-visible {
	background: var(--ed-sunken);
	color: var(--ed-navy-700);
}

/* The page's own buttons hold the right edge and never wrap. */
.ui-ph__actions {
	flex: 0 0 auto;
	margin-left: auto;
	flex-wrap: nowrap;
	white-space: nowrap;
}

@media (max-width: 991.98px) {
	/* The row is tight here, and the title is the only thing on it that can
	   give, so stop capping it at a third and let it use what is left. */
	.ui-ph__lead {
		max-width: none;
	}

	.ui-ph__search {
		flex: 0 0 auto;
		min-width: 0;
	}

	.ui-ph__searchbtn {
		display: inline-flex;
	}

	.ui-ph__searchbox {
		display: none;
	}

	/* Expanded, it takes the row rather than squeezing the buttons off it.
	   .title is already position: relative, so this anchors to the bar. */
	.ui-ph__search.is-open .ui-ph__searchbox {
		display: block;
		position: absolute;
		left: var(--ed-s4);
		right: var(--ed-s4);
		top: 50%;
		transform: translateY(-50%);
		max-width: none;
		z-index: 12;
	}

	.ui-ph__search.is-open .global-search-container {
		max-width: none;
	}
}

/* The same collapse when the ROW is narrow on a wide screen (Dash docked
   beside the page), so the page name is never squeezed to a letter. */
@container ph (max-width: 960px) {
	.ui-ph__lead { max-width: none; }
	.ui-ph__search { flex: 0 0 auto; min-width: 0; }
	.ui-ph__searchbtn { display: inline-flex; }
	.ui-ph__searchbox { display: none; }
	.ui-ph__search.is-open .ui-ph__searchbox {
		display: block;
		position: absolute;
		left: var(--ed-s4);
		right: var(--ed-s4);
		top: 50%;
		transform: translateY(-50%);
		max-width: none;
		z-index: 12;
	}
	.ui-ph__search.is-open .global-search-container { max-width: none; }
}

/* On a phone the row carries the menu, the page title, the page's own button
   and the help glyph, and that is already all of it. The magnifier is what
   gives - a title clipped to one letter is what the owner would notice, and
   search is still one tap away in the nav. */
@media (max-width: 575.98px) {
	.ui-ph__search {
		display: none;
	}

	/* The record's id pill is reference, the page name is the point: on a
	   phone the id gives way so the name is not cut to two letters (rule
	   0046). It is still on the record's own page body and in its URL. */
	.ui-ph__meta:has(> [class$="-id-pill"]:only-child) {
		display: none !important; /* the meta carries Bootstrap's d-flex */
	}

	/* Buy the title its letters back. gap-3 is a Bootstrap utility, so only
	   !important reaches it; the row's own padding stays put so the header
	   still lines up with the page body under it. */
	.ui-ph {
		gap: var(--ed-s2) !important;
	}

	.ui-ph__actions {
		max-width: 100%;
		flex-wrap: wrap;
		justify-content: flex-end;
	}

	.ui-help__btn {
		width: 28px;
		height: 28px;
		font-size: 16px;
	}

	.ui-help__glyph {
		width: 17px;
		height: 17px;
	}

	/* A page's own button keeps its size, and the name keeps its letters:
	   when both cannot share the row, the buttons take a row of their own
	   rather than squeezing the title to "Tomorr..." with its status pill
	   under the button (rule 0046, the bot run page on a phone). The row
	   grows to fit, so nothing falls out of the bar. */
	.ui-ph:has(.ui-ph__actions > :not(.ui-help):not(.d-none)) {
		flex-wrap: wrap;
		height: auto;
		min-height: 80px;
		row-gap: var(--ed-s2);
	}

	/* The lead asks for its whole width (name, pill, back arrow) and only
	   then truncates, on a row it has to itself. */
	.ui-ph:has(.ui-ph__actions > :not(.ui-help):not(.d-none)) .ui-ph__lead {
		flex: 1 0 auto;
		max-width: 100%;
	}
}

/* ---- Contextual Help beside the page or section title ---- */
.ui-help {
	position: relative;
	flex: 0 0 auto;
	/* A touch more air than the gap between the page's own buttons, so the
	   glyph reads as chrome rather than as one more page action. */
	margin-left: var(--ed-s1);
}

.ui-help__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: var(--ed-faint);
	font-size: 18px;
	line-height: 1;
	cursor: pointer;
	transition: background var(--ed-fast), color var(--ed-fast);
}

.ui-help__btn:hover,
.ui-help__btn:focus-visible {
	background: var(--ed-sunken);
	color: var(--ed-navy-700);
}

/* Popper positions the whole bubble; the legacy inner translation would shift it off screen. */
.ui-help-tooltip .tooltip-inner {
	transform: none;
}

.ui-help__menu,
.ui-action-menu__menu {
	position: absolute;
	top: calc(100% + 8px);
	right: 0;
	z-index: var(--ed-z-drawer);
	min-width: 180px;
	display: flex;
	flex-direction: column;
	padding: 6px;
	background: var(--ed-surface);
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-ctl);
	box-shadow: var(--ed-shadow-pop);
}

.ui-help__item,
.ui-action-menu__item {
	display: block;
	width: 100%;
	padding: 8px 12px;
	border: 0;
	border-radius: 6px;
	background: none;
	color: var(--ed-ink);
	font: 500 13.5px/1.4 var(--ed-font);
	text-align: left;
	text-decoration: none;
	white-space: nowrap;
	cursor: pointer;
}

.ui-help__item:hover,
.ui-help__item:focus-visible,
.ui-action-menu__item:hover,
.ui-action-menu__item:focus-visible {
	background: var(--ed-sunken);
	color: var(--ed-ink);
	text-decoration: none;
}

/* ---- <x-ui.action-menu> ---- */
.ui-action-menu { position: relative; display: inline-flex; flex-shrink: 0; }
.ui-action-menu__trigger { min-width: 44px; justify-content: center; }
.ui-action-menu__menu { max-width: calc(100vw - var(--ed-s6)); }
.ui-action-menu__item { display: flex; align-items: center; min-height: 44px; }

/* ---- <x-ui.bots-strip> ----
   The shop's bots on the right of the Dash card's suggestion row. The square
   itself is <x-ui.bot-mark> (.ui-botmark above) - this is only the row that
   holds them, the overflow count and the always-there dashed "+". Nothing here
   is purple: the Dash mark at the top of the card is the page's whole accent
   budget. */
.ui-bots {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	flex-shrink: 0;
}

.ui-bots__label {
	font: 500 12px/1.3 var(--ed-font);
	color: var(--ed-faint);
	margin-right: 2px;
	white-space: nowrap;
}

.ui-bots__btn {
	display: inline-flex;
	padding: 0;
	border: none;
	background: none;
	line-height: 0;
	cursor: pointer;
	border-radius: 8px;
	transition: transform var(--ed-fast), box-shadow var(--ed-fast);
}

.ui-bots__btn:hover,
.ui-bots__btn:focus-visible {
	transform: translateY(-1px);
	box-shadow: var(--ed-shadow-card);
}

/* The overflow count: a quiet square, never one of the six identity colors -
   it stands for several bots, not for one. */
.ui-bots__more {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	flex: 0 0 24px;
	border: 1px solid var(--ed-border);
	border-radius: 8px;
	background: var(--ed-sunken);
	color: var(--ed-muted);
	font: 700 10px/1 var(--ed-font);
	text-decoration: none;
}

.ui-bots__more:hover,
.ui-bots__more:focus-visible {
	color: var(--ed-navy-700);
	text-decoration: none;
}

.ui-bots__add {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	flex: 0 0 24px;
	border: 1px dashed var(--ed-setup-border);
	border-radius: 8px;
	background: none;
	color: var(--ed-faint);
	font-size: 10px;
	line-height: 1;
	text-decoration: none;
	cursor: pointer;
	transition: border-color var(--ed-fast), color var(--ed-fast);
}

.ui-bots__add i {
	font-size: 10px;
	line-height: 1;
}

.ui-bots__add:hover,
.ui-bots__add:focus-visible {
	border-color: var(--ed-navy-700);
	color: var(--ed-navy-700);
	text-decoration: none;
}

@media (max-width: 575.98px) {
	.ui-bots__label {
		display: none;
	}
}


/* Bot marks are a tap target on a phone, not a 24px dot. Last in the file so it
   beats the base sizes above. */
@media (max-width: 767px) {
	.ui-bots__btn,
	.ui-bots__add {
		width: 34px;
		height: 34px;
		/* A flex row would otherwise squeeze it back under a thumb's width. */
		flex: 0 0 auto;
	}
}

/* ── <x-ui.choice-select>: the row-level chooser (issue 4542) ─────────────────
   Self-contained on purpose. The first version borrowed its height and padding
   from `.set-section-body .form-control-category`, so it was 40px in Settings and
   something else everywhere else - including its own /design-system demo. The
   namespace is `.ui-chooser`, NOT `.ui-choice`: that one belongs to
   <x-ui.choice-cards> further up this file. */
.ui-chooser {
	position: relative;
}

.ui-chooser__trigger {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	width: 100%;
	height: 40px;
	padding: 0 12px;
	text-align: left;
	background: var(--ed-sunken);
	border: 1px solid var(--ed-border);
	border-radius: 10px;
	cursor: pointer;
	font-size: 16px;
	line-height: 22px;
	color: var(--ed-ink);
}

.ui-chooser__trigger:focus-visible,
.ui-chooser__option:focus-visible,
/* Plain :focus too, for the options. Focus is moved there by script when the
   list opens, and a scripted .focus() does not reliably satisfy
   :focus-visible - so the ring was invisible for exactly the keyboard user
   this component exists for. Options are only reachable while the list is
   open, so there is no stray ring to worry about. */
.ui-chooser__option:focus {
	outline: 2px solid var(--ed-navy-600);
	outline-offset: 2px;
}

/* ---- <x-ui.orb> (Dash, on a Dash surface) ----
   A row, not a shape: the app's own Dash hexagon with its name beside it. The
   painted squircle this replaces carried no glyph and no words, so the owner's
   first reaction to it was "wtf is this purple box?" - the mark now comes from
   <x-global-search.dash-icon-mini>, which is what every other Dash surface
   shows, and this file only says how the mark and the name sit together.
   Working motion lives in public/css/first-run-motion.css (.fm-orb). */
.ui-orb {
	display: inline-flex;
	align-items: center;
	flex: 0 0 auto;
	min-width: 0;
	gap: 9px;
}

/* The working ring sits 7px outside the mark, so at the resting gap the name
   ends up almost touching it. Give it back the room while it runs. */
.ui-orb.is-thinking,
[data-fm-state="thinking"] .ui-orb { gap: 16px; }

.ui-orb__mark {
	position: relative;
	flex: 0 0 auto;
	display: inline-flex;
	line-height: 0;
}

.ui-orb__mark svg { display: block; }

/* The name. Quiet on purpose: it names the voice, it is not a heading, and the
   question under it is the thing being read. */
.ui-orb__name {
	font: 500 12.5px/1 var(--ed-font);
	letter-spacing: .01em;
	color: var(--ed-muted);
	white-space: nowrap;
}

/* Which of the two names is on. The class alone decides, so a surface can
   swap them with wire:loading.class and no second binding. Both are one line
   tall, so the swap cannot change the height of anything. */
.ui-orb__name--busy { display: none; }
.ui-orb.is-thinking .ui-orb__name--rest { display: none; }
.ui-orb.is-thinking .ui-orb__name--busy { display: inline; }

.ui-orb--28 .ui-orb__name { font-size: 12px; }

/* The glyph's own box. It exists so the breathe and the blink can run at the
   same time without sharing a transform: the box scales, the mark inside it
   closes. Both animations are in public/css/first-run-motion.css. */
.ui-orb__glyph {
	display: inline-flex;
	line-height: 0;
	transform-origin: 50% 50%;
}

/* ---- .ui-composer (the one place an owner answers) ----
   A pill on a card: 56px tall, one line, a send button on the right. Two
   states and both are free - no JavaScript, no Livewire property, no commit.

     empty    :placeholder-shown - hairline border, ghost send button, and the
              placeholder itself shimmers, so an idle field still looks live
     filled   the instant there is text - brand ring and a solid send button.
              The field tells you it is ready before you look at the button.

   Markup:

     <form class="ui-composer" wire:submit="send">
         <input class="ui-composer__input" type="text" placeholder="Your answer"
                wire:model="draft">
         <span class="ui-composer__ghost" aria-hidden="true">Your answer</span>
         <button class="ui-composer__send" type="submit" aria-label="Send">…</button>
     </form>

   Order is load-bearing: the send button must come AFTER the input, because
   the filled state reaches it as a sibling. The ghost is the shimmer - it
   repeats the placeholder because ::placeholder cannot carry a clipped
   background in every engine. Leave the ghost out and you get a normal
   placeholder and no shimmer, which is a fine composer, just a quieter one.
   Keep the placeholder attribute either way: it is what :placeholder-shown
   reads, and it is what a screen reader announces.

   .is-filled forces the filled state where the value does not live in the
   input (a chip already chosen, a value pasted in by the surface).
   .is-working sweeps the shell - use it with wire:loading.class. */
.ui-composer {
	position: relative;
	display: flex;
	align-items: center;
	gap: var(--ed-s2);
	width: 100%;
	max-width: var(--ui-composer-w, 560px);
	min-height: 56px;
	padding: 8px 8px 8px 20px;
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-pill);
	background: var(--ed-surface);
	box-shadow: var(--ed-shadow-card);
	transition: border-color 180ms ease, box-shadow 180ms ease;
}

.ui-composer__input {
	flex: 1 1 auto;
	min-width: 0;
	border: 0;
	padding: 0;
	background: transparent;
	font: var(--ed-body-16);
	color: var(--ed-ink);
	outline: none;
}

.ui-composer__input::placeholder { color: var(--ed-faint); }

/* ---- the shimmering placeholder ---- */

.ui-composer__ghost {
	position: absolute;
	left: 20px;
	top: 50%;
	transform: translateY(-50%);
	max-width: calc(100% - 76px);
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
	font: var(--ed-body-16);
	pointer-events: none;
	opacity: 0;
	background-image: linear-gradient(100deg,
		var(--ed-faint) 0%,
		var(--ed-faint) 38%,
		var(--ed-muted) 50%,
		var(--ed-faint) 62%,
		var(--ed-faint) 100%);
	background-size: 320% 100%;
	/* repeat, NOT no-repeat. The gradient is 3.2x the box and it sweeps from
	   140% to -40%, so with no-repeat most of the letters have no paint at all
	   for most of the cycle - and background-clip: text means no paint is no
	   letter. On a full-width composer "Or tell me what to change" rendered as
	   "e what to change". Both ends of the ramp are the same color, so tiling
	   is seamless. */
	background-repeat: repeat;
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	animation: ui-composer-ghost 2s ease-in-out infinite;
}

@keyframes ui-composer-ghost {
	from { background-position: 140% 0; }
	to   { background-position: -40% 0; }
}

/* :has() gets its own rules on purpose. A selector list is thrown away whole
   by an engine that cannot parse one member of it, so pairing :has() with the
   .is-filled fallback in one list would take the fallback down with it. */
.ui-composer:has(.ui-composer__input:placeholder-shown) .ui-composer__ghost { opacity: 1; }
.ui-composer:has(.ui-composer__ghost) .ui-composer__input::placeholder { color: transparent; }

/* ---- filled: the field says it is ready ---- */

.ui-composer:focus-within {
	border-color: var(--ed-navy-600);
	box-shadow: 0 0 0 3px var(--ed-navy-100), var(--ed-shadow-card);
}

.ui-composer:has(.ui-composer__input:not(:placeholder-shown)) {
	border-color: var(--ed-navy-700);
	box-shadow: 0 0 0 3px var(--ed-navy-100), var(--ed-shadow-card);
}

.ui-composer.is-filled {
	border-color: var(--ed-navy-700);
	box-shadow: 0 0 0 3px var(--ed-navy-100), var(--ed-shadow-card);
}

/* ---- the send button ---- */

.ui-composer__send {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: var(--ed-sunken);
	color: var(--ed-faint);
	cursor: pointer;
	transition: background 180ms ease, color 180ms ease, transform 110ms var(--fm-ease, ease);
}

.ui-composer__send svg { display: block; }
.ui-composer__send:active { transform: scale(.94); }
.ui-composer__send[disabled] { cursor: default; opacity: .7; }

.ui-composer__input:not(:placeholder-shown) ~ .ui-composer__send,
.ui-composer.is-filled .ui-composer__send {
	background: var(--ed-navy-700);
	color: var(--ed-on-accent);
}

.ui-composer__input:not(:placeholder-shown) ~ .ui-composer__send:hover,
.ui-composer.is-filled .ui-composer__send:hover { background: var(--ed-navy-600); }

/* ---- working ---- */

.ui-composer.is-working {
	background-image: linear-gradient(100deg,
		var(--ed-surface) 0%,
		var(--ed-surface) 40%,
		var(--ed-navy-100) 50%,
		var(--ed-surface) 60%,
		var(--ed-surface) 100%);
	background-size: 260% 100%;
	animation: ui-composer-work 1.4s linear infinite;
}

@keyframes ui-composer-work {
	from { background-position: 130% 0; }
	to   { background-position: -30% 0; }
}

@media (prefers-reduced-motion: reduce) {
	.ui-composer__ghost,
	.ui-composer.is-working {
		animation: none !important;
	}

	/* The finished state of each, painted rather than animated to: the
	   placeholder is a placeholder, and a working composer says so with its
	   ring rather than with a sweep. */
	.ui-composer__ghost {
		background-image: none;
		color: var(--ed-faint);
	}

	.ui-composer.is-working {
		background-image: none;
		border-color: var(--ed-navy-600);
	}
}

/* ---- <x-ui.receipt-row> (a fact already settled, still undoable) ---- */
.ui-receipt {
	display: flex;
	align-items: center;
	gap: var(--ed-s2);
	min-width: 0;
	font: 400 13.5px/1.35 var(--ed-font);
	color: var(--ed-muted);
}

.ui-receipt__tick {
	flex: 0 0 auto;
	width: 14px;
	height: 14px;
	color: var(--ed-success);
	line-height: 0;
}

.ui-receipt__tick svg { display: block; }

/* A fact that did NOT happen. Not danger red - nothing is broken and nothing
   needs fixing, we just did not get it; red here would read as an error the
   owner has to act on. Quiet, hollow, and visibly not a tick. */
.ui-receipt--miss .ui-receipt__tick { color: var(--ed-faint); }

/* Still happening. The brand's own ring, so it reads as ours and as open. */
.ui-receipt--now .ui-receipt__tick { color: var(--ed-navy-600); }

.ui-receipt__text {
	flex: 1 1 auto;
	min-width: 0;
	overflow-wrap: anywhere;
}

/* The fact itself carries the weight; the sentence around it stays quiet. */
.ui-receipt__text b,
.ui-receipt__text strong {
	color: var(--ed-ink);
	font-weight: 600;
}

/* style.css sets a { display: block } app-wide, so a bare link here would
   break onto its own line. */
.ui-receipt__act {
	display: inline-flex;
	align-items: center;
	flex: 0 0 auto;
	margin-left: auto;
	padding: 0;
	background: none;
	border: 0;
	border-bottom: 1px solid var(--ed-navy-200);
	font: 400 12.5px/1.35 var(--ed-font);
	color: var(--ed-navy-600);
	text-decoration: none;
	cursor: pointer;
}

.ui-receipt__act:hover {
	color: var(--ed-navy-700);
	border-bottom-color: var(--ed-navy-600);
}

.ui-receipt__act:focus-visible {
	outline: 2px solid var(--ed-navy-600);
	outline-offset: 3px;
	border-radius: 3px;
}

/* Older receipts recede so only the live turn is at full strength. Opacity
   ONLY: anything here that changed a height would move the page under the
   owner every time they answer.

   The floors were .60 and .40. A receipt is a FACT the owner may want to read
   back and a control they may want to press, and at .40 on white the oldest
   line and its Change link were nearly gone - the stack stopped being a record
   and started looking like it had failed to load. Still clearly behind the
   live line, still legible. */
.ui-receipt--age1 { opacity: .78; }
.ui-receipt--age2 { opacity: .62; }

/* ---- <x-ui.thread-card> (a draft to approve, or the thing that landed) ----
   One card, two registers. The flip from draft to live happens IN PLACE: the
   label crossfades purple to green and the border warms. Never a second card
   and never a well-done screen. */
.ui-tcard {
	background: var(--ed-surface);
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-card);
	box-shadow: var(--ed-shadow-thread);
	overflow: hidden;
	transition: border-color 320ms ease, box-shadow 320ms ease;
}

/* A draft is the thing on the table, and it has to look like it. A 1px grey
   box on a white page carried no more weight than the sentences around it, so
   when a card arrived the eye stayed on the composer at the bottom and the
   owner never saw that there was something to approve. It sits up off the page
   while it is undecided, and settles flat once it is real. */
.ui-tcard--draft {
	box-shadow: var(--ed-shadow-pop);
}

.ui-tcard--live {
	border-color: var(--ed-success-border);
	box-shadow: var(--ed-shadow-thread);
}

.ui-tcard__head { padding: 15px 18px 0; }

.ui-tcard__label {
	display: flex;
	align-items: center;
	gap: 7px;
	font: 600 10.5px/1 var(--ed-font);
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--ed-navy-600);
	transition: color 320ms ease;
}

.ui-tcard--live .ui-tcard__label { color: var(--ed-success); }
.ui-tcard--note .ui-tcard__label { color: var(--ed-muted); }

.ui-tcard__title {
	margin: 9px 0 0;
	font: 600 17px/1.35 var(--ed-font);
	color: var(--ed-ink);
}

.ui-tcard__body { padding: 14px 18px 4px; }

.ui-tcard__row {
	margin: 0;
	display: flex;
	align-items: baseline;
	gap: var(--ed-s3);
	padding: 11px 0;
}

.ui-tcard__row + .ui-tcard__row { border-top: 1px solid var(--ed-border); }

.ui-tcard__row dt {
	flex: 0 0 140px;
	margin: 0;
	font: 400 13.5px/1.4 var(--ed-font);
	color: var(--ed-muted);
}

.ui-tcard__row dd {
	margin: 0;
	flex: 1 1 auto;
	min-width: 0;
	display: flex;
	align-items: baseline;
	gap: 9px;
	font: 600 14.5px/1.4 var(--ed-font);
	color: var(--ed-ink);
	overflow-wrap: anywhere;
}

.ui-tcard__was {
	font-weight: 400;
	color: var(--ed-faint);
	text-decoration: line-through;
}

.ui-tcard__arrow {
	font-weight: 400;
	color: var(--ed-faint);
}

/* The ONE value nobody knows yet. Its own keyframe, so a surface that takes
   the kit without the motion layer still gets a bar rather than nothing. */
.ui-tcard__pending {
	display: inline-block;
	width: var(--ui-pending-w, 120px);
	height: 11px;
	max-width: 100%;
	border-radius: var(--ed-r-ctl);
	background: linear-gradient(90deg, var(--ed-border) 0 25%, var(--ed-sunken) 50%, var(--ed-border) 75%);
	background-size: 260px 100%;
	animation: ui-shim 1.25s linear infinite;
}

@keyframes ui-shim {
	from { background-position: -130px 0; }
	to   { background-position: 130px 0; }
}

/* The decision. It arrives whole - see the settle contract in
   public/css/first-run-motion.css. */
.ui-tcard__acts {
	display: flex;
	align-items: center;
	gap: var(--ed-s3);
	margin-top: 10px;
	padding: 14px 18px;
	border-top: 1px solid var(--ed-border);
}

/* Screen-reader-only, owned by the kit so a component never depends on
   Bootstrap being on the page (the first-run layout does not load it). */
.ui-sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

@media (max-width: 640px) {
	.ui-tcard__head { padding: 14px 14px 0; }
	.ui-tcard__body { padding: 12px 14px 4px; }

	/* Label over value: 140px of label and a long value do not share a phone. */
	.ui-tcard__row {
		flex-direction: column;
		align-items: stretch;
		gap: 3px;
	}

	.ui-tcard__row dt { flex: none; }

	.ui-tcard__acts {
		flex-direction: column;
		align-items: stretch;
		padding: 14px;
	}

	.ui-tcard__acts > * {
		width: 100%;
		justify-content: center;
	}
}

@media (prefers-reduced-motion: reduce) {
	.ui-tcard,
	.ui-tcard__label,
	.ui-tcard__pending {
		animation: none !important;
		transition: none !important;
	}

	.ui-tcard__pending { background: var(--ed-border); }
}

/* ---- <x-ui.chat-turn> (one turn in an assistant conversation) ----
   No mark beside the turn. The presence is <x-ui.orb>, one per page, at the
   live edge of the thread - a mark on every assistant line is the retro
   chat-transcript look and it went with the redesign (Sep 2026). The bubble
   asymmetry stays: Dash speaks on the canvas, the owner replies in a bubble
   on their own side. */
.ui-turn {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	min-width: 0;
}

.ui-turn__body {
	flex: 1 1 auto;
	min-width: 0;
	font: 400 14px/1.5 var(--ed-font);
	color: var(--ed-ink);
	white-space: pre-wrap;
	overflow-wrap: anywhere;
	padding-top: 3px;
}

/* The question on its own line: the thing the chips answer. */
.ui-turn__q {
	display: block;
	font: 600 15.5px/1.35 var(--ed-font);
	color: var(--ed-ink);
	margin-top: 6px;
}

/* The owner's reply: a soft right-aligned bubble. */
.ui-turn--user {
	justify-content: flex-end;
}

.ui-turn--user .ui-turn__body {
	flex: 0 1 auto;
	max-width: 70%;
	background: var(--ed-navy-100);
	border-radius: 12px 12px 4px 12px;
	padding: 10px 14px;
	color: var(--ed-ink);
}

/* ---- <x-ui.chip-row> (tappable answer chips) ---- */
.ui-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.ui-chip {
	display: inline-flex;
	align-items: center;
	min-height: 36px;
	padding: 7px 14px;
	border: 1.5px solid var(--ed-border);
	border-radius: var(--ed-r-pill);
	background: var(--ed-surface);
	color: var(--ed-ink);
	font: 500 13.5px/1.3 var(--ed-font);
	white-space: nowrap;
	cursor: pointer;
	transition: border-color var(--ed-fast), background var(--ed-fast);
}

.ui-chip:hover {
	border-color: var(--ed-navy-600);
}

.ui-chip:focus-visible {
	outline: 2px solid var(--ed-navy-600);
	outline-offset: 2px;
}

.ui-chip.is-selected {
	border-color: var(--ed-navy-600);
	background: var(--ed-navy-100);
	color: var(--ed-navy-700);
	font-weight: 600;
}

.ui-chip:disabled {
	opacity: .5;
	cursor: default;
}

/* Pick mode: the chip selects, a Continue button commits. The radio is the
   truth, so the highlight survives every morph. */
.ui-chips--pick .ui-chip {
	position: relative;
}

.ui-chips--pick .ui-chip input {
	position: absolute;
	opacity: 0;
	width: 100%;
	height: 100%;
	inset: 0;
	margin: 0;
	cursor: pointer;
}

.ui-chips--pick .ui-chip:has(input:checked) {
	border-color: var(--ed-navy-600);
	background: var(--ed-navy-100);
	color: var(--ed-navy-700);
	font-weight: 600;
}

.ui-chips--pick .ui-chip:has(input:focus-visible) {
	outline: 2px solid var(--ed-navy-600);
	outline-offset: 2px;
}

.ui-chooser__value {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.ui-chooser__value.is-placeholder {
	color: var(--ed-muted);
}

.ui-chooser__caret {
	flex: 0 0 auto;
}

.ui-chooser__panel {
	position: absolute;
	top: 100%;
	left: 0;
	width: 100%;
	z-index: 1000;
}

.ui-chooser__list {
	margin-top: 4px;
	max-height: 322px;
	overflow-y: auto;
	background: var(--ed-surface);
	border: 1px solid var(--ed-border);
	border-radius: 10px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, .08);
}

/* An option is a <button>, so the browser's own button chrome has to go for it
   to read as a row. */
.ui-chooser__option {
	display: block;
	width: 100%;
	padding: 10px 12px;
	text-align: left;
	background: none;
	border: 0;
	font-size: 15px;
	line-height: 20px;
	color: var(--ed-ink);
	cursor: pointer;
}

.ui-chooser__option:hover {
	background: var(--ed-sunken);
}

/* The hint sits after the label on the same line, the way it read before. */
.ui-chooser__hint {
	margin-left: 6px;
	font-size: 12px;
	color: var(--ed-muted);
}

/* ---- <x-ui.sequence> editing (#4666) ----
   The drafted job is the editing surface, so the fields live on the step, at the
   same size as the text they replace - a field that grows on focus moves the page
   under the owner's cursor. */
.ui-seq__title-field {
	font: 600 13.5px/1.4 var(--ed-font);
	color: var(--ed-ink);
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-ctl);
	background: var(--ed-surface);
	padding: 5px 9px;
	min-width: 220px;
	flex: 1 1 220px;
}

.ui-seq__title-field:focus {
	outline: none;
	border-color: var(--ed-navy-600);
	box-shadow: 0 0 0 1px var(--ed-navy-600);
}

.ui-seq__msg-field {
	margin-top: 5px;
	width: 100%;
	font: 400 12.5px/1.45 var(--ed-font);
	color: var(--ed-ink);
	background: var(--ed-surface);
	border: 1px solid var(--ed-border);
	border-radius: 8px;
	padding: 7px 10px;
	resize: vertical;
}

.ui-seq__msg-field:focus {
	outline: none;
	border-color: var(--ed-navy-600);
	box-shadow: 0 0 0 1px var(--ed-navy-600);
}

/* The gate as a real switch on the step. Same chip, so nothing moves when it
   flips; only the pointer says it can be pressed. */
button.ui-seq__chip--switch {
	border: 0;
	cursor: pointer;
	font-family: inherit;
}

button.ui-seq__chip--switch:hover {
	filter: brightness(0.96);
}

.ui-seq__drop {
	margin-left: auto;
	background: none;
	border: 0;
	padding: 0;
	font: 500 11.5px/1.4 var(--ed-font);
	color: var(--ed-faint);
	cursor: pointer;
}

.ui-seq__drop:hover {
	color: var(--ed-danger);
}

/* ---- <x-ui.ask-box> - say it in a sentence ----
   One box, three doors: the Bots page, the builder and a finished run. */
.ui-ask {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.ui-ask__head {
	display: flex;
	align-items: center;
	gap: 9px;
}

.ui-ask__title {
	font: var(--ed-title-18);
	color: var(--ed-ink);
	margin: 0;
}

.ui-ask__row {
	display: flex;
	align-items: center;
	gap: 10px;
}

.ui-ask__field {
	flex: 1 1 auto;
	min-width: 0;
	height: 44px;
	border: 1px solid var(--ed-border);
	border-radius: 12px;
	background: var(--ed-sunken);
	padding: 0 14px;
	font: var(--ed-body-14);
	color: var(--ed-ink);
}

.ui-ask__field::placeholder {
	color: var(--ed-faint);
}

.ui-ask__field:focus {
	outline: none;
	background: var(--ed-surface);
	border-color: var(--ed-navy-600);
	box-shadow: 0 0 0 1px var(--ed-navy-600);
}

.ui-ask__go {
	flex-shrink: 0;
	height: 44px;
	border-radius: 12px;
}

.ui-ask__go:disabled {
	opacity: 0.5;
}

@media (max-width: 575px) {
	.ui-ask__row { flex-wrap: wrap; }
	.ui-ask__field { flex: 1 1 100%; }

	/* A chip that DOES something needs a thumb's worth of room. The look does
	   not change; only the box around the words grows. */
	button.ui-seq__chip--switch { padding: 9px 13px; }
	.ui-seq__drop { padding: 11px 4px; }
}

@media (max-width: 767px) {
	.ui-turn--user .ui-turn__body {
		max-width: 88%;
	}

	.ui-chip {
		min-height: 44px;
	}
}

/* ---- <x-ui.reading-pane> --------------------------------------------------
   A listing with the record being read open beside it. Nothing about the
   listing changes: it keeps its filters, its tick boxes, its columns and its
   pagination, and only where a record opens moves. */
/* Search and filters, full width above both columns: a filter row that wrapped
   the moment a record opened would be the list paying for the pane. */
.ui-read__toolbar {
	margin-bottom: var(--ed-s4);
}

.ui-read__cols {
	display: grid;
	gap: var(--ed-s4);
	align-items: start;
}

.ui-read__list {
	min-width: 0;
}

.ui-read__pane {
	position: relative;
	display: flex;
	flex-direction: column;
	min-width: 0;
	background: var(--ed-surface);
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-card);
	box-shadow: var(--ed-shadow-card);
}

.ui-read__head {
	padding: 16px 52px 14px 18px;
	border-bottom: 1px solid var(--ed-border);
}

.ui-read__body {
	min-height: 0;
	padding: 16px 18px;
	overflow-y: auto;
}

/* Quiet, the way every secondary icon control on a page is (rule 0024): the
   record is what the pane is for, not the way out of it. */
.ui-read__close {
	position: absolute;
	top: 10px;
	right: 10px;
	width: 30px;
	height: 30px;
	padding: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: var(--ed-r-ctl);
	background: none;
	color: var(--ed-muted);
	font-size: 14px;
	line-height: 1;
	cursor: pointer;
	transition: background var(--ed-fast), color var(--ed-fast);
}

.ui-read__close:hover,
.ui-read__close:focus-visible {
	background: var(--ed-sunken);
	color: var(--ed-ink);
}

/* The row being read is painted, never implied (rule 0045). The listing's own
   hover rule is `.content .table > tbody > tr:hover > td`, so this carries the
   specificity to beat it; the accent down the leading edge reads under the
   pointer either way. */
.content .ui-read__list .ui-listing__table > tbody > tr.is-reading > td {
	background: var(--ed-navy-100);
}

.content .ui-read__list .ui-listing__table > tbody > tr.is-reading > td:first-child {
	box-shadow: inset 3px 0 0 var(--ed-navy-700);
}

@media (min-width: 1400px) {
	/* Both fit only from here: a listing of this shape needs about 750px before
	   its columns start squeezing, and the pane needs 340px to be readable. */
	.ui-read--open .ui-read__cols {
		grid-template-columns: minmax(0, 1fr) clamp(340px, 33%, 420px);
	}

	/* The pane follows the reader down a long list, and the body inside it
	   scrolls so the title row never leaves. */
	.ui-read--open .ui-read__pane {
		position: sticky;
		top: 16px;
		max-height: calc(100vh - 32px);
	}

	.ui-read--open .ui-read__body {
		flex: 1 1 auto;
	}
}

@media (max-width: 1399.98px) {
	/* No room for two columns here. A page that uses this component shows the
	   record another way below this width - the inbox opens the same body in
	   <x-modal.sidebar.panel> - so the pane keeps out of the way rather than
	   squeezing the listing it sits beside. */
	.ui-read__pane {
		display: none;
	}
}

/* ============================================================================
   Motion primitives for the conversational surfaces (Sep 2026).
   Tokens: --ed-ease-* / --ed-dur-* in base-tokens.css. Showcase: /design-system
   "Thinking, status and arrival". Each one collapses under reduced motion.
   ========================================================================== */

/* Thinking is the words themselves, shimmering - never a spinner. The shine
   holds at each end so it reads as a pass, not a strobe (aicss.dev Thinking
   State). Put it on the one line that says what is being done. */
.ui-shimmer-text {
	color: transparent;
	background: linear-gradient(90deg,
		var(--ed-muted) 0 30%,
		color-mix(in srgb, var(--ed-muted) 40%, transparent) 45% 55%,
		var(--ed-muted) 70% 100%);
	background-size: 300% 100%;
	-webkit-background-clip: text;
	background-clip: text;
	animation: ui-shimmer-text 2.25s cubic-bezier(.25, .1, .25, 1) infinite;
}

@keyframes ui-shimmer-text {
	0%, 18%   { background-position: 100% 0; }
	82%, 100% { background-position: 0% 0; }
}

/* A status line whose copy changes ("Reading your site" -> "Found 12 items")
   without ever moving the page: the height is reserved for two lines. */
.ui-status-line {
	min-height: 2.6em;
	max-width: 56ch;
	margin-inline: auto;
	text-align: center;
	font: var(--ed-small-12);
	color: var(--ed-muted);
}

.ui-status-line b,
.ui-status-line strong { color: var(--ed-ink); font-weight: 600; }

/* Arrival: 8px lift on a gentle overshoot. Children step their own --delay
   (question 0, field 100ms, chips 200ms) so a screen assembles instead of
   appearing. Opacity and transform only, so nothing reflows. */
.ui-rise {
	animation: ui-rise var(--ed-dur-rise) var(--ed-ease-gentle) var(--delay, 0s) both;
}

@keyframes ui-rise {
	from { opacity: 0; transform: translateY(var(--ed-s2)); }
	to   { opacity: 1; transform: none; }
}

/* The press: every kit control squeezes for 90ms before it acts. Hover only
   ever changes color. */
.ui-btn:active,
.ui-chip:active,
.ui-composer__send:active {
	transform: scale(.96);
	transition: transform var(--ed-dur-press) var(--ed-ease-press);
}

@media (prefers-reduced-motion: reduce) {
	.ui-shimmer-text {
		animation: none;
		color: var(--ed-muted);
		background: none;
	}

	.ui-rise { animation: none; }

	.ui-btn:active,
	.ui-chip:active,
	.ui-composer__send:active { transform: none; }
}

/* ============================================================================
   dash-md - what a Dash answer looks like, wherever it is painted.

   public/js/dash-markdown.js is one renderer for every surface. This is its one
   stylesheet: the tags it can emit, styled once. A surface that paints Dash
   markdown puts `dash-md` on the element it writes into and adds only its own
   type scale on top - never its own table or link rules. The on-page Dash card
   shipped without this and rendered a table as one run-together word
   (.claude/rules/learned/0054-a-shared-renderer-needs-shared-css.md). It lives in the
   kit rather than in a page stylesheet so a surface that loads only the tokens
   and the kit still gets it.

   What is NOT here: how wide a table is and whether it scrolls. A panel 380px
   wide and a full page want different answers, so each surface sets its own
   width, min-width and overflow on top.
   ========================================================================== */

.dash-md table {
	border-collapse: collapse;
	margin: 8px 0;
	font-size: 0.8125rem;
}

.dash-md th,
.dash-md td {
	padding: 6px 10px;
	text-align: left;
	border: 1px solid var(--ed-border);
}

.dash-md th {
	background-color: var(--ed-sunken);
	font-weight: 600;
}

/* public/style.css sets a global `a { display: block }`, so a link inside a
   sentence drops onto its own line unless it is put back inline. */
.dash-md a {
	color: var(--ed-navy-600);
	text-decoration: none;
	display: inline;
}

.dash-md a:hover {
	text-decoration: underline;
}

.dash-md code {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.875em;
	background: var(--ed-sunken);
	border: 1px solid var(--ed-border);
	border-radius: 4px;
	padding: 1px 4px;
}

.dash-md h2,
.dash-md h3,
.dash-md h4 {
	margin: 10px 0 4px;
	font-weight: 600;
	line-height: 1.35;
	color: var(--ed-ink);
}

.dash-md h2 { font-size: 1rem; }
.dash-md h3 { font-size: 0.9375rem; }
.dash-md h4 { font-size: 0.875rem; }

.dash-md h2:first-child,
.dash-md h3:first-child,
.dash-md h4:first-child {
	margin-top: 0;
}

.dash-md li {
	margin-bottom: 2px;
}

.dash-md li p {
	margin-bottom: 0;
}

/* Product-tour step. Approved in docs/mockups/in-product-tour/step-crop.png. */
.ui-tour-step { position: fixed; z-index: 1055; padding: var(--ed-s5); border-top: 4px solid var(--ed-navy-600); border-radius: var(--ed-r-card); box-shadow: var(--ed-shadow-card); }
.ui-tour-step__heading { display: flex; align-items: flex-start; gap: var(--ed-s2); }
.ui-tour-step__title { flex: 1; font: var(--ed-display-24); }
.ui-tour-step__line { margin: var(--ed-s3) 0 var(--ed-s4); color: var(--ed-muted); }
.ui-tour-step__actions { display: flex; align-items: center; gap: var(--ed-s2); padding-top: var(--ed-s3); border-top: 1px solid var(--ed-border); }
.ui-tour-step .ui-btn { min-height: 44px; }
.ui-tour-step__count { flex: 1; font: var(--ed-small-12); color: var(--ed-muted); }
.ui-tour-step .ui-tour-step__autoplay { position: relative; flex: none; min-width: 44px; width: 44px; height: 44px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--ed-muted); justify-content: center; align-items: center; }
.ui-tour-step__autoplay i { position: relative; font-size: 14px; line-height: 1; }
/* The play triangle's weight sits left of its box; nudge it to the optical center. */
.ui-tour-step__autoplay .ui-tour-step__play { left: 1px; }
/* Countdown: the ring empties over the step's seconds, starting at the top. */
.ui-tour-step__countdown { position: absolute; inset: 2px; width: calc(100% - 4px); height: calc(100% - 4px); transform: rotate(-90deg); pointer-events: none; }
.ui-tour-step__countdown circle { fill: none; stroke-width: 2.5; }
.ui-tour-step__countdown-track { stroke: var(--ed-border); }
.ui-tour-step__countdown-left { stroke: var(--ed-navy-600); stroke-dasharray: 100; stroke-linecap: round; transition: stroke-dashoffset .25s linear; }
@media (prefers-reduced-motion: reduce) { .ui-tour-step__countdown-left { transition: none; } }
.ui-tour-step .ui-tour-step__autoplay:hover,
.ui-tour-step .ui-tour-step__autoplay:focus-visible { background: var(--ed-navy-100); color: var(--ed-navy-700); }
.ui-tour-step .ui-tour-step__other { width: 100%; margin-top: var(--ed-s3); }

/* <x-ui.choice-list> - a question as a card of choices, in a conversation.
   Approved 2026-09-23 (docs/mockups/setup-opener/, variant B). Rows are
   buttons; the picked one takes the selected navy, the rest go quiet. */
.ui-choice-list {
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-card);
	background: var(--ed-surface);
	padding: 16px;
}

.ui-choice-list__title {
	font: 600 15px/1.45 var(--ed-font);
	color: var(--ed-ink);
	margin-bottom: 12px;
}

.ui-choice-list__rows {
	border: 1px solid var(--ed-border);
	border-radius: 10px;
	overflow: hidden;
}

.ui-choice-list__row {
	display: flex;
	align-items: center;
	gap: 12px;
	width: 100%;
	min-height: 56px;
	padding: 12px 14px;
	border: 0;
	border-top: 1px solid var(--ed-border);
	background: var(--ed-surface);
	color: var(--ed-ink);
	text-align: left;
	cursor: pointer;
	transition: background var(--ed-fast);
}

.ui-choice-list__row:first-child {
	border-top: 0;
}

.ui-choice-list__row:hover:not(:disabled),
.ui-choice-list__row:focus-visible {
	background: var(--ed-sunken);
}

.ui-choice-list__row:focus-visible {
	outline: 2px solid var(--ed-navy-600);
	outline-offset: -2px;
}

.ui-choice-list__icon {
	flex: 0 0 34px;
	height: 34px;
	border-radius: 9px;
	background: var(--ed-navy-100);
	color: var(--ed-navy-700);
	display: grid;
	place-items: center;
	font-size: 14px;
	line-height: 1;
}

.ui-choice-list__text {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.ui-choice-list__name {
	font: 500 14px/1.4 var(--ed-font);
}

.ui-choice-list__desc {
	font: var(--ed-small-12);
	color: var(--ed-muted);
}

.ui-choice-list__row:disabled {
	cursor: default;
}

.ui-choice-list.is-settled .ui-choice-list__row:not(.is-picked) {
	opacity: .55;
}

.ui-choice-list__row.is-picked {
	background: var(--ed-navy-100);
}

.ui-choice-list__row.is-picked .ui-choice-list__name {
	font-weight: 600;
	color: var(--ed-navy-700);
}

/* A phone gives a conversation about 380px of thread above the composer. The
   card keeps every word and gives back its padding, so the question and all
   four rows fit without scrolling. */
@media (max-width: 575.98px) {
	.ui-choice-list {
		padding: 12px;
	}

	.ui-choice-list__title {
		margin-bottom: 8px;
	}

	.ui-choice-list__row {
		min-height: 48px;
		padding: 9px 12px;
		gap: 10px;
	}

	.ui-choice-list__icon {
		flex-basis: 30px;
		height: 30px;
		font-size: 13px;
	}
}

/* ---- <x-ui.brand-tile> (no-photo picture for a gift card or a pass) ----
   Its rules ship inside resources/views/components/ui/brand-tile.blade.php
   (@once): it renders on the booking page and the shop's website, which load
   base-tokens.css and widget/style.css but not this file. */

/* ---- <x-ui.partner-logo> (a connected service's real mark) ---- */
.ui-partner-logo { display: block; flex: 0 0 auto; object-fit: cover; background: var(--ed-surface); box-shadow: 0 0 0 1px var(--ed-border); }
.ui-partner-logo--sm { width: 32px; height: 32px; border-radius: 8px; }
.ui-partner-logo--md { width: 48px; height: 48px; border-radius: 10px; }
.ui-partner-logo--inset { object-fit: contain; padding: 6px; }
.ui-partner-logo--md.ui-partner-logo--inset { padding: 10px; }

/* ---- <x-ui.choice-cards> 'logo' ----
   A brand tile above the title, at its own size (no thumb panel behind it). */
.ui-choice__mark { display: block; margin-bottom: 8px; line-height: 0; }
/* Beside a logo card, an icon card takes the logo's height so the titles line up. */
.ui-choice:has(.ui-choice__mark) .ui-choice__icon { height: 32px; display: flex; align-items: center; }

/* ---- <x-ui.copy-prompt> ----
   Words to paste into another app. Two lines and a fade until opened. */
.ui-copy-prompt { border: 1px solid var(--ed-border); background: var(--ed-sunken); border-radius: var(--ed-r-card); padding: 18px 20px; }
.ui-copy-prompt__head { display: flex; align-items: flex-start; gap: var(--ed-s4); }
.ui-copy-prompt__words { flex: 1; min-width: 0; }
.ui-copy-prompt__title { font: var(--ed-subtitle-15); color: var(--ed-ink); }
.ui-copy-prompt__sub { font: 400 13px/1.45 var(--ed-font); color: var(--ed-muted); margin-top: 2px; }
.ui-copy-prompt__copy { flex: 0 0 auto; }
.ui-copy-prompt__text { margin-top: var(--ed-s3); background: var(--ed-surface); border: 1px solid var(--ed-border); border-radius: 10px; padding: 10px 12px; font: 400 13px/1.5 var(--ed-font); color: var(--ed-ink); max-height: 60px; overflow: hidden; -webkit-mask-image: linear-gradient(var(--ed-ink) 55%, transparent); mask-image: linear-gradient(var(--ed-ink) 55%, transparent); white-space: pre-line; overflow-wrap: anywhere; }
.ui-copy-prompt__text.is-open { max-height: none; -webkit-mask-image: none; mask-image: none; }
.ui-copy-prompt__more { display: inline-flex; align-items: center; gap: 6px; margin-top: var(--ed-s2); padding: 0; border: 0; background: none; font: 500 13px/1.4 var(--ed-font); color: var(--ed-navy-600); cursor: pointer; }
.ui-copy-prompt__more i { font-size: 10px; transition: transform var(--ed-fast); }
@media (max-width: 575px) {
	.ui-copy-prompt { padding: 16px; }
	.ui-copy-prompt__head { flex-direction: column; align-items: stretch; gap: var(--ed-s3); }
	.ui-copy-prompt__copy { align-self: flex-start; }
}

/* ---- <x-ui.news-pill> ----
   One new thing to set up, in the page header. The block form is the phone row. */
.ui-news-pill { display: inline-flex; align-items: center; gap: 10px; padding: 5px 6px 5px 12px; border: 1px solid var(--ed-border); border-radius: var(--ed-r-pill); background: var(--ed-surface); white-space: nowrap; }
.ui-news-pill__text { font: 400 14px/1.3 var(--ed-font); color: var(--ed-ink); }
.ui-news-pill__x { border: 0; background: none; color: var(--ed-muted); padding: 0 6px; font-size: 13px; line-height: 1; cursor: pointer; }
.ui-news-pill__x:hover { color: var(--ed-ink); }
.ui-news-pill--block { display: flex; width: 100%; white-space: normal; border-radius: var(--ed-r-card); padding: 10px 10px 10px 12px; margin-bottom: var(--ed-s4); }
.ui-news-pill--block .ui-news-pill__text { flex: 1; min-width: 0; }

/* ---- <x-ui.spotlight> ----
   A first-time pointer: the ring's huge spread shadow is the dimmed page, so
   the target stays bright and clickable-looking without moving it in the DOM. */
.ui-spotlight { position: relative; z-index: calc(var(--ed-z-drawer) - 2); }
.ui-spotlight__ring { position: fixed; z-index: calc(var(--ed-z-drawer) - 2); border-radius: var(--ed-r-card); border: 2px solid var(--ed-surface); box-shadow: 0 0 0 9999px var(--ed-scrim); pointer-events: none; transition: all var(--ed-fast); }
.ui-spotlight__tip { position: fixed; z-index: calc(var(--ed-z-drawer) - 2); background: var(--ed-surface); border-radius: var(--ed-r-card); padding: var(--ed-s4) var(--ed-s5); box-shadow: var(--ed-shadow-pop); }
.ui-spotlight__title { font: var(--ed-subtitle-15); color: var(--ed-ink); margin-bottom: var(--ed-s1); }
.ui-spotlight__text { font: var(--ed-body-14); color: var(--ed-muted); }
.ui-spotlight__actions { display: flex; justify-content: flex-end; align-items: center; gap: var(--ed-s2); margin-top: var(--ed-s3); }
.ui-spotlight__count { flex: 1; font: var(--ed-small-12); color: var(--ed-muted); }

/* ---- <x-settings.banner> ----
   The banner's look used to live only in css/ai-bookings.css, which the admin
   layout loads and the supplier layout does not, so on a settings page the
   banner painted as plain text (#6323). The kit owns it now; ai-bookings.css
   keeps its own copy for the pages that load it. */
.dash-banner { display: flex; align-items: center; gap: var(--ed-s4); border: 1px solid var(--ed-border); border-radius: var(--ed-r-card); background: var(--ed-sunken); padding: var(--ed-s5); margin-bottom: var(--ed-s5); }
.dash-banner-tile { width: 44px; height: 44px; flex: none; background: var(--ed-surface); border: 1px solid var(--ed-border); border-radius: var(--ed-r-ctl); display: flex; align-items: center; justify-content: center; font-size: 20px; }
.dash-banner-body { flex: 1; min-width: 0; }
.dash-banner-title { display: flex; align-items: center; gap: var(--ed-s2); }
.dash-banner-title span { font: var(--ed-subtitle-15); font-weight: 700; color: var(--ed-ink); }
.dash-banner-text { font: var(--ed-body-14); color: var(--ed-muted); margin-top: var(--ed-s1); }
/* style.css sets a global a { display: block }, which broke every link in a banner onto its own line. */
.dash-banner-text a { display: inline; }
.dash-banner--warn { background: var(--ed-warning-soft); border-color: var(--ed-warning); }
.dash-banner .dash-pill { display: inline-flex; align-items: center; font: var(--ed-caption-11); font-weight: 700; border-radius: var(--ed-r-pill); padding: 3px 9px; white-space: nowrap; background: var(--ed-sunken); color: var(--ed-muted); }
@media (max-width: 575.98px) { .dash-banner { flex-wrap: wrap; padding: var(--ed-s4); } }

/* ============================================================================
   <x-ui.who-can-book> - the three-way "Who can book it" picker (rule 0064,
   #6184). Lifted unchanged from .ed-segmented / .pd-segmented on the detail
   pages it came from: the track and idle text read the detail pages' own
   :root values, with the nearest v2 token as the fallback, so the extraction
   moves nothing on those pages.
   ========================================================================== */
.ui-who-can-book {
	display: inline-flex;
	background: var(--ed-soft-bg, var(--ed-sunken));
	border-radius: var(--ed-r-ctl);
	padding: 3px;
	gap: 2px;
}
.ui-who-can-book__btn {
	padding: 6px 14px;
	border-radius: 6px;
	background: transparent;
	border: none;
	font-size: 13px;
	font-weight: 500;
	color: var(--ed-mute, var(--ed-muted));
	cursor: pointer;
	transition: background var(--ed-fast), color var(--ed-fast);
}
.ui-who-can-book__btn:hover:not(.active):not(:disabled) { color: var(--ed-ink); }
.ui-who-can-book__btn.active {
	background: var(--ed-surface);
	color: var(--ed-navy-700);
	box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
}
.ui-who-can-book__btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* W3 (seed walk 2026-10-07): the round Dash button sits fixed at the bottom right
   (56px, 24px in). A settings page's last row could not scroll out from under it, so a
   click on its switch landed on Dash. The page makes room; the button never moves
   (learned rule 0077). */
.settings-content-wraper { padding-bottom: 96px; }

/* ---- Help: the labeled button that closes every page header ---- */
.ui-help__label {
	gap: 6px;
	white-space: nowrap;
}
.ui-ph__actions > .ui-help,
.ui-help:has(> .ui-help__label) {
	margin-left: 0;
}
/* <x-ui.progress-ring> (issue 8924): a short list's progress, done/total in the middle. */
.ui-ring { display: block; flex: none; }
.ui-ring__track { fill: none; stroke: var(--ed-navy-200); stroke-width: 7; }
.ui-ring__fill { fill: none; stroke: var(--ed-success); stroke-width: 7; stroke-linecap: round; transition: stroke-dasharray .4s ease; }
.ui-ring__text { font: 700 15px var(--ed-font); fill: var(--ed-navy-900); }

/* <x-ui.next-step> (issue 8924): the one step to do now. Live keeps the page's navy;
   the Dash mark in its label is the accent (rule 0025). */
.ui-next-step { background: var(--ed-surface); border: 2px solid var(--ed-navy-700); border-radius: 14px; padding: 16px; }
.ui-next-step__label { display: flex; align-items: center; gap: 6px; font: var(--ed-caption-11); letter-spacing: .06em; text-transform: uppercase; color: var(--ed-navy-600); }
.ui-next-step__title { font: 600 17px/1.3 var(--ed-font); color: var(--ed-navy-900); margin-top: 4px; }
.ui-next-step__line { font: var(--ed-small-12); color: var(--ed-muted); margin-top: 2px; }
.ui-next-step__actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.ui-next-step__actions .ui-btn .ui-dash-mark { color: var(--ed-on-accent); margin-right: 6px; }

/* <x-ui.app-badges> (issue 8924): store badges, and a QR code beside them on a wide screen. */
.ui-app-badges { display: flex; align-items: center; gap: 24px; }
.ui-app-badges__stores { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.ui-app-badges__store img { display: block; height: 40px; width: auto; }
.ui-app-badges__qr { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 6px; font: var(--ed-small-12); color: var(--ed-muted); }
.ui-app-badges__code { display: block; width: 132px; height: 132px; padding: 10px; background: var(--ed-surface); border: 1px solid var(--ed-border); border-radius: 14px; }
.ui-app-badges__code svg { display: block; width: 100%; height: 100%; }
.ui-app-badges__code .dark { fill: var(--ed-navy-900); }
.ui-app-badges__code .light { fill: transparent; }
@media (max-width: 760px) { .ui-app-badges__qr { display: none; } }

/* <x-ui.demo-shop-links> (#8980): Try it yourself, Ways to sell online and See its
   website under a playing demo video. Kit buttons in the portal, signup's own
   .ais-chip pills there; the surface sets the spacing around the row. */
.ui-demo-links { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.ui-demo-links .ui-btn, .ui-demo-links .ais-chip { text-decoration: none; white-space: nowrap; }
.ui-demo-links__ext { margin-left: 5px; flex: 0 0 auto; }
.ui-demo-links--chip { gap: 6px; }
.ui-demo-links--chip .ui-demo-links__ext { margin-left: 0; }
/* <x-ui.demo-shop-card> (#9261): a demo shop that opens live, side by side so three fit in a card. */
.ui-demo-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; }
.ui-demo-card { display: flex; gap: 10px; align-items: center; min-width: 0; padding: 8px; border: 1px solid var(--ed-border); border-radius: var(--ed-r-card); background: var(--ed-surface); color: var(--ed-ink); text-decoration: none; }
.ui-demo-card:hover { background: var(--ed-sunken); color: var(--ed-ink); text-decoration: none; }
.ui-demo-card__thumb { flex: 0 0 64px; height: 40px; border-radius: var(--ed-r-ctl); background: var(--ed-sunken) center / cover no-repeat; }
.ui-demo-card__text { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.ui-demo-card__name { font-size: 13px; font-weight: 600; }
.ui-demo-card__kind { font-size: 12px; color: var(--ed-muted); }
.ui-demo-card__go { font-size: 12px; font-weight: 600; color: var(--ed-navy-600); }

/* <x-ui.setup-list> - what Dash worked out, as one list to approve (#8964).
   overflow: clip, not hidden: hidden makes the list a scroll box and the
   pinned footer would stick to the list instead of the chat it scrolls in. */
.ui-setup-list {
	border: 1px solid var(--ed-border);
	border-radius: var(--ed-r-card);
	background: var(--ed-surface);
	overflow: clip;
}

.ui-setup-list__head {
	padding: 14px 16px 10px;
	border-bottom: 1px solid var(--ed-border);
}

.ui-setup-list__title {
	font: 600 15px/1.45 var(--ed-font);
	color: var(--ed-ink);
}

.ui-setup-list__sub {
	font: var(--ed-small-12);
	color: var(--ed-muted);
	margin-top: 2px;
}

.ui-setup-list__group {
	border-bottom: 1px solid var(--ed-border);
}

.ui-setup-list__group-head {
	padding: 9px 16px;
	background: var(--ed-sunken);
	font: 600 13px/1.4 var(--ed-font);
	color: var(--ed-ink);
}

.ui-setup-list__count {
	font-weight: 400;
	color: var(--ed-muted);
	margin-left: 4px;
}

.ui-setup-list__row {
	display: flex;
	align-items: center;
	gap: 12px;
	min-height: 56px;
	margin: 0;
	padding: 8px 16px;
	border-top: 1px solid var(--ed-border);
	cursor: pointer;
}

.ui-setup-list__group-head + .ui-setup-list__row {
	border-top: 0;
}

.ui-setup-list__tick {
	flex: 0 0 18px;
	width: 18px;
	height: 18px;
	accent-color: var(--ed-navy-700);
}

.ui-setup-list__thumb {
	flex: 0 0 40px;
	width: 40px;
	height: 40px;
	border-radius: 8px;
	object-fit: cover;
	background: var(--ed-sunken);
}

.ui-setup-list__thumb--icon {
	display: grid;
	place-items: center;
	color: var(--ed-navy-700);
	font-size: 14px;
}

.ui-setup-list__text {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
}

.ui-setup-list__name {
	font: 500 14px/1.4 var(--ed-font);
	color: var(--ed-ink);
	overflow-wrap: anywhere;
}

.ui-setup-list__detail {
	font: var(--ed-small-12);
	color: var(--ed-muted);
	overflow-wrap: anywhere;
}

.ui-setup-list__more {
	display: block;
	margin: 0 0 6px 46px;
	padding: 6px 0;
	border: 0;
	background: none;
	font: 500 13px/1.4 var(--ed-font);
	color: var(--ed-navy-700);
	text-decoration: underline;
	cursor: pointer;
}

.ui-setup-list__same {
	display: flex;
	gap: 8px;
	align-items: baseline;
	padding: 10px 16px;
	font: var(--ed-small-12);
	color: var(--ed-muted);
}

.ui-setup-list__foot {
	position: sticky;
	bottom: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	align-items: center;
	padding: 12px 16px;
	border-top: 1px solid var(--ed-border);
	background: var(--ed-surface);
}

.ui-setup-list__row.is-extra {
	display: none;
}

.ui-setup-list__more-toggle:checked ~ .ui-setup-list__row.is-extra {
	display: flex;
}

.ui-setup-list__more-toggle:checked ~ .ui-setup-list__more {
	display: none;
}

.ui-setup-list.is-settled .ui-setup-list__row {
	cursor: default;
}
