/*
 * THE SHELL — see AppShell.tsx. Plain CSS on purpose: this chrome sits above
 * Bootstrap pages and Tailwind pages alike and belongs to neither.
 *
 * Widths: 14rem open, 3.5rem collapsed (jBKB: w-56 / w-14). Selection is the
 * console's near-black (#1c2530), the same the queue rail uses, so "where am
 * I" reads the same in the menu and in the queue.
 */
.shell { display: flex; height: 100dvh; overflow: hidden; background: #f4f6f8; color: #1e293b; }
.shell-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
/* ⚠ THE PAGE AREA. Scrolls for pages that scroll; pages that own scrolling are
   flex children and take its height (`.queue-shell`, the report pages). */
.shell-page { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: auto; background: #fff; }
.shell-page > .ws-pane { flex: 1 1 auto; min-height: 0; }

/* Sidebar */
.shell-side { flex: 0 0 14rem; width: 14rem; display: flex; flex-direction: column; min-height: 0; background: #e9edf1; border-right: 1px solid #d5dbe2; transition: width .15s ease, flex-basis .15s ease; }
.shell-side.is-collapsed { flex-basis: 3.5rem; width: 3.5rem; }
.shell-side.is-drawer { position: fixed; inset: 0 auto 0 0; z-index: 60; width: 16rem; flex-basis: 16rem; box-shadow: 0 0 0 1px rgba(0,0,0,.06), 0 12px 32px rgba(0,0,0,.25); }
.shell-scrim { position: fixed; inset: 0; z-index: 59; background: rgba(28,37,48,.45); }
/* Light, like jBKB's: the one dark surface of the frame is the page-tab strip (Johan, 2026-09-15, screenshot of jBKB). */
.shell-brand { display: flex; align-items: center; gap: .5rem; height: 2.75rem; padding: 0 .55rem 0 .7rem; border-bottom: 1px solid #d5dbe2; background: #e9edf1; color: #1e293b; }
.shell-brandname { font-weight: 700; letter-spacing: .04em; font-size: .9rem; flex: 1 1 auto; }
/* jBKB's mark: a small dark rounded square with jB in it (Johan, 2026-09-15: the image looked strange when narrow). */
.shell-mark { text-decoration: none; transition: opacity .12s; display: inline-flex; align-items: center; justify-content: center; width: 1.75rem; height: 1.75rem; flex: 0 0 auto; border-radius: .4rem; background: #1c2530; color: #fff; font-size: .72rem; font-weight: 700; letter-spacing: -.01em; }
a.shell-mark:hover { opacity: .8; color: #fff; }
.shell-side.is-collapsed .shell-brand { padding: 0; justify-content: center; flex-direction: column; height: auto; gap: .2rem; padding: .4rem 0; }
.shell-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 1.9rem; height: 1.9rem; border: none; border-radius: .35rem; background: transparent; color: inherit; cursor: pointer; opacity: .8; }
.shell-iconbtn:hover { opacity: 1; background: rgba(28,37,48,.08); }
.shell-top .shell-iconbtn:hover { background: rgba(28,37,48,.08); }
.shell-iconbtn svg { width: 1.05rem; height: 1.05rem; }
.shell-nav { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: .5rem .45rem; display: flex; flex-direction: column; gap: .7rem; }
.shell-sectionlabel { font-size: .6rem; text-transform: uppercase; letter-spacing: .08em; color: #7b8794; padding: 0 .45rem .25rem; }
.shell-sectionrule { height: 1px; background: #d5dbe2; margin: .1rem .3rem .35rem; }
.shell-link { display: flex; align-items: center; gap: .55rem; padding: .38rem .5rem; border-radius: .35rem; color: #1e293b; text-decoration: none; font-size: .84rem; line-height: 1.2; position: relative; }
.shell-link:hover { background: #dde3e9; color: #1e293b; text-decoration: none; }
.shell-link.is-active { background: #1c2530; color: #fff; font-weight: 600; }
.shell-link.is-loading { opacity: .75; }
.shell-linkicon { width: 1rem; height: 1rem; flex: 0 0 auto; opacity: .85; }
.shell-linklabel { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shell-side.is-collapsed .shell-link { justify-content: center; padding: .45rem 0; }
.shell-spin { width: .7rem; height: .7rem; border-radius: 999px; border: 2px solid currentColor; border-right-color: transparent; animation: shell-spin .7s linear infinite; flex: 0 0 auto; }
.shell-side.is-collapsed .shell-spin { position: absolute; right: .2rem; top: .2rem; width: .5rem; height: .5rem; }
@keyframes shell-spin { to { transform: rotate(360deg); } }
.shell-sidefoot { padding: .4rem .5rem; border-top: 1px solid #d5dbe2; font-size: .72rem; min-height: 1.6rem; }
.shell-side.is-collapsed .shell-sidefoot { display: none; }

/* Top bar */
.shell-top { display: flex; align-items: center; gap: .6rem; height: 2.75rem; flex: 0 0 auto; padding: 0 .8rem; background: #fff; border-bottom: 1px solid #d5dbe2; }
.shell-topspacer { flex: 1 1 auto; }
/* Notices, about a centimetre right of the solution — Johan, 2026-09-15. The board anchors here. */
.shell-notices { position: relative; margin-left: 1cm; display: flex; align-items: center; }
/* The board was an in-flow block on the queue page; under the bar it is a dropdown. */
.shell-notices .noticeboard { position: absolute; top: calc(100% + .35rem); left: 0; z-index: 70; width: min(36rem, calc(100vw - 2rem)); margin: 0; max-height: 75vh; overflow-y: auto; }
.shell-solsel { max-width: 16rem; padding: .2rem 1.6rem .2rem .5rem; font-size: .82rem; border-radius: .3rem; border: 1px solid #c8d0d8; background: #fff; color: #1e293b; }
.shell-solname { font-size: .84rem; font-weight: 600; color: #1e293b; }
.shell-user { position: relative; }
.shell-userbtn { display: inline-flex; align-items: center; gap: .45rem; border: none; background: transparent; cursor: pointer; padding: .15rem .3rem; border-radius: .35rem; color: #1e293b; }
.shell-userbtn:hover { background: #eef1f4; }
.shell-userbtn svg { width: 1.1rem; height: 1.1rem; opacity: .8; }
.shell-username { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.1; }
.shell-useremail { font-size: .78rem; max-width: 16rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shell-userrole { font-size: .64rem; color: #7b8794; }
.shell-usermenu { position: absolute; right: 0; top: calc(100% + .3rem); z-index: 50; min-width: 11rem; background: #fff; border: 1px solid #d5dbe2; border-radius: .4rem; box-shadow: 0 8px 24px rgba(28,37,48,.14); padding: .25rem; display: flex; flex-direction: column; }
.shell-usermenu button { display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left; border: none; background: transparent; padding: .4rem .55rem; border-radius: .3rem; font-size: .82rem; cursor: pointer; color: #1e293b; }
.shell-usermenu button:hover { background: #eef1f4; }
.shell-usermenu svg { width: .95rem; height: .95rem; opacity: .75; }
@media (max-width: 860px) {
  .shell-top { padding: 0 .5rem; }
  .shell-useremail { max-width: 9rem; }
  .shell-solsel { max-width: 10rem; font-size: .76rem; }
}
@media (prefers-reduced-motion: reduce) { .shell-side { transition: none; } .shell-spin { animation: none; } }

/* Workspace tabs — between the bar and the page. The home tab is the queue.
   ⛔ THE SAME PALETTE AS jBKB'S STRIP, TO THE HEX — Johan, 2026-09-15: "make
   sure AIM and jBKB look exactly the same". Tailwind slate: strip 800
   (#1e293b, edge 900 #0f172a); an unselected tab 500 (#64748b) with 100 text
   (#f1f5f9) and a 400 edge (#94a3b8), 400 on hover; the open page white with
   the indigo-500 underline (#6366f1). Text .75rem, padding .5rem .75rem, at
   least 7.5rem wide - jBKB's text-xs px-3 py-2 min-w-[120px]. Change one,
   change the other (frontend/src/components/ui/draggable-tab-bar.tsx). */
.ws-tabs { display: flex; align-items: flex-end; gap: .25rem; padding: .25rem .375rem 0; background: #1e293b; border-bottom: 1px solid #0f172a; overflow-x: auto; flex: 0 0 auto; scrollbar-width: thin; }
.ws-tab { display: inline-flex; align-items: center; gap: .375rem; min-width: 7.5rem; max-width: 13.75rem; padding: .5rem .75rem; border: 1px solid #94a3b8; border-bottom: none; border-radius: .375rem .375rem 0 0; background: #64748b; color: #f1f5f9; font-size: .75rem; font-weight: 500; line-height: 1rem; cursor: pointer; user-select: none; white-space: nowrap; position: relative; flex: 0 1 auto; overflow: hidden; }
.ws-tab:hover { background: #94a3b8; color: #fff; }
.ws-tab.is-active { background: #fff; color: #0f172a; border-color: #fff; }
.ws-tab.is-active::after { content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: #6366f1; }
/* The queue is the home tab: when it is not the open page it still reads apart from the others — indigo-tinted, its accent bar. */
.ws-tab.is-home { padding-left: .7rem; background: #4f46e5; border-color: #818cf8; color: #eef2ff; font-weight: 600; }
.ws-tab.is-home:hover { background: #6366f1; }
.ws-tab.is-home::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: #c7d2fe; border-radius: .375rem 0 0 0; }
.ws-tab.is-home.is-active { background: #fff; color: #0f172a; border-color: #fff; }
.ws-tab.is-home.is-active::before { background: #4f46e5; }
.ws-tab.is-over { outline: 2px dashed #a5b4fc; outline-offset: -2px; }
.ws-tabicon { width: .9rem; height: .9rem; opacity: .85; flex: 0 0 auto; }
.ws-tabbadge { flex: 0 0 auto; }
/* ⛔ The LABEL gives way, never the X — Johan, 2026-09-16: "make sure that the X is always inside the tab". */
.ws-tablabel { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ws-tabsuffix { font-weight: 400; opacity: .7; }
.ws-tabbadge { display: inline-flex; align-items: center; justify-content: center; min-width: 1.15rem; height: 1.15rem; padding: 0 .3rem; border-radius: 999px; background: #dc2626; color: #fff; font-size: .68rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.ws-tabclose { display: inline-flex; align-items: center; justify-content: center; width: 1.1rem; height: 1.1rem; border: none; border-radius: .25rem; background: transparent; color: inherit; opacity: .55; cursor: pointer; padding: 0; margin-right: -.2rem; flex: 0 0 auto; }
.ws-tabclose:hover { opacity: 1; background: rgba(0,0,0,.12); }
.ws-tab.is-active .ws-tabclose:hover { background: rgba(28,37,48,.1); }
.ws-tabclose svg { width: .8rem; height: .8rem; }
.ws-tab:focus-visible { outline: 2px solid #9aa5f0; outline-offset: -2px; }
/* Close-all, at the strip's right end and vertically centred on the tabs. */
.ws-closeall-wrap { margin-left: auto; align-self: center; display: flex; flex: 0 0 auto; padding-bottom: .25rem; }
.ws-closeall { display: inline-flex; align-items: center; justify-content: center; width: 1.6rem; height: 1.6rem; border: none; border-radius: .3rem; background: transparent; color: #94a3b8; cursor: pointer; padding: 0; }
.ws-closeall:hover { color: #fff; background: rgba(255,255,255,.12); }
.ws-closeall svg { width: 1rem; height: 1rem; }
.ws-closeall:focus-visible { outline: 2px solid #9aa5f0; outline-offset: -2px; }
.ws-pane { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.ws-pane > * { flex: 1 0 auto; }
.ws-pane > .queue-shell, .ws-pane > .tw-root { flex: 1 1 auto; min-height: 0; }

/* The menu's hover tips: a fixed width that wraps, on the menu's own grey — easier to read over the page than a white strip (Johan, 2026-09-15). Unlayered, so it wins over the tip's utility classes. */
.shell-tip { width: 15rem; max-width: 15rem; white-space: normal; background: #e9edf1; color: #1e293b; border-color: #c8d0d8; box-shadow: 0 6px 18px rgba(28,37,48,.16); font-size: .74rem; line-height: 1.35; padding: .45rem .6rem; }
.shell-tip::first-line { font-weight: 600; }
.control-slider {
	margin: 0;
	background-color: #e0e0e0;
	border-width: 0.125em;
	border-style: solid;
	border-color: #d0d0d0;
	border-radius: 0.5em;
	color: #303030;
}
.control-slider__wrap {}
.control-slider--horizontal__wrap {
	padding: 2em 1.375em;
}
.control-slider--vertical__wrap {
	padding: 1.375em 2em;
	height: 100%;
}
.control-slider__track {
	position: relative;
	z-index: 1;
	/* // border: '1px solid black', */
	border-width: 0.0625em;
	border-style: solid;
	border-color: #a0a0a0;
	border-radius: 1px;
}
.control-slider__track-actual {
	position: relative;
	width: 100%;
}
.control-slider__bt {
	position: absolute;
}
.control-slider--horizontal__wrap .control-slider__bt {
	top: 0;
}
.control-slider--vertical__wrap .control-slider__bt {
	left: 0;
}
.control-slider__bt-single {}
.control-slider__bt-min {}
.control-slider__bt-max {}
.control-slider__bt-handle {
	position: absolute;
	top: 0;
	width: 2.25em;
	height: 2.25em;
	font-size: 0.625em;
	line-height: 1.875em;
	text-align: center;
	color: #f0f0f0;
	background: #2080f0;
	border-width: 0.1875em;
	border-style: solid;
	border-color: #2080f0;
	/* // border: '2px solid red', */
	border-radius: 100%;
	transform: translateY(-50%);
	cursor: pointer;
}
.control-slider--horizontal__wrap .control-slider__bt-single .control-slider__bt-handle {
	left: 0;
	transform: translate(-50%, -50%);
}
.control-slider--horizontal__wrap .control-slider__bt-min .control-slider__bt-handle {
	right: 0;
}
.control-slider--horizontal__wrap .control-slider__bt-max .control-slider__bt-handle {
	left: 0;
}
.control-slider--vertical__wrap .control-slider__bt-single .control-slider__bt-handle {
	left: 0;
	top: 0;
	transform: translate(-50%, -50%);
}
.control-slider--vertical__wrap .control-slider__bt-min .control-slider__bt-handle {
	right: auto;
	top: auto;
	bottom: 0;
}
.control-slider--vertical__wrap .control-slider__bt-max .control-slider__bt-handle {
	left: 0;
	top: 0;
}
.control-slider__range {
	position: absolute;
	background: #2080f0;
}
.control-slider--horizontal__wrap .control-slider__range {
	top: -0.25em;
	height: 0.5em;
}
.control-slider--vertical__wrap .control-slider__range {
	left: -0.25em;
	width: 0.5em;
}
.control-slider__ticks {
	display: flex;
	flex-wrap: nowrap;
	justify-content: space-between;
	align-items: flex-start;
}
.control-slider--horizontal__ticks {
	width: 100%;
	height: 0;
	flex-direction: row;
}
.control-slider--vertical__ticks {
	width: 0;
	height: 100%;
	flex-direction: column;
}
.control-slider__ticks--start {}
.control-slider__ticks--end {}
.control-slider__ticks-item--big2 {}
.control-slider__ticks-item--big1 {}
.control-slider__ticks-item {
	position: relative;
}
.control-slider--horizontal__ticks > .control-slider__ticks-item {
	width: 0;
}
.control-slider--horizontal__ticks.control-slider__ticks--start > .control-slider__ticks-item {
	bottom: 0.75em;
}
.control-slider--horizontal__ticks.control-slider__ticks--end > .control-slider__ticks-item {
	top: 0.75em;
}
.control-slider__ticks-item__mark {
	position: absolute;
	background-color: black;
}
.control-slider--horizontal__ticks.control-slider__ticks--start > .control-slider__ticks-item > .control-slider__ticks-item__mark {
	bottom: 0;
}
.control-slider--horizontal__ticks.control-slider__ticks--end > .control-slider__ticks-item > .control-slider__ticks-item__mark {
	top: 0;
}
.control-slider--horizontal__ticks > .control-slider__ticks-item > .control-slider__ticks-item__mark {
	width: 1px;
	height: 0.3em;
}
.control-slider--horizontal__ticks > .control-slider__ticks-item--big1 > .control-slider__ticks-item__mark {
	width: 1px;
	height: 0.55em;
}
.control-slider--horizontal__ticks > .control-slider__ticks-item--big2 > .control-slider__ticks-item__mark {
	width: 1px;
	height: 0.8em;
}
.text-transparent { color: transparent; }

.text-primary-0 { color: #020033; }
.text-primary-10 { color: #030066; }
.text-primary-20 { color: #050099; }
.text-primary-30 { color: #0700CC; }
.text-primary-40 { color: #3A33FF; }
.text-primary-50 { color: #5F59FF; }
.text-primary-60 { color: #6B66FF; }
.text-primary-70 { color: #9C99FF; }
.text-primary-80 { color: #CECCFF; }
.text-primary-90 { color: #E6E5FF; }
.text-primary-100 { color: #F2F1FF; }

.text-error-0 { color: #410E0B; }
.text-error-10 { color: #601410; }
.text-error-20 { color: #8C1D18; }
.text-error-30 { color: #B3261E; }
.text-error-40 { color: #DC362E; }
.text-error-50 { color: #E46962; }
.text-error-60 { color: #EC928E; }
.text-error-70 { color: #F2B8B5; }
.text-error-80 { color: #F9DEDC; }
.text-error-90 { color: #FCEEEE; }

.text-alert-0 { color: #5C5300; }
.text-alert-10 { color: #857700; }
.text-alert-20 { color: #AD9C00; }
.text-alert-30 { color: #D6C100; }
.text-alert-40 { color: #FFE500; }
.text-alert-50 { color: #FFEA2E; }
.text-alert-60 { color: #FFEE5C; }
.text-alert-70 { color: #FFF38A; }
.text-alert-80 { color: #FFF8B7; }
.text-alert-90 { color: #FFFCE5; }

.text-success-0 { color: #11592C; }
.text-success-10 { color: #1F7B42; }
.text-success-20 { color: #339D5B; }
.text-success-30 { color: #4ABF77; }
.text-success-40 { color: #67E195; }
.text-success-50 { color: #85FFB4; }
.text-success-60 { color: #A0FFC4; }
.text-success-70 { color: #BCFFD5; }
.text-success-80 { color: #D7FFE6; }
.text-success-90 { color: #F3FFF7; }

.text-neutral-0 { color: #000000; }
.text-neutral-10 { color: #1D1B20; }
.text-neutral-20 { color: #3D3D3D; }
.text-neutral-30 { color: #666666; }
.text-neutral-40 { color: #808080; }
.text-neutral-50 { color: #969696; }
.text-neutral-60 { color: #B4B4B4; }
.text-neutral-70 { color: #CCCCCC; }
.text-neutral-80 { color: #E9E9E9; }
.text-neutral-90 { color: #F4F4F4; }
.text-neutral-100 { color: #FFFFFF; }

.bg-transparent { background-color: transparent; }

.bg-primary-0 { background-color: #020033; }
.bg-primary-10 { background-color: #030066; }
.bg-primary-20 { background-color: #050099; }
.bg-primary-30 { background-color: #0700CC; }
.bg-primary-40 { background-color: #3A33FF; }
.bg-primary-50 { background-color: #5F59FF; }
.bg-primary-60 { background-color: #6B66FF; }
.bg-primary-70 { background-color: #9C99FF; }
.bg-primary-80 { background-color: #CECCFF; }
.bg-primary-90 { background-color: #E6E5FF; }
.bg-primary-100 { background-color: #F2F1FF; }

.bg-error-0 { background-color: #410E0B; }
.bg-error-10 { background-color: #601410; }
.bg-error-20 { background-color: #8C1D18; }
.bg-error-30 { background-color: #B3261E; }
.bg-error-40 { background-color: #DC362E; }
.bg-error-50 { background-color: #E46962; }
.bg-error-60 { background-color: #EC928E; }
.bg-error-70 { background-color: #F2B8B5; }
.bg-error-80 { background-color: #F9DEDC; }
.bg-error-90 { background-color: #FCEEEE; }

.bg-alert-0 { background-color: #5C5300; }
.bg-alert-10 { background-color: #857700; }
.bg-alert-20 { background-color: #AD9C00; }
.bg-alert-30 { background-color: #D6C100; }
.bg-alert-40 { background-color: #FFE500; }
.bg-alert-50 { background-color: #FFEA2E; }
.bg-alert-60 { background-color: #FFEE5C; }
.bg-alert-70 { background-color: #FFF38A; }
.bg-alert-80 { background-color: #FFF8B7; }
.bg-alert-90 { background-color: #FFFCE5; }

.bg-success-0 { background-color: #11592C; }
.bg-success-10 { background-color: #1F7B42; }
.bg-success-20 { background-color: #339D5B; }
.bg-success-30 { background-color: #4ABF77; }
.bg-success-40 { background-color: #67E195; }
.bg-success-50 { background-color: #85FFB4; }
.bg-success-60 { background-color: #A0FFC4; }
.bg-success-70 { background-color: #BCFFD5; }
.bg-success-80 { background-color: #D7FFE6; }
.bg-success-90 { background-color: #F3FFF7; }

.bg-neutral-0 { background-color: #000000; }
.bg-neutral-10 { background-color: #1D1B20; }
.bg-neutral-20 { background-color: #3D3D3D; }
.bg-neutral-30 { background-color: #666666; }
.bg-neutral-40 { background-color: #808080; }
.bg-neutral-50 { background-color: #969696; }
.bg-neutral-60 { background-color: #B4B4B4; }
.bg-neutral-70 { background-color: #CCCCCC; }
.bg-neutral-80 { background-color: #E9E9E9; }
.bg-neutral-90 { background-color: #F4F4F4; }
.bg-neutral-100 { background-color: #FFFFFF; }

.border-transparent { border-color: transparent; }

.border-primary-0 { border-color: #020033; }
.border-primary-10 { border-color: #030066; }
.border-primary-20 { border-color: #050099; }
.border-primary-30 { border-color: #0700CC; }
.border-primary-40 { border-color: #3A33FF; }
.border-primary-50 { border-color: #5F59FF; }
.border-primary-60 { border-color: #6B66FF; }
.border-primary-70 { border-color: #9C99FF; }
.border-primary-80 { border-color: #CECCFF; }
.border-primary-90 { border-color: #E6E5FF; }
.border-primary-100 { border-color: #F2F1FF; }

.border-error-0 { border-color: #410E0B; }
.border-error-10 { border-color: #601410; }
.border-error-20 { border-color: #8C1D18; }
.border-error-30 { border-color: #B3261E; }
.border-error-40 { border-color: #DC362E; }
.border-error-50 { border-color: #E46962; }
.border-error-60 { border-color: #EC928E; }
.border-error-70 { border-color: #F2B8B5; }
.border-error-80 { border-color: #F9DEDC; }
.border-error-90 { border-color: #FCEEEE; }

.border-alert-0 { border-color: #5C5300; }
.border-alert-10 { border-color: #857700; }
.border-alert-20 { border-color: #AD9C00; }
.border-alert-30 { border-color: #D6C100; }
.border-alert-40 { border-color: #FFE500; }
.border-alert-50 { border-color: #FFEA2E; }
.border-alert-60 { border-color: #FFEE5C; }
.border-alert-70 { border-color: #FFF38A; }
.border-alert-80 { border-color: #FFF8B7; }
.border-alert-90 { border-color: #FFFCE5; }

.border-success-0 { border-color: #11592C; }
.border-success-10 { border-color: #1F7B42; }
.border-success-20 { border-color: #339D5B; }
.border-success-30 { border-color: #4ABF77; }
.border-success-40 { border-color: #67E195; }
.border-success-50 { border-color: #85FFB4; }
.border-success-60 { border-color: #A0FFC4; }
.border-success-70 { border-color: #BCFFD5; }
.border-success-80 { border-color: #D7FFE6; }
.border-success-90 { border-color: #F3FFF7; }

.border-neutral-0 { border-color: #000000; }
.border-neutral-10 { border-color: #1D1B20; }
.border-neutral-20 { border-color: #3D3D3D; }
.border-neutral-30 { border-color: #666666; }
.border-neutral-40 { border-color: #808080; }
.border-neutral-50 { border-color: #969696; }
.border-neutral-60 { border-color: #B4B4B4; }
.border-neutral-70 { border-color: #CCCCCC; }
.border-neutral-80 { border-color: #E9E9E9; }
.border-neutral-90 { border-color: #F4F4F4; }
.border-neutral-100 { border-color: #FFFFFF; }

/** Tabler css fix */
.btn {
	--tblr-btn-hover-color: var(--tblr-body-color);
}
/** End Tables css fix */

body {
	background: linear-gradient(180deg, #0042, #0042), linear-gradient(0deg, var(--bs-body-bg, var(--tblr-body-bg)), var(--bs-body-bg, var(--tblr-body-bg)));
}

.page-width {
	margin: 0 auto;
	width: 90em;
	max-width: calc(100vw - 4em);
}

.border-dotted {
	border-style: dotted;
}
.border-dashed {
	border-style: dashed;
}
.border-dashed.border-dark {
	border-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='%23333' stroke-width='24' stroke-dasharray='24%2c 62' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e") 12 / 3px / 0px repeat;
}
.border-dashed.border-success {
	border-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='%230a0' stroke-width='24' stroke-dasharray='24%2c 62' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e") 12 / 3px / 0px repeat;
}
.border-dashed.border-danger {
	border-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='%23c11' stroke-width='24' stroke-dasharray='24%2c 62' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e") 12 / 3px / 0px repeat;
}
.cursor-pointer {
	cursor: pointer;
}
.pointer-events-all {
	pointer-events: all !important;
}
.overflow-visible {
	overflow: visible !important;
}

.w-e1 {
	width: 1em;
}
.w-e1-5 {
	width: 1.5em;
}
.w-e8 {
	width: 8em;
}
.w-e12 {
	width: 12em;
}
.w-e16 {
	width: 16em;
}
.w-15div100 {
	width: calc(15% - 1em);
}
.h-e1 {
	height: 1em;
}
.h-e1-5 {
	height: 1.5em;
}
.w-maxc {
	width: max-content;
}
.w-fitc {
	width: fit-content;
}
.max-w-e12 {
	max-width: 12em;
}
.max-w-e16 {
	max-width: 16em;
}
.max-w-e18 {
	max-width: 18em;
}
.max-w-full {
	max-width: 100%;
}
.left-0 {
	left: 0 !important;
}
.right-0 {
	right: 0 !important;
}
.left-1s2 {
	left: 50%;
}
.pl-1o2 {
	padding-left: 0.125em;
}
.pr-1o2 {
	padding-right: 0.125em;
}
.flex-grow-4 {
	flex-grow: 4 !important;
}
.flex-grow-50 {
	flex-grow: 50 !important;
}
.flex-shrink-4 {
	flex-shrink: 4 !important;
}
.flex-shrink-8 {
	flex-shrink: 8 !important;
}
.flex-shrink-16 {
	flex-shrink: 16 !important;
}
.fs-5o8 {
	font-size: 0.625em;
}
.fs-3o4 {
	font-size: 0.75em;
}
.fs-7o8 {
	font-size: 0.875em;
}
.lead-1 {
	line-height: 1 !important;
}
.text-left {
	text-align: left !important;
}
.text-center {
	text-align: center !important;
}
.text-right {
	text-align: right !important;
}
.opacity-50 {
	opacity: 0.5;
}
.opacity-65 {
	opacity: 0.65;
}
.opacity-75 {
	opacity: 0.75;
}
.-translate-x-1s2 {
	transform: translateX(-50%);
}
.z-index-100 {
	z-index: 100;
}
.z-index-1000 {
	z-index: 1000;
}
.tsit-opacity-500 {
	transition: opacity .5s;
}
.shadow-strong {
	box-shadow: 0 0.5em 1em #00000060;
}
.text-link-over-secondary {
	color: #91bbed;
	text-decoration: underline;
}

.form-floating > .form-control-ata-answer-text {
	height: calc(5.75rem + calc(var(--tblr-border-width) * 2));
}

.form-select-plaintext:disabled {
	/* background-color: transparent; */
	padding-left: 0;
	color: var(--bs-body-color);
	background: none;
	border-style: none;
}
.form-floating .form-select-plaintext:disabled {
	padding-left: 0.75rem;
}
.form-check-label.stretched-link::after {
	display: none;
}

.jb-toast-container {
	bottom: 5em;
	right: 5em;
	max-width: calc(100vw - 10em);
	max-height: calc(100vh - 10em);
}
@media only screen and (max-width: 575px) {
	.jb-toast-container {
		bottom: 2em;
		right: 2em;
		max-width: calc(100vw - 4em);
		max-height: calc(100vh - 4em);
	}
}

.modal-dialog,
.modal-content {
	pointer-events: all !important;
}
.modal-dialog__content {
	width: 70em;
	max-width: calc(100vw - 2em);
}
.modal-dialog__content--sm {
	width: 24em;
	max-width: calc(100vw - 2em);
}

/* Fix for Bootswatch theme Sketchy */
.btn-close::before {
	position: static;
}

.nav-item {
	display: flex;
	align-items: stretch;
}
.nav-link {
	cursor: pointer;
}
.btn-default.nav-link {
	font-weight: 600;
	color: var(--tblr-body-color);
}

.form-switch .form-check-input {
	/* --tblr-form-switch-bg: url(/img/input-switch-button.svg); */
	background-color: #808080;
}
.form-switch .form-check-input:checked {
	background-color: #3A33FF;
}

.chat-list {
	display: flex;
	flex-flow: row wrap;
	justify-content: center;
	align-items: flex-start;
}
.chat-list-card {
	margin: 0 1em 4em;
	min-width: 20em;
}
.chat-list__header {
	display: flex;
	flex-flow: row wrap;
	justify-content: space-between;
	align-items: baseline;
	margin: 0 0 1em;
}

.caf-group-kedata .bg-secondary dl dt {
	display: inline-block;
	color: lightgray;
}

.caf-group-kedata .dl-block {
	display: flex;
	flex-flow: row nowrap;
	justify-content: flex-start;
	align-items: flex-start;
	gap: 0.5em;
}
.caf-group-kedata .dl-block > dt {
	flex: 0 0 auto;
}
.caf-group-kedata .dl-block > dd {
	flex: 1 1 auto;
}

.caf-group-quality-relevance.dl-block {
	margin: 0 auto 0.5em;
}

/* .caf-group-quality-review dd > input[type="checkbox"] {
	width: 1.25em;
	height: 1.25em;
} */

/* ── Agent queue (stage 1 of the console redesign) ──────────────────
   One row per session, scannable. Density is the point: the current
   console shows three or four conversations at a time because each is a
   full chat client. These rows are ~52px, so twenty fit on a laptop and
   the list stays readable on a phone. */

/* Wider overall, because the point is to fit the list AND one or more
   conversations side by side. */
.queue-page { max-width: 100%; margin: 0; padding: 1rem 1rem 3rem; }

.queue-head { display: flex; align-items: baseline; gap: .75rem; margin-bottom: .75rem; }
.queue-head h2 { margin: 0; font-size: 1.25rem; }
.queue-total { font-size: .8rem; color: #6b7280; font-variant-numeric: tabular-nums; }
/* ⚠ Smaller than the button beside it — Johan, 2026-08-26. It is the ANSWER to
   Refresh, not a control: read once after pressing, ignored the rest of the
   time. At the row's default size it competed with the thing it reports on. */
.queue-fetched { color: #9ca3af; font-size: .68rem; }

/* (The original .queue-filters / .queue-chip block lived here. Superseded by
   the filter-bar section further down — one chip style for every row.) */

/* ⚠ A LITTLE more air, not a lot — Johan, 2026-08-26. The ground does most of
   the separating now; the gap only has to stop the cards touching. */
.queue-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .28rem; }

/* FIXED COLUMNS, like the agent table. A flex row let every cell size itself,
   so nothing lined up between rows and the eye had to re-find each field on
   every line. Now: wait · identity and preview · meta, all aligned down the
   list. */
/* ⚠ THREE STACKED ROWS, each a flex line — Johan, 2026-08-21. The old
   two-column grid sized its cells from the content, so nothing lined up between
   one row and the next and the eye had to re-find each field on every line.
   Every fact now has one place, at the same x, down the whole list. */
.queue-row {
  width: 100%; display: flex; flex-direction: column;
  gap: .12rem; text-align: left;
  background: #fff; border: 1px solid #e5e7eb; border-radius: .3rem;
  padding: .35rem .6rem; cursor: pointer; font: inherit; min-height: 46px;
}
/*
 * ⚠ HOVER HAS TO BE FELT — Johan, 2026-08-21: "on mouse over on session card
 * need stronger ux feedback, perhaps thicker border".
 *
 * It was #c7d2fe on #fbfcff: a border you had to look for and a background one
 * step off white. On a list of near-identical cards that is no feedback at all.
 *
 * ⚠ The thicker edge is an INSET SHADOW, not a wider border. A border going
 * 1px → 2px changes the content box, so every line in the card shifts a pixel
 * as the pointer crosses it and the whole list twitches. The shadow paints
 * inside the border and costs no layout, so nothing moves.
 *
 * The 3px left stripe survives it: the ring is drawn inside the border box, so
 * red-waiting and violet-owed still read while hovered.
 */
.queue-row:hover {
  border-color: #94a3b8; background: #f4f7fc;
  box-shadow: inset 0 0 0 1px #94a3b8;
}
/* An open card keeps its blue identity while hovered — it just gets the ring
   too. Without this, hovering the card you are already reading looked like
   hovering nothing. */
.queue-row.is-open:hover {
  border-color: #94a3b8; background: #d6def9;
  box-shadow: inset 0 0 0 1px #94a3b8;
}
/* ⚠ THE PRESS. A click must never produce zero visual change before the pane
   appears — see the interaction-responsiveness rule in the frontend docs. */
.queue-row:active { background: #e6edfa; }
/*
 * ⛔ OPEN = A WASH AND A BAR ON THE RIGHT, not a blue frame — Johan,
 * 2026-09-15: "a thin blue border is probably not enough ... avoid that frame
 * that is visually a bit confusing". The row keeps its neutral border and
 * gets the tab strip's indigo as a pale ground plus a 3px bar on its RIGHT
 * edge, pointing at the panes it is open in. The left edge stays with the
 * red-waiting and violet-owed stripes, which say something else.
 *
 * Solid bar = pinned open. Dashed bar = the PREVIEW, the one the next click
 * replaces — the difference has to be visible BEFORE clicking, or pinning is
 * guesswork. A pseudo-element, not a border, so the row's box never shifts.
 */
.queue-row { position: relative; }
/* Two steps deeper than the first cut (#eef2ff) — Johan, 2026-09-15: "I do not really see that". */
.queue-row.is-open { background: #e0e7ff; }
.queue-row.is-open::after {
  content: ''; position: absolute; top: 0; bottom: 0; right: 0; width: 3px;
  background: #4f46e5; border-radius: 0 .3rem .3rem 0; pointer-events: none;
}
.queue-row.is-preview::after {
  background: repeating-linear-gradient(to bottom, #4f46e5 0 6px, transparent 6px 10px);
  border-radius: 0;
}
.queue-row:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 1px; }

/* ── The three lines of a session row ──────────────────────────────────── */
.qr-line { display: flex; align-items: center; gap: .35rem; min-width: 0; width: 100%; }
/* Pushes whatever follows to the far right. */
.qr-push { flex: 1 1 auto; min-width: .35rem; }

/*
 * AGENT PRESENCE — see console/AgentDot.tsx for what each state means.
 *
 * ⛔ ONE SCHEME ON EVERY PAGE — Johan, 2026-09-14: "use same color for the
 * different availabilities on all pages: red - not taking chats; grey - not
 * available; green - focused; not filled green - multitasking". Before this
 * the console dot, the Agent Overview column and the Agent Activity strip each
 * had their own palette (amber here, red there, yellow for multitasking on one
 * page and a green ring on another).
 *
 * The colours are variables so the pages that paint bars and swatches inline
 * (Agent Activity) read the same values; a page that redefines them is the
 * drift this exists to stop. `--presence-green-fill` is the pale green a BAR
 * uses for multitasking, where a hollow ring is not possible.
 *
 * ⚠ FILLED vs HOLLOW still carries the focused/multitasking distinction: at
 * 8px two greens are a guess; a filled circle beside an empty one is not.
 */
:root {
  --presence-green: #22c55e;
  --presence-green-fill: #bbf7d0;
  --presence-red: #ef4444;
  --presence-grey: #9ca3af;
}
.ag-dot {
  display: inline-block; flex: 0 0 auto;
  width: .5rem; height: .5rem; border-radius: 999px;
  margin-right: .3rem; vertical-align: baseline;
}
.ag-dot-available { background: var(--presence-green); }
.ag-dot-away      { background: transparent; box-shadow: inset 0 0 0 1.5px var(--presence-green); }
.ag-dot-busy      { background: var(--presence-red); }
.ag-dot-offline   { background: var(--presence-grey); }
/* ⚠ Assigned to a user who no longer exists. Wider than the others because it
   carries a glyph — this one is meant to be noticed, not scanned past. */
.ag-dot-unknown {
  width: auto; height: auto; min-width: .8rem; padding: 0 .15rem;
  background: #fef3c7; color: #b45309;
  font-size: .6rem; font-weight: 700; line-height: 1.1;
  text-align: center; border-radius: 999px;
}

/* Presence, first on the row and ALWAYS occupying its slot — see the markup.
   Empty when nobody is there, so every id below it still starts at the same x. */

.qr-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .78rem; font-weight: 600; color: #111827; flex: 0 0 auto;
}
/* ⚠ SHRINKS AND CLIPS. A long address must not push the row taller or the
   column wider — "if email does not fit then cut it off". */
.qr-email {
  font-size: .78rem; color: #374151;
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Fixed, so every pill after it starts at the same x down the list. */
.qr-ago {
  /* Room for "not replying 59m" — the word is the point (Johan, 2026-09-08). */
  flex: 0 0 6.8rem; font-size: .7rem; color: #6b7280;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* The same weights as the header's badge — see .cv-waiting. No colour: the
   row already carries urgency in violet and red. */
.qr-ago-waiting.is-aging { color: #4b5563; }
.qr-ago-waiting.is-overdue { color: #b45309; font-weight: 600; }
.qr-ago-noreply { color: #9ca3af; }
/*
 * ⚠ NOT UPPERCASE — Johan, 2026-08-23: "for the urgency pills in the session
 * list, do not use capital letters".
 *
 * Shouting a word the reader already knows costs legibility and buys nothing:
 * "IMPORTANT" is slower to read than "Important" and pulls harder than a
 * descriptive pill should. The CHANNEL pill keeps its caps — "WEB" and "SMS"
 * are abbreviations, where caps are how they are written anyway.
 */
.qr-state {
  display: inline-flex; align-items: center; line-height: 1;
  font-size: .66rem; font-weight: 700; border-radius: 999px; padding: .2rem .45rem;
  letter-spacing: .01em;
}
/*
 * ⚠ SOLID, AND THIS REVERSES AN EARLIER RULE ON PURPOSE — Johan, 2026-08-24:
 * "the pills in the session cards in list and in ACI must also be louder, and
 * aligned with selection buttons".
 *
 * They used to be tint + coloured ink + a 1px edge, deliberately "one weight
 * quieter than the tabs", on the reasoning that a tab is navigation and should
 * pull while a row pill merely describes its row. In practice that made the
 * one thing you scan a list FOR — which bucket is this in — the faintest mark
 * on the card, and it no longer matched the tab it corresponds to.
 *
 * So a pill now looks like its tab does when SELECTED: filled, white, the same
 * token. One colour per state, one appearance per colour, and the row answers
 * from across the screen instead of on inspection.
 *
 * ⚠ Every fill carries white at 4.8:1 or better (red 5.9, orange 4.8, green
 * 4.9, bot blue 6.4, action violet 8.6). Check any new state against that
 * before adding it — a pale fill here would be unreadable, not just quiet.
 */
.qr-state-urgent    { background: var(--st-urgent-fill);    color: var(--st-urgent-ink); }
.qr-state-important { background: var(--st-important-fill); color: var(--st-important-ink); }
.qr-state-replied   { background: var(--st-replied-fill);   color: var(--st-replied-ink); }
/* ⚠ BLUE, NOT VIOLET — Johan, 2026-08-21. Violet now means "an agent must do
   something", and auto is the state where nobody has to. Wearing the action
   colour, the quietest bucket in the queue was shouting the loudest. Blue is
   the bot's colour in the transcript, so this now matches the bubbles it
   describes. */
.qr-state-bot       { background: var(--st-bot-fill); color: var(--st-bot-ink); }
/* ⚠ HISTORY STAYS QUIET while the other four went solid — it is the one state
   that is not work, and the same reason the tabs only badge Urgent and
   Important. Filling this too would spend attention on the rows that need
   none, and five shouting pills is the same as none. */
.qr-state-history   { background: #e5e7eb; color: #4b5563; box-shadow: inset 0 0 0 1px #d4d8dd; }
/* Done and Archived on the same pill as the buckets — Johan, 2026-09-15. Done quiet green (the agent finished), Archived darker grey (written off). */
.qr-state-done      { background: #e7f5ec; color: #15803d; box-shadow: inset 0 0 0 1px #cbe9d6; }
.qr-state-archived  { background: #d1d5db; color: #374151; box-shadow: inset 0 0 0 1px #b9c2cc; }
/* ⚠ `.cv-pill.sc-*` too: `.cv-pill` sets white ink and is declared LATER in this
   file, so a plain `.sc-done` lost the cascade and the pane showed white on
   pale green — Johan, 2026-09-15: "make sure all pills are readable". */
.sc-done, .cv-pill.sc-done             { background: #e7f5ec; color: #15803d; box-shadow: inset 0 0 0 1px #cbe9d6; }
.sc-archived, .cv-pill.sc-archived     { background: #d1d5db; color: #374151; box-shadow: inset 0 0 0 1px #b9c2cc; }
.qr-line3 { margin-top: .05rem; }

/* ── A row that has moved out of this view ─────────────────────
 *
 * ⚠ FADED, NOT HIDDEN, and it must not look clickable-but-broken. The row is
 * still openable — it is a real session, it just lives somewhere else now —
 * so this dims rather than disables. The stripe goes neutral because whatever
 * the row was owed, it is not owed HERE any more.
 */
.queue-row.is-moved { opacity: .55; }
.queue-row.is-moved::before { background: #cbd5e1 !important; }
.qr-moved {
  display: flex; align-items: baseline; gap: .35rem;
  font-size: .64rem; font-weight: 700; letter-spacing: .02em;
  color: #475569; text-transform: uppercase;
}
/* Says what to DO about it, quietly. Without this the note reads as a state the
   row is stuck in rather than something the agent can clear. */
.qr-moved-hint { font-weight: 500; text-transform: none; color: #94a3b8; letter-spacing: 0; }
/* Fixed, and RESERVED when empty — the message must start at the same x
   whether or not the language is known. */
.qr-lang-slot { flex: 0 0 2.1rem; display: flex; align-items: center; }


/* "An agent has been in here" — history, not work. Grey and quiet on purpose:
   the two pills on this row that ARE work must win the eye. */
/*
 * ⚠ BIG AND CLEAR, both places — Johan, 2026-08-23. This is not a descriptive
 * pill like the channel or the bucket; it is an agent's judgement that the work
 * is finished, and it answers the question the list is scanned for. Solid
 * green, the same green the transfer mark uses for "this happened".
 */
.qr-done {
  flex: 0 0 auto; font-size: .66rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  background: #15803d; color: #fff; border-radius: 999px;
  padding: .1rem .5rem;
}
/* Outlined, not solid: Done is a conclusion and shouts; Reopened is a footnote
   explaining why a session is back. See the row markup. */
/* Written off. Deliberately DULLER than Done's green: nothing was achieved
   here, and a badge that looked like an accomplishment would be a small lie
   told on every row. */
.qr-archived {
  flex: 0 0 auto; font-size: .66rem; font-weight: 600;
  background: #55606e; color: #fff; border-radius: 999px; padding: .1rem .5rem;
}

.qr-reopened {
  flex: 0 0 auto; font-size: .62rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em;
  color: #4b5563; background: #fff; border-radius: 999px;
  padding: .05rem .45rem; box-shadow: inset 0 0 0 1px #cbd5e1;
}

/* Your own pin on a list row — see the Pinned lookup. Quiet: it marks a chat
   you already chose, it is not asking to be clicked. */
.qr-pinned {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.15rem; height: 1.15rem; border-radius: 999px; flex: 0 0 auto;
  background: #eef2ff; color: #4f46e5;
}
.qr-touched {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.15rem; height: 1.15rem; border-radius: 999px; flex: 0 0 auto;
  background: #f3f4f6; color: #6b7280;
}

/* ⚠ VIOLET, and the only violet on the row — Johan, 2026-08-21. It is the one
   thing here that is somebody's TASK rather than a description of the
   conversation, and it must not be mistaken for the old transfer reason it
   replaces. Truncates: the message beside it is what the row is for. */
.qr-action {
  flex: 0 1 auto; min-width: 0; max-width: 14rem;
  font-size: .64rem; font-weight: 600;
  background: #ede9fe; color: #5b21b6; border-radius: 999px;
  padding: .05rem .4rem; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

.queue-main { min-width: 0; display: flex; flex-direction: column; gap: .1rem; }
.queue-who { font-size: .85rem; font-weight: 600; display: flex; align-items: center; gap: .35rem; }
.queue-live {
  font-size: .62rem; text-transform: uppercase; letter-spacing: .04em;
  background: #dcfce7; color: #15803d; border-radius: 999px; padding: .05rem .35rem;
}
.queue-flag {
  font-size: .62rem; text-transform: uppercase; letter-spacing: .04em;
  background: #fef3c7; color: #b45309; border-radius: 999px; padding: .05rem .35rem;
}
/* Quiet, monospace, secondary — findable when you are looking for it, ignorable
   when you are not. */
.queue-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .68rem; color: #9ca3af; font-weight: 400;
}
/* Up to TWO rows, then clipped — Johan, 2026-08-20. One line lost the end of
   most questions; unbounded would let a long paste own the whole list. */
/*
 * ⛔ CONTENT, NOT METADATA — Johan, 2026-08-25: "for clarity, makes sense to
 * have different font or something for the last message and the other content
 * on the cards?"
 *
 * Yes, and it is the one thing on the card that is somebody's WORDS. Everything
 * else — the id, the pills, the channel, the age — is the console describing
 * the row. Set the same way, the message read as one more field and the eye
 * skimmed past the only part that says what the conversation is about.
 *
 * A SERIF at the same size does it without a font file (Georgia and its
 * fallbacks ship everywhere) and without shouting: prose looks like prose, UI
 * looks like UI, and the distinction survives at 12px where a weight change
 * would not. Darker too — quoted speech should be the most readable thing in
 * the row, not the faintest.
 *
 * ⚠ NOT italic. It is the obvious reach for "quoted", and it costs legibility
 * on a two-line clamp of somebody else's punctuation and product names.
 */
.queue-preview {
  font-family: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  font-size: .78rem; color: #29313d;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
/*
 * The speaker stays UI: it is OUR label on their words, not part of them.
 * ⚠ `inherit` would have taken the serif from `.queue-preview`, which is the
 * parent — the whole point is that this one is not prose.
 */
.queue-from {
  font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-size: .72rem; color: #9ca3af;
}
.queue-nopreview { color: #d1d5db; font-style: italic; }

/* One line, not three. Stacking these tripled the row height and cost the
   density that is the whole argument for a list. */
/* The meta column is itself a grid, so "58m ago", the owner and the channel sit
   under each other down the list instead of drifting with name length. */
.queue-meta {
  display: grid; grid-template-columns: 4.2rem minmax(6rem, auto);
  align-items: baseline; gap: .45rem; font-size: .7rem; white-space: nowrap;
  justify-items: end;
}
.queue-by { font-weight: 600; }
/*
 * WHO HAS IT — one hue, one meaning, and the same hues the bubbles use, so the
 * card and the conversation it opens agree.
 *
 *   blue    the bot has it            (matches .cv-from-bot)
 *   teal    YOU have it               (matches .cv-from-me)
 *   slate-teal  another agent has it  (matches .cv-from-agent)
 *   red     nobody has it
 *
 * ⚠ Both agents are teal on purpose: "a human has this" is the first thing to
 * read, and BRIGHTNESS says whether that human is you. Two unrelated hues would
 * have made your own sessions no easier to find than anyone else's.
 */
/*
 * ⚠ WEIGHT IS THE SECOND AXIS — Johan, 2026-08-21. Colour says WHO has it;
 * weight says whether anybody has to move.
 *
 *   bold    somebody must act: it is yours, or nobody has taken it
 *   normal  a statement of fact: a colleague has it, or the bot does
 *
 * Two axes on one word, and neither costs a pixel. It also means a page of
 * auto rows reads as flat grey text with your own sessions standing out of it,
 * which is what an agent scans the column for.
 */
.by-bot { color: #2f5da8; font-weight: 500; }
/* ⚠ #00857a, not the duller #0f766e it started as — Johan, 2026-08-21:
   "perhaps a stronger colour for you have it". More saturated so it carries
   against the violet beside it, and still 4.53:1 on white, so it is stronger
   without being less readable. */
.by-me { color: #00857a; font-weight: 600; }
.by-agent { color: #3d5a66; font-weight: 500; }
/*
 * ⚠ VIOLET, NOT RED — nobody has taken a chat that needs a human, which is the
 * queue's definition of work outstanding. Red leaves the who-label entirely
 * here, and is the better for it: it now means only "waiting on us" (the row
 * stripe) and "something failed" (a dead send).
 */
.by-unassigned { color: #7c3aed; font-weight: 600; }
/* ⚠ Unassigned WITHOUT anything owed — see the row markup. An auto chat an
   agent once touched is not work, so it drops to grey AND to normal weight:
   both axes say "nothing to do here". */
.queue-by.by-quiet { color: #8a94a3; font-weight: 500; }
.queue-solution { color: #9ca3af; }
/* A pill now, sitting with the reason rather than in the meta column — the two
   are read together when deciding whether a conversation is yours. Quiet: it is
   context, not a flag. */
.queue-channel {
  font-size: .62rem; text-transform: uppercase; letter-spacing: .04em;
  background: #f3f4f6; color: #6b7280; border-radius: 999px; padding: .05rem .35rem;
  font-weight: 500;
}

/* Centred ON THE FILTER BAR, not on the column — Johan, 2026-08-19.
   ⚠ The column runs the full width of the page, so centring in it put the
   message far to the right of everything else on screen and it read as
   floating loose. Bounding the box to the bar's width and keeping it flush
   left means "centre" lands between the first and last filter, which is where
   the eye already is.

   Italic because it is a statement about the ABSENCE of content, not content. */
.queue-empty {
  width: 100%; max-width: var(--queue-bar-width); min-height: 14rem;
  margin-right: auto;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: .35rem;
  padding: 2rem 1rem; text-align: center;
  color: #6b7280; font-size: .875rem; font-style: italic;
}
.queue-empty p { margin: 0; }
.queue-error { color: #b91c1c; }
.queue-clear {
  margin-top: .5rem; border: 1px solid #d1d5db; background: #fff;
  border-radius: .3rem; padding: .35rem .7rem; font-size: .8rem; cursor: pointer;
}
/* Sits with the count and the load time — the three facts about freshness in
   one place, so the button is found where the doubt arises. */
/* ⚠ The Alerts button now sits first and .queue-head already supplies the gap,
   so the historical left margin would double it. Kept on the rule for the
   cases where Refresh IS first. */
.qs-wrap + .queue-refresh { margin-left: 0; }
.queue-refresh {
  margin-left: .6rem; border: 1px solid #d5dae2; background: #fff;
  border-radius: .3rem; padding: .12rem .55rem; font-size: .72rem;
  color: #374151; cursor: pointer; min-height: 24px;
  display: inline-flex; flex-direction: column; align-items: center; line-height: 1.15;
  /* Two lines tall in a baseline-aligned row: pin the TOP to the other
     buttons' top, or it hangs below them (Johan, 2026-09-07, screenshot). */
  align-self: flex-start;
}
/* The last-updated time rides inside the button (Johan, 2026-09-07). */
.queue-refresh-time { font-size: .58rem; color: #9ca3af; font-variant-numeric: tabular-nums; }
/* An old refresh colours the whole button — Johan, 2026-09-22. Amber from 5
   minutes, red from 30; with no live updates, amber at once and red at 5. */
.queue-refresh-time.is-aging { color: #b45309; }
.queue-refresh-time.is-old { color: #b4342b; font-weight: 600; }
.queue-refresh:has(.is-aging) { border-color: #f0c58a; background: #fdf3e3; color: #92620a; }
.queue-refresh:has(.is-aging):hover:not(:disabled) { border-color: #e2a94f; background: #fdeed2; }
.queue-refresh:has(.is-old) { border-color: #f3bdb8; background: #fdeceb; color: #b4342b; }
.queue-refresh:has(.is-old):hover:not(:disabled) { border-color: #e79b93; background: #fce0dd; }
.queue-notices-count {
  display: inline-block; margin-left: .35rem; min-width: 1.1rem; padding: 0 .3rem;
  border-radius: 999px; background: #4f46e5; color: #fff; font-size: .62rem;
  font-weight: 700; line-height: 1.1rem; text-align: center; vertical-align: 1px;
}
/* The one solution picker, in the navbar beside the brand — see Header.tsx. */
.jb-solsel {
  max-width: 14rem; margin-right: .75rem; padding: .2rem 1.6rem .2rem .5rem;
  font-size: .8rem; border-radius: .3rem; border: 1px solid rgba(255,255,255,.35);
  background: #fff; color: #1e293b;
}
@media (max-width: 575.98px) { .jb-solsel { max-width: 9.5rem; font-size: .74rem; } }
.queue-refresh:hover:not(:disabled) { border-color: #97a3b1; background: #f7f9fb; }
.queue-refresh:disabled { opacity: .6; cursor: default; }

.queue-note { margin-top: 1.5rem; font-size: .72rem; color: #9ca3af; text-align: center; }

/* Phone: the meta column moves under the preview rather than squeezing it. */
@media (max-width: 640px) {
  .queue-page { padding: .75rem .6rem 3rem; }
  /* Phone: the meta column would squeeze the preview to nothing, so it drops
     below rather than competing for the same line. */
  .queue-row { grid-template-columns: 2.4rem minmax(0, 1fr); gap: .5rem; }
  .queue-meta {
    grid-column: 2; grid-template-columns: repeat(3, auto);
    justify-items: start; gap: .4rem;
  }
}

/* ── Conversation pane (new console) ────────────────────────────────
   Not the shopper's widget. An agent-grade transcript: denser, events
   distinguishable from utterances, and a composer this app owns rather
   than a textarea living inside an embedded widget. */

/*
 * ⚠ NEVER WRAPS. It used to, as an anti-overflow measure — every pane stayed
 * reachable, but the ones that did not fit stacked DOWN THE PAGE below the
 * fold, with nothing on screen saying they were there. Johan, 2026-08-19: that
 * should never happen; what does not fit belongs in the "n more open" menu.
 *
 * How many fit is measured — see `paneFit.ts`. Nothing is put out of reach:
 * the overflow menu is somewhere the agent already looks, and a second row is
 * not.
 */
/*
 * ⚠ THE SHELL OWNS THE SCREEN, so the LIST scrolls and the page does not —
 * Johan, 2026-08-23: "when I scroll on the session list, only the session list
 * shall scroll, not the entire page".
 *
 * The phone layout was bounded like this on 2026-08-23 for the same reason and
 * the desktop was left alone, so scrolling the queue carried the whole document
 * — taking the views rail, the tabs, the filter bar and the conversation panes
 * off the top with it. Everything you need while reading the list disappears
 * the moment you read the list.
 *
 * `dvh` rather than `vh` for the same reason as on the phone: a browser with a
 * collapsing toolbar reports a larger `vh` than it actually shows.
 *
 * ⚠ `min-height: 0` all the way down, or the flex children refuse to shrink
 * below their content and the inner scroll never engages — the outer one just
 * comes back.
 */
.queue-shell {
  display: flex; flex-wrap: nowrap; gap: 1rem; align-items: stretch;
  /* ⚠ The shell's page area gives the height (flex: 1 1 auto; min-height: 0
     from `.shell-page > .queue-shell`); the old `calc(100dvh - 4rem)` was the
     navbar's height and left with it (2026-09-14). */
  min-height: 0; overflow: hidden;
}
.queue-page { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
.queue-body { min-height: 0; }
/*
 * ⚠ `align-self: stretch` IS WHAT MAKES THE LIST SCROLL — Johan, 2026-08-24:
 * "the scroll does not work".
 *
 * `.queue-body` sets `align-items: flex-start` so the views rail keeps its own
 * height instead of stretching down the page. That applied to this column too,
 * so it grew to its CONTENT rather than to the shell — measured on the deployed
 * page: body 697px, main column 1931px. The list surface then had
 * `overflow-y: auto` and a height equal to its own content, so there was
 * nothing for it to scroll; it simply overflowed and the shell clipped it.
 *
 * Bounding the shell was necessary and not sufficient: every ancestor between
 * the shell and the scroller has to pass the height down, and one `flex-start`
 * three levels up silently stops it.
 *
 * ⚠ On THIS column only, not by changing `align-items` on the body — the rail
 * should still size to its cards.
 */
.queue-main-col { display: flex; flex-direction: column; min-height: 0; align-self: stretch; }
/* ⚠ NO `overflow` on the views rail. It would clip the agent picker, which is
   absolutely positioned inside it — a dropdown you cannot see is worse than a
   rail that occasionally runs long, and the rail is five cards. */

/* The panes and their overflow menu, as one column so the menu sits ABOVE the
   row rather than beside it — a rail beside them competed for the very width
   that caused the overflow. */
.cv-stack { display: flex; flex-direction: column; gap: .35rem; min-width: 0; margin-top: .9rem; }
/* ⚠ THE PANES MUST NEVER REACH THE LIST. Johan, 2026-08-20, with a screenshot
   of three panes sitting on top of the session list.
   `min-width: 0` is the load-bearing part: a flex item defaults to
   `min-width: auto`, so this container refused to shrink below the sum of its
   panes' own minimums and simply overflowed to the LEFT, painting over the
   column beside it. With it, the row shrinks and `overflow: hidden` clips
   inside its own bounds instead of the neighbour's.
   The real defence is still `panesThatFit` — it decides how many open at all,
   so clipping should never be reached. This is the backstop. */
.cv-panes {
  display: flex; flex-wrap: nowrap; gap: 1rem; align-items: stretch;
  min-height: 0; min-width: 0; overflow: hidden;
  /* ⛔ TAKES THE ROOM THERE IS — Johan, 2026-09-15: "if the left menu is
     collapsed, use the available horizontal space to make the ACIs and/or the
     session list a bit wider ... in most cases they will keep the menu
     collapsed". The row grows into what the folded menu frees, and the panes
     inside it share that (see .cv-pane). Twice the list's weight: reading a
     conversation is what the width is for. */
  flex: 2 1 auto;
  /* Room for the grip, which now straddles each pane's TOP BORDER — Johan,
     2026-09-15. Without this the half above the edge is clipped. */
  padding-top: .5rem;
}

/* ── "n more open" ──────────────────────────────────────────────────────── */
/* Full width so the dropdown below it can be too — the BUTTON stays its own
   size, it is only the positioning context that spans. */
.cv-more { position: relative; align-self: stretch; }
.cv-more-btn {
  display: inline-flex; align-items: center; gap: .3rem;
  border: 1px solid #d5dae2; background: #fff; border-radius: .3rem;
  padding: .2rem .5rem; font-size: .72rem; font-weight: 600; color: #374151;
  cursor: pointer; min-height: 26px;
}
.cv-more-btn:hover, .cv-more-btn.is-on { border-color: #97a3b1; background: #f7f9fb; }
.cv-more-caret { font-size: .9rem; line-height: 1; color: #6b7280; }
/* ⚠ AS WIDE AS THE PANE ROW — Johan, 2026-08-20: "allow that select field to
   use all space above the ACIs". At 22rem the last message was clipped to a few
   words, which is the one field that tells conversations apart; the space above
   the panes is doing nothing else. */
.cv-more-list {
  position: absolute; top: calc(100% + .25rem); left: 0; z-index: 40;
  /* ⚠ A NARROW LIST, NOT THE WHOLE WIDTH — Johan, 2026-09-15: "open the n
     more ones in a narrower popup and line break the latest message". The
     message wraps to two lines instead (.cv-more-msg). */
  width: 28rem; max-width: calc(100vw - 2rem); max-height: 28rem; overflow-y: auto;
  display: flex; flex-direction: column; gap: .1rem; padding: .25rem;
  background: #fff; border: 1px solid #d5dae2; border-radius: .4rem;
  box-shadow: 0 12px 28px -10px rgba(16, 24, 40, .28), 0 2px 6px rgba(16, 24, 40, .08);
}
.cv-more-item {
  display: flex; flex-direction: column; gap: .1rem; text-align: left;
  border: 0; background: none; cursor: pointer;
  padding: .35rem .45rem; border-radius: .3rem; width: 100%;
}
.cv-more-item:hover { background: #eff6ff; }
.cv-more-top { display: flex; align-items: baseline; gap: .35rem; min-width: 0; }
.cv-more-who {
  font-size: .76rem; font-weight: 600; color: #111827;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cv-more-id { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .64rem; color: #8d97a3; }
.cv-more-waiting {
  font-size: .58rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: #b91c1c; background: #fdeaea; border-radius: .2rem; padding: 0 .25rem;
}
/* Pushed right and never squeezed — it is the one thing scanned down a column. */
.cv-more-ago { margin-left: auto; flex: 0 0 auto; font-size: .66rem; color: #8d97a3; font-variant-numeric: tabular-nums; }
.cv-more-msg {
  font-size: .7rem; color: #6b7280; line-height: 1.35;
  /* Two lines, then an ellipsis: enough to tell conversations apart without
     one long bot reply taking the whole list. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; white-space: normal; word-break: break-word;
}
.cv-more-close {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 1.1rem; height: 1.1rem; margin-left: .15rem; border: 0; border-radius: .25rem;
  background: transparent; color: #8d97a3; cursor: pointer; padding: 0; align-self: center;
}
.cv-more-close:hover { background: #e5e7eb; color: #111827; }
.cv-more-close svg { width: .8rem; height: .8rem; }
/* ⚠ SIZES TO ITS CONTENT, it does not fill — Johan, 2026-08-21: the first
   conversation "should display just to the right of the session list, not to
   the far right as now".
   While this grew, the list column's own cap left the surplus INSIDE the page,
   so the panes were pushed to the far edge with a band of empty ground between
   them and the list they came from. Now the page is as wide as the rail plus
   the list, and the panes begin immediately after it. */
.queue-shell .queue-page { flex: 0 1 auto; min-width: 0; }
/* And the panes take the room the page gave up, rather than only their own
   basis — otherwise the pair would sit left with a gap on the right instead.

   ⚠ `.cv-stack`, NOT `.cv-panes` — Johan, 2026-09-07: the pane "renders narrow
   while fetching transactions. Should directly be normal width."
   This rule used to target `.cv-panes` back when that was the shell's child.
   The "n more open" menu wrapped it in `.cv-stack`, and a descendant selector
   kept matching, so nothing looked wrong — but the element that actually sits
   in the shell's row was left at `flex: 0 1 auto`: sized to its CONTENT. While
   the transcript was empty the content was a header and one line of text, so
   the stack shrank to 22rem and dragged the pane down with it; when the
   messages arrived it grew to 30rem. Measured on prod: 352px → 480px, a
   visible jump on every open. Pointing the rule at the stack gives the pane its
   basis from the first frame. */
.queue-shell .cv-stack { flex: 1 1 auto; }

/* ⚠ MUCH NARROWER than it was (34rem basis, unbounded). A conversation is a
   column of short messages; giving it half the screen wastes it on whitespace
   and makes long lines hard to track. Bounded at 30rem so two or three can sit
   side by side, which is where this is heading — an agent working several
   chats at once. */
/*
 * ⚠ NOTHING RENDERS OUTSIDE THE PANE. Johan, 2026-08-19, as a general rule —
 * the header's controls used to spill past the right edge and sit on top of the
 * next conversation, so a button belonging to one chat appeared to belong to
 * another.
 *
 * Two parts, and the order matters. The header rows WRAP, so a control that no
 * longer fits moves to a new line and stays reachable. `overflow: hidden` is
 * only the backstop that guarantees nothing paints past the border — it must
 * never be the mechanism, because clipping a control does not tidy it away, it
 * makes it unusable while still looking present.
 *
 * Safe because the transcript scrolls in its own container, and the transfer
 * picker is narrower than the pane's minimum width.
 */
/*
 * ⚠ THE DROP LINE — Johan, 2026-08-21: "perhaps showing a vertical line where
 * it will be placed". Drawn on the EDGE the pane will actually land against;
 * which edge is decided in Queue.tsx, because only the order knows.
 *
 * An outline rather than a border: a border would change the pane's width mid
 * drag and shove everything sideways under the cursor.
 */
.cv-pane.is-drop-before::after,
.cv-pane.is-drop-after::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: 4px;
  background: #1d4ed8; pointer-events: none; z-index: 5;
}
/*
 * ⚠ INSIDE the edge, not outside it. This was `left: -.5rem` / `right: -.5rem`,
 * drawn into the gap between panes — where `.cv-pane`'s own `overflow: hidden`
 * clipped it. The indicator has been invisible since it was written, which is
 * why Johan asked twice for one (2026-08-23).
 */
.cv-pane.is-drop-before::after { left: 0; border-radius: .4rem 0 0 .4rem; }
.cv-pane.is-drop-after::after { right: 0; border-radius: 0 .4rem .4rem 0; }
/*
 * ⚠ AND THE PANES PART — Johan: "either separate the ACIs behind if this ACI is
 * now positioned to drop in between them, or show a vertical line". Both: the
 * line says exactly where, the gap makes the space for it visible from the
 * corner of the eye while the cursor is elsewhere.
 *
 * Margin rather than transform, so the neighbours actually move over instead of
 * this one sliding across them.
 */
.cv-pane.is-drop-before { margin-left: 1.1rem; }
.cv-pane.is-drop-after { margin-right: 1.1rem; }
@media (prefers-reduced-motion: no-preference) {
  .cv-pane { transition: margin .12s ease-out; }
}
/* The one being carried steps back, so the line is the loudest thing on screen
   and it is obvious which pane is in flight. */
.cv-pane.is-dragging { opacity: .45; }

.cv-pane {
  position: relative;
  /* 20rem minimum, was 22 — with the menu beside the queue a third pane needs it (Johan, 2026-09-15). paneFit.ts LAYOUT.paneMin MUST match. */
  /* Grows into freed space up to 38rem — Johan, 2026-09-15; 30rem stays the basis, so how MANY fit does not change (paneFit.ts). */
  flex: 1 1 30rem; max-width: 38rem; min-width: 20rem;
  display: flex; flex-direction: column;
  /* ⚠ A DARKER EDGE AND A LIFT — Johan, 2026-09-15: "make the ACIs stand out
     more". A white card on the white page with a hairline had no edge; the
     border is now the rail cards' mid grey and a faint shadow lifts it. */
  background: #fff; border: 1px solid #b9c2cc; border-radius: .4rem;
  box-shadow: 0 1px 3px rgba(28,37,48,.10), 0 4px 14px -8px rgba(28,37,48,.22);
  max-height: calc(100vh - 6rem);
  /*
   * ⚠ top: 0, AND align-self — Johan, 2026-08-21: "clicking on All agent based
   * moved the open ACIs a bit down. should not happen".
   *
   * It was `top: 1rem`. `.cv-panes` sets `overflow: hidden`, which makes it a
   * scroll container, so the pane sticks relative to IT. With a short list the
   * container is exactly as tall as the pane, there is no slack, and the offset
   * cannot show; the moment the list grows taller the slack appears and the
   * pane drops by that 1rem. So the conversation you were reading moved because
   * a DIFFERENT column got longer.
   *
   * The inset bought nothing even when it was invisible: the shell is
   * viewport-height and scrolls internally, so this sticky never actually
   * sticks (verified in the browser — the document scroll offset stays 0). It
   * is kept at 0 rather than deleted because the narrow layout below re-points
   * it.
   *
   * ⚠ NOT `align-self: flex-start`, which also stops the drift but costs the
   * stretch: the pane would shrink to its content instead of filling the
   * column, so a two-line conversation would render a stub of a panel.
   */
  position: sticky; top: 0;
  /* ⚠ VISIBLE, was hidden — Johan, 2026-09-16: "the drag icon is only shown
     inside the ACI but not outside". The grip straddles the top border and
     the clip cut its upper half. Nothing inside needs the clip: the
     transcript scrolls itself, the head has its own top radius, and the
     composer has no ground of its own. The popups under the header are now
     free to hang past the edge too, which they always wanted to. */
  overflow: visible;
}

/*
 * SEVERAL AT ONCE. The pane's basis tightens as more open, so three fit beside
 * the list rather than pushing it off screen; below three the extra width goes
 * back to the conversation.
 *
 * THE ARITHMETIC, so the next person changing a number knows what it costs.
 * Three panes need: 3 × basis + 2 gaps, beside sidebar 10.5rem + list + gaps.
 *
 *   at basis 21rem  →  65rem + 11.5 + list 20 + 1  =  97.5rem  (1560px)
 *   at min   18rem  →  56rem + 11.5 + list 20 + 1  =  88.5rem  (1416px)
 *
 * So three are comfortable from about a 15" laptop up; below that the panes
 * ride their floor and the list keeps its own. Narrower still and the shell
 * wraps rather than overflowing — nothing is pushed out of reach.
 */
.queue-shell.open-2 .cv-pane { flex-basis: 25rem; max-width: 25rem; }
.queue-shell.open-3 .cv-pane { flex-basis: 21rem; max-width: 21rem; min-width: 18rem; }

/* TWO ROWS. Row one is what this conversation IS; row two is where it STANDS.
   See the comment in ConversationPane.tsx. */
.cv-head {
  display: flex; flex-direction: column; gap: .3rem;
  padding: .45rem .6rem; border-bottom: 1px solid #d5dbe2; flex: 0 0 auto;
  /* The head is a band, so the card reads as a card from the top down. */
  background: #f7f9fb; border-radius: .4rem .4rem 0 0;
}
.cv-head-row {
  display: flex; align-items: center; gap: .25rem; min-width: 0;
  /* Wrap rather than spill — see the rule on .cv-pane. */
  flex-wrap: wrap; row-gap: .25rem;
}
/* Everything after this is pushed right. One spacer per row, so the pin/close
   pair and the answering switch line up down the right edge.
   `1 1 0` and not `1 1 auto`: an auto basis lets the spacer claim a line of its
   own once the row wraps, which pushes the controls onto a third line. */
.cv-head-push { flex: 1 1 0; min-width: 0; }

/* The handle a session is referred to by, everywhere. Monospace because it is
   read character by character when someone reads one out. */
.cv-sid {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .8rem; font-weight: 600; color: #111827; letter-spacing: -.01em;
}
/* Its own row, so it gets the pane's full width before it has to truncate —
   half an email cannot be read out, searched for, or recognised. */
/* ⚠ HOLDS ITS HEIGHT WHEN EMPTY — see the markup. Panes sit side by side, and a
   row that appears only sometimes puts one pane's transcript a line below its
   neighbour's. The height is the email's own line-height, so a pane with an
   address and one without agree on where every row after this sits. */
/* ⛔ `.cv-head-email` IS GONE — the address moved onto the identity row
   (2026-09-04), which retired the reserved-blank-row trick that kept
   side-by-side panes aligned. There is no row left to reserve. */
.cv-email {
  font-size: .74rem; color: #4b5563; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cv-chip {
  flex: 0 0 auto;
  font-size: .6rem; font-weight: 600; line-height: 1.5;
  /* .28rem, was .35 — less padding so the row keeps its chips (Johan, 2026-09-15). */
  padding: .05rem .28rem; border-radius: .2rem;
  background: #f1f3f7; color: #4b5563; box-shadow: inset 0 0 0 1px #e2e6ee;
  white-space: nowrap;
}
/* The CBVA, when shown at all, is a coarser fact than the two beside it. */
.cv-chip-solution { background: #eef2ff; color: #4338ca; box-shadow: inset 0 0 0 1px #dfe3fb; }

/* Icon-only controls, right-aligned on row one. */
.cv-icon {
  flex: 0 0 auto;
  border: 1px solid transparent; background: none; cursor: pointer;
  border-radius: .3rem; padding: .15rem .3rem; line-height: 1;
  font-size: .95rem; color: #6b7280; min-height: 24px;
}
.cv-icon:hover { background: #f3f4f6; }
.cv-icon.cv-close { font-size: 1.15rem; }
/*
 * PINNED is the same grey mark, just filled in — Johan, 2026-08-19: no frame,
 * and grey rather than red.
 *
 * The emoji carries its own colour, so both states are desaturated and the
 * difference is weight alone. A red pin and a blue frame made a small,
 * reversible preference look like a warning.
 */
.cv-pin { filter: grayscale(1); opacity: .45; }
.cv-pin.is-on { filter: grayscale(1); opacity: .95; }

/* Wrap stays as the BACKSTOP — the containment rule on .cv-pane means a control
   that genuinely cannot fit must move to a new line rather than spill. With the
   owner name capped it should not come to that at ordinary pane widths. */
.cv-head-state { flex-wrap: wrap; row-gap: .25rem; }
/* Neither of these gives: the link is the only way to reach the picker, and a
   squeezed switch is the control most likely to be mis-hit. */
.cv-transfer, .cv-toggle { flex: 0 0 auto; }
/* Centred like the old console's, and quiet: it is context, never an action. */
/*
 * ⚠ THREE COLUMNS, so the date stays optically centred whatever length the
 * owner's name happens to be. A flex row with a push would slide the date left
 * every time "Agent 1 AllSolutions" appeared.
 */
/*
 * ⚠ ROW ONE NEVER WRAPS — Johan, 2026-08-23: "stuff is on the 1st or 2nd line
 * depending on what fits. We do not want that. Close and pin should always be
 * top right."
 *
 * A long CBVA name pushed the controls onto a second line, so the two things an
 * agent FINDS rather than reads moved with the data. The identity chips give
 * way instead: they shrink and ellipse, and the pin and close hold their
 * corner whatever the name.
 */
/*
 * ⛔ ROW ONE IS FRAMED — Johan, 2026-09-04: "makes sense to put a simple frame
 * / border around the 'what this conversation is' data?". It does: the header
 * holds two different kinds of thing, and only the reading order said so.
 * Identity (who and what) is now visibly one block; everything below it — the
 * switch, the pills, the dates, the owner — is state, which changes while the
 * block above does not.
 *
 * ⚠ A GROUND, NOT A BOX. A full border would draw a second frame inside the
 * pane's own and read as a nested card; a faint tint with one hairline under it
 * groups without adding a shape to look at.
 */
/*
 * ⛔ THE FRAME WRAPS BOTH IDENTITY LINES — Johan, 2026-09-04. Row one is
 * what this conversation IS (id, state, place, language, channel, store,
 * CBVA); row two is WHO it is with (address, history), and it sits directly
 * above the toggle so nothing separates the person from the controls that act
 * on their conversation. Framing only the first said the second was something
 * else.
 *
 * ⚠ A GROUND, NOT A BOX. A full border draws a second frame inside the pane's
 * own and reads as a nested card; a faint tint with one hairline under it
 * groups without adding a shape to look at.
 */
/*
 * ⛔ THE FRAME HOLDS DATA, THE ICONS SIT OUTSIDE IT — Johan, 2026-09-04. Pin
 * and close act on the PANE rather than describing the conversation, and they
 * must be in the same place on every pane; a box that grows with its contents
 * is the wrong thing to put them in.
 *
 * ⚠ A REAL BORDER NOW, not a ground with one hairline. Once the icons stepped
 * outside, the block has an edge on all four sides and needs to show one —
 * otherwise the icons read as having drifted off the end of a row rather than
 * as sitting beside a panel.
 */
.cv-head-identity { display: flex; align-items: flex-start; gap: .35rem; }
.cv-head-frame {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column;
  background: #f8fafc;
  border: 1px solid #e5e7eb;
  border-radius: .35rem;
  /* Tighter — Johan, 2026-09-15: "allow the session group to be a bit wider". */
  padding: .12rem .3rem;
  margin-bottom: .15rem;
}
/* The controls beside the frame give it room: narrower gap, smaller Mark Done. */
.cv-head-identity { gap: .2rem; }
.cv-head-done .cv-link { font-size: .68rem; }
.cv-head-icons .cv-icon { padding: .15rem .2rem; }
.cv-head-id, .cv-head-who { flex-wrap: nowrap; padding: 0; margin: 0; }
/* ⚠ Top-aligned, so they hold their place whether the frame is one line tall
   or two — the frame's height follows its contents, these must not. */
.cv-head-icons { flex: 0 0 auto; display: flex; gap: .1rem; padding-top: .1rem; }

/*
 * ⛔ BOTH LINES KEEP THEIR HEIGHT WHEN EMPTY. A session with no address, or one
 * on a solution the view does not span, must not pull the lines below it up —
 * two panes side by side would then disagree about where their dates and
 * transcripts sit, which is the whole reason these became fixed lines.
 *
 * ⚠ The value is the line-box of the smallest thing in them (.7rem text), not
 * a guess. Change either font and change this.
 */
.cv-head-who { min-height: 1.15rem; align-items: baseline; }

/* ⚠ BESIDE THE ADDRESS, not at the far end — Johan, 2026-09-04. Pushed right
   it read as a separate column and left a gap across a wide pane; next to the
   address it reads as one fact about one person, which is what it is. The
   address still takes what room it needs first and truncates. */
.cv-head-who { justify-content: flex-start; gap: .45rem; }
.cv-head-who .cv-vmenu { flex: 0 1 auto; min-width: 0; }
.cv-head-id .cv-sid { flex: 0 0 auto; }
/* The chips are the only thing here that may lose room — a truncated store name
   is still recognisable, a control that moved is not. */
.cv-head-id .cv-chip {
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/*
 * The address, on the identity row since 2026-09-04.
 *
 * ⚠ IT SHRINKS LAST OF THE FLEXIBLE THINGS. A truncated store name is still
 * recognisable; half an email is not — it cannot be read out, searched for or
 * recognised, which is why this used to have a row to itself. So the chips give
 * up their room first (shrink 3 against 1), and the full address stays on the
 * button's hover title.
 */
.cv-head-who .cv-vmenu-trigger { flex: 0 1 auto; min-width: 0; }
.cv-head-id .cv-chip { flex-shrink: 3; }
/* ⛔ THE LANGUAGE CODE ALWAYS FITS — Johan, 2026-09-15. Two letters that say what
   the conversation is in; the city chip beside it is the one that may lose
   room, and it goes first. */
.cv-head-id .cv-chip-lang { flex: 0 0 auto; }
.cv-head-id .cv-chip-where { flex-shrink: 6; }
.cv-head-id .cv-icon, .cv-head-id .cv-back { flex: 0 0 auto; }

/* ⚠ Reads as absence, not as identity — it sits where an address goes and must
   not be mistaken for one. See the slot in ConversationPane. */
.cv-noemail {
  font-size: .74rem; font-style: italic; color: #9ca3af;
  flex: 0 0 auto; white-space: nowrap;
}
/* Place and language: quieter than the store chips beside them, because they
   are context for reading the conversation rather than what it belongs to. */
.cv-head-id .cv-chip-where, .cv-head-id .cv-chip-lang {
  background: none; border: 1px solid #e5e7eb; color: #6b7280;
}
/* A flag needs no frame: the emoji is the chip. Never shrinks; it is one glyph. */
.cv-head-id .cv-chip-flag { border: 0; padding: 0 .1rem; font-size: .9rem; line-height: 1; flex: 0 0 auto; }

/*
 * The wait, on the state row.
 *
 * ⚠ NO NEW HUE — see waitLevel. `overdue` gets weight and the existing warning
 * ink that the Important pill already uses; nothing here introduces a colour an
 * agent has to learn.
 */
/*
 * "4th session · last 2 days ago".
 *
 * ⚠ WEIGHT, NOT A HUE — Johan asked whether recency should be a colour; see
 * returningVisitor.ts. The header already spends colour on urgency, and a
 * fourth meaning competing there costs more than it says. `is-recent` (back
 * within a day, usually the same problem) darkens and bolds; everything older
 * stays quiet.
 */
.cv-returning {
  font-size: .7rem; color: #9ca3af; white-space: nowrap;
  flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
/* ⚠ AMBER — the Important colour — for "back again, unresolved": within 48h,
   or a third conversation in a week. Johan, 2026-09-07: "alert color in the
   ACI if within 48 hrs". It replaces the bold-grey of 2026-09-04; the reason
   for no colour then was a crowded header, and this reuses a colour the header
   already means "needs you" by. */
/* ⚠ `--st-important`, the ink for a PALE ground — not `-ink`, which is what sits on the solid fill and
   went white on 2026-09-14 (Johan: "I see something after the email", the returning mark had vanished). */
.cv-returning.is-alert { color: var(--st-important); font-weight: 700; }
.qr-returning {
  font-size: .68rem; color: #9ca3af; white-space: nowrap; flex: 0 0 auto;
  padding: 0 .3rem; border-radius: .2rem;
}
.qr-returning.is-alert { color: var(--st-important); background: var(--st-important-tint); font-weight: 700; }

.cv-waiting { font-size: .72rem; color: #6b7280; white-space: nowrap; }
/* Right-justified at the end of the email row (Johan, 2026-09-08) — the one
   thing on that row pushed to the far end, so it reads as the row's clock. */
.cv-head-who .cv-waiting { margin-left: auto; flex: 0 0 auto; }
/* The clock now sits inside its tip wrapper, so the wrapper is what gets pushed right — Johan, 2026-09-15: "right justify the n days". */
.cv-head-who .cv-waiting-wrap { margin-left: auto; flex: 0 0 auto; }
/* On the switch row since 2026-09-15: after the push, just left of the state pill. */
.cv-head-state .cv-waiting-wrap { flex: 0 0 auto; margin-right: .1rem; }
/* ⛔ ORANGE MEANS "THE VISITOR IS WAITING ON US", at every age — Johan,
   2026-09-15: the word "waiting" went, so the colour alone has to say which
   clock this is. Was grey until 5 minutes and dark grey until 30, which read
   the same as the not-replying grey below. Weight still carries the age:
   bold once overdue. */
.cv-waiting.is-fresh, .cv-waiting.is-aging { color: #b45309; }
.cv-waiting.is-overdue { color: #b45309; font-weight: 600; }
/* Our message unanswered — the quiet direction. Never weighted: nobody is
   owed anything, it only says whether a nudge is due. */
.cv-waiting.is-noreply { color: #9ca3af; font-weight: 400; }
/* The email trigger's tip wrapper must shrink like the trigger did, or the
   address stops ellipsing on a narrow pane. */
.cv-vmenu-tipwrap, .qr-email-tipwrap { display: flex; min-width: 0; flex: 0 1 auto; }

/*
 * ⚠ TWO COLUMNS, date LEFT — Johan, 2026-08-23. It was `1fr auto 1fr`, an
 * empty first column reserved purely to keep the date optically centred. That
 * centring cost the agent's name its room: "Agent 1 AllSolutions" wrapped onto
 * two lines with a third of the row blank beside it. A centred date is worth
 * nothing; a readable name is.
 */
/*
 * ⚠ THREE COLUMNS NOW: date · pencil · owner. It was `auto 1fr`, and adding the
 * pencil as a third child would have pushed the owner out of the 1fr track that
 * right-aligns it under Done / Assign.
 *
 * The pencil takes `auto` and sits between them: it belongs to the
 * conversation, not to the owner, and trailing the row would put it under the
 * controls that change assignment.
 */
.cv-head-when {
  display: grid; grid-template-columns: auto auto 1fr; align-items: center; gap: .5rem;
  /* ⚠ ONE ROW, ALWAYS — Johan, 2026-08-25: "make sure that that row is not
     broken into two". A grid with explicit columns still creates implicit ROWS
     for anything that does not fit; the name ellipses instead (see
     `.cv-owner .queue-by`), which is the honest way to lose space. */
  grid-auto-flow: column; grid-auto-columns: min-content;
}
/* Right-aligned so it lines up under Done / Assign — the controls that change
   it. Same classes as the list rows (.by-*), so a session cannot be teal in the
   list and something else in its own header. */
/* ⚠ `min-width: 0` — without it a long name refuses to shrink and shoves the
   date out of the row rather than ellipsing itself. */
/*
 * ⛔ COLUMN 3, AND THAT IS WHY IT WRAPPED — Johan, 2026-08-25: "move agent to
 * same row, make sure that row is not broken into two".
 *
 * It was pinned to `grid-column: 2` from when this row had two tracks. Adding
 * the pencil made column 2 the PENCIL's, so the owner was forced onto a new
 * grid row — silently, because a grid does not error, it just reflows.
 *
 * ⚠ Any change to `.cv-head-when`'s template must move this with it. Explicit
 * placement plus a changing template is exactly how a layout drifts without
 * anything failing.
 */
.cv-owner {
  grid-column: 3; justify-self: end; min-width: 0;
  display: inline-flex; align-items: center; gap: .3rem;
  /* Smaller and bold — Johan, 2026-08-25. A name is the one thing on this row
     read as an identity rather than as data, and weight carries that at a size
     that leaves the date room to breathe. */
  font-size: .72rem; font-weight: 600;
}
.cv-owner .queue-by {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.cv-when {
  font-size: .66rem; color: #8b96a5;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* Sized to the pills beside it, so row two reads as one band. */
.cv-btn-sm { padding: .12rem .45rem; font-size: .68rem; min-height: 22px; }

/*
 * WHO IS ANSWERING — a switch, because it has two states and flips between
 * them. Same height as the pills so it sits on their row.
 */
.cv-toggle {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: .3rem;
  border: 0; background: none; cursor: pointer; padding: 0 0 0 .2rem;
  min-height: 22px;
}
.cv-toggle:disabled { opacity: .5; cursor: not-allowed; }
.cv-toggle-track {
  width: 1.85rem; height: 1rem; border-radius: 999px;
  background: #9ca3af; position: relative; flex: 0 0 auto;
  transition: background .12s ease;
}
.cv-toggle-knob {
  position: absolute; top: 2px; left: 2px;
  width: .75rem; height: .75rem; border-radius: 50%; background: #fff;
  transition: transform .12s ease;
}
/* Matches the agent's own bubbles — the switch and the messages it produces are
   the same colour, which is the point of the split. */
/* The YOU bubbles' teal, so the switch and the bubbles say "agent" in one colour — Johan, 2026-09-17. Bot stays the bot bubbles' blue. */
.cv-toggle.is-agent .cv-toggle-track { background: #00857a; }
.cv-toggle.is-agent .cv-toggle-knob { transform: translateX(.85rem); }
.cv-toggle.is-bot .cv-toggle-track { background: #4675bf; }
.cv-toggle-label { font-size: .68rem; font-weight: 600; color: #374151; }

/*
 * A HANDOVER IS WAITING. Loud on purpose — Johan, 2026-08-19 — because it is
 * the one thing on this header that somebody has to act on, and the control
 * that answers it is this same switch.
 *
 * ⚠ It is NOT disabled. It used to be, which disabled the only control that
 * resolves the situation: `takeOverSession` refuses only an already-taken-over
 * session, and taking over is what sets the request to Fulfilled.
 */
/*
 * ⚠ NO SPECIAL TREATMENT WHILE A TRANSFER IS WAITING — Johan, 2026-08-19.
 *
 * It was first a filled amber pill carrying the words, then a ring. Both were
 * the same mistake in different sizes: the "Transfer requested" pill on the row
 * above already says it, and a second mark for one fact makes the quieter of
 * the two read as a different thing. The switch says who is answering, and
 * only that.
 *
 * ⚠ It is not disabled either. `takeOverSession` refuses only an
 * already-taken-over session, and taking over is what fulfils the request.
 */
@media (prefers-reduced-motion: reduce) {
  .cv-toggle-track, .cv-toggle-knob { transition: none; }
}
.cv-back {
  border: 0; background: none; color: #1d4ed8; font-size: 1.4rem; line-height: 1;
  cursor: pointer; padding: 0 .2rem; display: none;   /* desktop keeps the list visible */
}
/* `.cv-ident`, `.cv-who` and `.cv-sub` were the old one-row header. The two-row
   one above replaced them — deleted rather than left as dead rules nobody can
   tell are unused. */
/* WHO has it and WHICH BUCKET it is in, on the conversation header.
   The state half reuses the sidebar's .sc-* colours rather than restating them,
   so a bucket cannot be one colour in the list and another here. */
/*
 * ⚠ THE WHOLE ROW STAYS ON ONE LINE. Johan, 2026-08-19 — an agent named
 * "Agent 1 AllSolutions" pushed the answering switch onto a second line.
 *
 * The group shrinks instead of wrapping, and the pressure lands on the OWNER
 * name first because it is the only part that is arbitrarily long. Everything
 * else here is a fixed vocabulary — Replied, Action request — and truncating a
 * known word to "Act…" costs more than it saves.
 */
.cv-pills {
  display: flex; align-items: center; gap: .25rem;
  flex: 0 1 auto; min-width: 0; flex-wrap: nowrap;
}
/* "Undo" beside the Action done pill — a link at the pill's size, so it reads
   as belonging to that pill and not to the control band above. */
.cv-pill-undo { font-size: .68rem; margin-left: .1rem; }
.cv-pill {
  flex: 0 0 auto;
  /* ⚠ CENTRED TEXT — Johan, 2026-09-16: "the status pills must have vertically
     centered text". Inline-flex with line-height 1 puts the glyphs in the
     middle of the box whatever the font; the old line-height 1.5 on an inline
     span sat them a hair high. Same height as before. */
  display: inline-flex; align-items: center;
  font-size: .6rem; font-weight: 600; line-height: 1;
  padding: .2rem .35rem; border-radius: .2rem; color: #fff; white-space: nowrap;
}

/*
 * ⛔ AFTER `.cv-pill`, AND THAT IS THE FIX — Johan, 2026-08-25: "Agent clearly
 * marked as Done (see comment) but in header not marked as done!!", with an
 * empty white box where the pill should be.
 *
 * The pill was there. `.cv-pill` sets `color: #fff` for the filled states, and
 * these two modifiers lived ~650 lines EARLIER in this file — same specificity,
 * so the base won and "Reopened" rendered as white text on its own white
 * background. Invisible, and it read as an empty box rather than as a word
 * nobody could see.
 *
 * ⚠ The header was not wrong: that session had been reopened, and Reopened +
 * "Mark done" is exactly right for it. Only the label was unreadable.
 *
 * ⚠ Do not move these back above `.cv-pill`. Nothing here raises specificity —
 * they win on order alone, which is why the position is the rule.
 */
/* Outlined, matching `.qr-reopened` on the list rows — one idea, one look. */
.cv-pill-reopened {
  background: #fff; color: #4b5563; font-weight: 600;
  box-shadow: inset 0 0 0 1px #cbd5e1;
}
.cv-pill-done {
  background: #15803d; color: #fff; font-weight: 700;
  box-shadow: 0 0 0 2px #dcfce7;
}
/* The one variable-length pill, so the one that gives. Floor is wide enough to
   still tell "Mine" from "Unassigned" from a person's first name. */
.cv-pill-owner {
  flex: 0 1 auto; min-width: 3.2rem; max-width: 8rem;
  overflow: hidden; text-overflow: ellipsis;
}
/* The owner pill is outlined, the state pill filled — so at a glance the eye
   separates "whose" from "how urgent" without reading either. */
.cv-pill-owner-bot        { background: #eef3fb; color: #2f5da8; box-shadow: inset 0 0 0 1px #cddcf3; }
.cv-pill-owner-me         { background: #effcf9; color: #00857a; box-shadow: inset 0 0 0 1px #a8e2d8; }
.cv-pill-owner-unassigned { background: #fef2f2; color: #b91c1c; box-shadow: inset 0 0 0 1px #fecaca; }
.cv-pill-owner-agent      { background: #f1f5f6; color: #3d5a66; box-shadow: inset 0 0 0 1px #d7e0e3; }
/* ⚠ `.sc-bot`'s solid-purple rule USED TO LIVE HERE and was dead — overridden
   ~1500 lines below, where the token version now is. Two rules for one pill,
   1500 lines apart, is how the pane ended up purple while the list was blue
   without anyone editing either. Removed; the definition is with the other
   states.
   `.sc-history` stays: quieter than every bucket colour ON PURPOSE — this one
   is not work and should not compete with the four that are. */
.sc-history { background: #e5e7eb; color: #4b5563; }

/* Dragging a pane to re-order the row. The header is the handle — see the
   component. `grab` only while it is actually draggable, so the phone (which
   passes no handler) does not advertise something it cannot do. */
.cv-head[draggable="true"] { cursor: grab; }
.cv-head[draggable="true"]:active { cursor: grabbing; }
/* The pane being dragged fades rather than disappears: it is still where it
   was until the drop lands, and hiding it would suggest the move already
   happened. */
.cv-pane:has(.cv-head.is-dragging) { opacity: .55; }

/* Pin. Pressed means the pane survives the next row click.
   ⚠ GREY WHEN PINNED, and no frame — Johan, twice (2026-08-19 and again
   2026-08-20): "do not use red with square if pinned. just use that icon but
   not red - use grey". The pinned emoji is red by default and a blue box round
   it made it louder still; pinning is a quiet housekeeping act, not a state
   worth an alert. The ICON changing is the signal. */
.cv-pin { padding: .3rem .4rem; line-height: 1; filter: grayscale(1); opacity: .55; }
.cv-pin.is-on { filter: grayscale(1); opacity: 1; border-color: transparent; background: none; }

.cv-btn {
  border: 1px solid #d1d5db; background: #fff; border-radius: .3rem;
  padding: .3rem .6rem; font-size: .78rem; cursor: pointer; min-height: 32px;
}
.cv-btn:disabled { opacity: .5; cursor: not-allowed; }
.cv-btn-primary { background: #1d4ed8; border-color: #1d4ed8; color: #fff; }
.cv-btn-send { background: #1d4ed8; border-color: #1d4ed8; color: #fff; }

.cv-blocked {
  margin: 0; padding: .4rem .7rem; font-size: .74rem;
  background: #fef3c7; color: #92400e; border-bottom: 1px solid #fde68a;
}
.cv-blocked.cv-error { background: #fee2e2; color: #991b1b; border-bottom-color: #fecaca; }

/*
 * ⛔ THE CHAT KEEPS 4cm — Johan, 2026-09-21: "always display at least the agent
 * input field area + 4cm or so of the end of the chat". Basis 0 and a 4cm floor,
 * so when the pane is tight it is the Agent work panel (.wt) that gives way,
 * never the last messages. The composer below is fixed height already.
 */
.cv-transcript {
  flex: 1 1 0; min-height: 4cm; overflow-y: auto; padding: .7rem;
  display: flex; flex-direction: column; gap: .35rem;
}
.cv-warn {
  background: #fee2e2; color: #991b1b; border: 1px solid #fecaca;
  border-radius: .3rem; padding: .4rem .6rem; font-size: .74rem; margin-bottom: .3rem;
}
.cv-day {
  align-self: center; font-size: .68rem; color: #9ca3af;
  text-transform: uppercase; letter-spacing: .05em; margin: .5rem 0 .2rem;
}

.cv-row { display: flex; }
/* OUR side (agent AND bot) right, the shopper left — matching the shopper's own
   widget, and the division an agent actually reads the thread by. */
.cv-row-ours { justify-content: flex-end; }
.cv-row-theirs { justify-content: flex-start; }

/*
 * ⚠ BLOCK, not a column flex. The timestamp has to be able to sit at the END OF
 * THE LAST LINE of the message (Johan, 2026-08-19), and a float can only be
 * pulled onto a line box — in a column flex it is always its own row instead.
 */
.cv-bubble {
  max-width: 78%; border-radius: .5rem; padding: .35rem .55rem;
  font-size: .82rem; line-height: 1.4; display: block;
}
/* The float is the last thing in the box, so without this the box has no height
   of its own and the bubble collapses behind its own text. */
.cv-bubble::after { content: ''; display: block; clear: both; }
.cv-from-shopper { background: #f3f4f6; color: #111827; }
.cv-from-agent { background: #dbeafe; color: #0f172a; }
.cv-from-bot { background: #ede9fe; color: #1f1235; }
.cv-from-system { background: #f9fafb; color: #4b5563; }
.cv-bubble.is-pending { opacity: .65; }
.cv-bubble.is-failed { background: #fee2e2; color: #991b1b; }

.cv-label {
  display: block;
  font-size: .62rem; text-transform: uppercase; letter-spacing: .05em;
  opacity: .6; font-weight: 600;
}
/* INLINE so the timestamp after it lands on the same line box. `pre-wrap` still
   honours the message's own line breaks. */
.cv-text { display: inline; white-space: pre-wrap; word-break: break-word; }
/* Trails the last line when there is room and drops below when there is not —
   which is what a float does, and why this is one. */
.cv-time {
  float: right; margin: .3rem 0 0 .7rem;
  font-size: .62rem; white-space: nowrap;
  /* Passes the bubble's background down to `.cv-peek`. NOT positioned: the
     peek anchors to the bubble, so it sits above it and not over its text. */
  background: inherit;
}
/*
 * The fade sits on the CHILDREN, not on the span. Opacity is compositional, so
 * a faded parent caps everything inside it — and the language peek lives inside
 * this span and must not come up faded.
 */
.cv-time > :not(.cv-peek) { opacity: .55; }
.cv-clock {
  border: 0; background: none; padding: 0; margin: 0;
  font: inherit; color: inherit; letter-spacing: inherit;
}
.cv-clock-flip { cursor: pointer; }
/*
 * ⛔ THE OTHER LANGUAGE ON HOVER — Johan, 2026-09-17. The browser tooltip was
 * slow and only described the click; what the agent wants to see IS the other
 * rendering. It appears over the bubble in the bubble's own colours, so it
 * reads as "the same message, other language", and it is a peek: the flip
 * still exists for reading at length or copying.
 *
 * ⚠ ON THIS BUBBLE, NOT ABOVE IT — Johan, 2026-09-18. Floating above, it sat
 * in the gap between two bubbles and read as the translation of the one
 * ABOVE. So it lies over this bubble, covering it whole: the bubble's full
 * width and at least its full height, growing upward when the other text is
 * longer. A cut that stopped short of the code and the time "felt erratic"
 * (the width changed with the text), so it is always the whole bubble.
 *
 * ⚠ `pointer-events: none`, so the hover on the code and the time under it
 * survives the card appearing over them.
 *
 * ⚠ A short delay before it shows, so crossing the time on the way to
 * somewhere else does not flash a panel; none on the way out.
 */
.cv-bubble { position: relative; }
.cv-peek {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 30;
  min-height: 100%; box-sizing: border-box;
  padding: .35rem .55rem; border-radius: .5rem;
  /* ⛔ Its own paint. `background` does not inherit by itself, and without
     this the bubble under it showed through (Johan, 2026-09-18). The value
     comes down from `.cv-time`, which inherits the bubble's. */
  background: inherit;
  /* ⚠ A SHADE OFF THE BUBBLE — Johan, 2026-09-18: grown upward over the
     previous bubble from the same person, the same colour "will look
     strange". The text colour mixed into the bubble's own: a dark bubble
     turns lighter, a pale one darker, one rule for every palette row. */
  background-image: linear-gradient(
    color-mix(in srgb, currentColor 14%, transparent),
    color-mix(in srgb, currentColor 14%, transparent)
  );
  font-size: .82rem; line-height: 1.4; letter-spacing: normal;
  white-space: normal; text-align: left;
  border: 1px solid rgba(255,255,255,.35);
  outline: 1px solid rgba(0,0,0,.18);
  box-shadow: 0 6px 18px rgba(0,0,0,.25);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .12s ease, visibility 0s linear .12s;
}
/* Which language this is — the code, as the marker writes it. */
.cv-peek-lang {
  display: block; margin-bottom: .1rem;
  font-size: .58rem; font-weight: 700; letter-spacing: .04em; opacity: .7;
}
.cv-time-peek:hover .cv-peek {
  opacity: 1; visibility: visible;
  transition-delay: .1s, .1s;
}
/* Say the pair is one control: hovering either underlines both. */
.cv-time-peek:hover .cv-lang,
.cv-time-peek:hover .cv-clock-flip {
  text-decoration: underline; text-underline-offset: 2px; opacity: 1;
}
.cv-sending { font-style: italic; }
.cv-failedmark { font-weight: 600; }

/*
 * "Visitor chose this option" — the shopper tapped a button rather than typing.
 *
 * ⛔ ITS OWN LINE, INSIDE THE BUBBLE — Johan, 2026-09-01. Inline it ran straight
 * on from the message ("Chat with an agent*chose this option*"), which reads as
 * part of what the visitor said rather than as a note about HOW they said it.
 * The distinction is the whole reason the label exists: "Returns" typed and
 * "Returns" tapped mean different things about intent.
 *
 * ⚠ A BLOCK, and that is what puts the timestamp beside it rather than below.
 * `.cv-time` is a right float declared after this element, and a float may not
 * sit higher than the top of a block box earlier in the source — so it lands on
 * this line. (Do not make this a flex child of the bubble: the bubble is
 * deliberately `display: block` so the float can be pulled onto a line box at
 * all — see the note there.)
 */
.cv-chosen {
  display: block; margin-top: .15rem;
  font-size: .62rem; opacity: .6; font-style: italic;
}
/*
 * The language a message was actually written in, beside its time.
 *
 * ⛔ Replaced "• translated from SV" — Johan, 2026-08-26: "ugly and confusing".
 * It was both: a full italic phrase on nearly every bubble, and wrong on half
 * of them (an agent's own English marked "translated" in the English view).
 *
 * ⚠ Sits in the timestamp run and inherits its colour, so it is legible on the
 * blue and green bubbles as well as the grey ones — a fixed colour here would
 * have needed a variant per bubble type.
 */
.cv-lang {
  margin-right: .3rem; padding: 0 .15rem;
  border: 0; background: none; color: inherit; font: inherit;
  /* .58rem → .7rem, Johan 2026-09-17: "a larger EN" — it is a hover target now. */
  font-weight: 700; font-size: .7rem; letter-spacing: .04em;
  cursor: pointer; border-radius: .18rem;
}
/* Beats the `.cv-time > :not(.cv-peek)` fade; the code is the brighter thing. */
.cv-time > .cv-lang { opacity: .85; }
/*
 * ⛔ SAY IT IS CLICKABLE — Johan, 2026-08-26: "on mouse over show user it is
 * clickable".
 *
 * It has to be discoverable without being loud: it sits on every translated
 * bubble, so anything permanent would turn the timestamp run into a row of
 * buttons. Underline plus a faint wash on hover is enough to invite the click
 * and invisible until the pointer is on it.
 *
 * ⚠ currentColor throughout — this sits on the blue, green and grey bubbles
 * alike, and a fixed colour would be illegible on at least one of them.
 */
.cv-lang:hover,
.cv-lang:focus-visible {
  opacity: 1; text-decoration: underline; text-underline-offset: 2px;
  background: color-mix(in srgb, currentColor 14%, transparent);
}
.cv-lang:focus-visible { outline: 1px solid currentColor; outline-offset: 1px; }
/* Read-only: the shopper's buttons, shown so an agent knows what was offered. */
.cv-options { display: flex; flex-wrap: wrap; gap: .25rem; margin-top: .2rem; }
.cv-option {
  font-size: .68rem; border: 1px dashed currentColor; opacity: .65;
  border-radius: 999px; padding: .05rem .4rem;
}
.cv-attach { display: flex; align-items: baseline; gap: .4rem; }
.cv-attach-name { text-decoration: underline; }
.cv-attach-size { font-size: .65rem; opacity: .6; }
.cv-suggest-tag {
  font-size: .62rem; text-transform: uppercase; letter-spacing: .05em;
  color: #6d28d9; font-weight: 600;
}

/* Events read as a line across the thread, not as someone speaking. */
.cv-event {
  align-self: center; display: flex; align-items: baseline; gap: .4rem;
  font-size: .7rem; color: #6b7280; background: #f9fafb;
  border: 1px solid #f3f4f6; border-radius: 999px; padding: .1rem .6rem;
}
.cv-event-time { font-size: .62rem; opacity: .6; }

.cv-composer {
  flex: 0 0 auto; display: flex; align-items: flex-end; gap: .4rem;
  padding: .5rem .6rem; border-top: 1px solid #e5e7eb;
}
/*
 * ⚠ TOP-aligned, against the composer's flex-end — Johan, 2026-08-25: "move the
 * template icon up to vertically top align with the top part of the write a
 * reply field".
 *
 * The reply box GROWS as you type (min 36px, max 8rem). Everything else in the
 * row is bottom-aligned, which is right for Send — it stays where the cursor
 * finishes. Templates is the opposite: you reach for it BEFORE writing, so it
 * belongs where the empty field starts, and it should not drift down the screen
 * as the message gets longer.
 */
.cv-tplwrap { align-self: flex-start; }

/*
 * ⛔ THE ICONS LIVE IN THE REPLY BOX — Johan, 2026-09-21: beside the box they
 * took ~5rem of a 20rem pane. Side by side in the bottom-right corner, templates
 * then attach, and a little smaller, so the box keeps its one-line 36px height.
 *
 * ⚠ The template PICKER anchors to this wrap, not to its button (.cv-tplwrap is
 * static here): it opens left-aligned over the thread, as wide as before.
 */
.cv-inputwrap { position: relative; flex: 1 1 auto; min-width: 0; display: flex; }
.cv-inputwrap .cv-input { flex: 1 1 auto; min-width: 0; padding-right: 3.1rem; }
.cv-inputwrap .cv-tplwrap { position: static; }
/* Wrapped: full-width text, the icons in a strip under it (ConversationPane, multiline). */
.cv-inputwrap.is-multi .cv-input { padding-right: .5rem; padding-bottom: 1.75rem; }
.cv-inputwrap .cv-btn-tpl,
.cv-inputwrap .cv-btn-attach {
  position: absolute; bottom: .2rem; width: 1.4rem; min-width: 0; min-height: 1.4rem; height: 1.4rem;
  padding: 0; font-size: .7rem; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.cv-inputwrap .cv-btn-attach { right: .2rem; }
.cv-inputwrap .cv-btn-tpl { right: 1.65rem; }
.cv-inputwrap .cv-btn-tpl svg { width: 13px; height: 13px; }
/* The upload percentage ("100%") has to fit the icon's width while it counts. */
.cv-inputwrap .cv-btn-attach:disabled { font-size: .5rem; }

/*
 * ⚠ SEND'S HEIGHT, EVEN WITH A LONG LABEL — Johan, 2026-09-21: "Translate & Send
 * SV" wrapped to two lines at full size and stood taller and wider than Send.
 * Smaller type and tight leading keep both lines inside Send's 32px.
 */
.cv-btn-send.is-long {
  max-width: 5.6rem; padding: .15rem .45rem;
  font-size: .66rem; line-height: 1.1; white-space: normal;
}

/*
 * The language row, above the reply box.
 *
 * ⚠ Quiet. It states a setting rather than asking for an action, and it sits
 * directly above the field an agent uses constantly — anything loud here would
 * compete with the conversation for the rest of the shift.
 */
.cv-langbar {
  display: flex; align-items: center; gap: .45rem;
  /* ⚠ Inside the reply box's border, not above it — Johan, 2026-08-26. The
     top border comes from .cv-composer below, so this row sits under the line
     and reads as part of the reply box it configures. */
  padding: .4rem .6rem 0;
  border-top: 1px solid #e5e7eb;
  font-size: .68rem; color: #6b7280;
}
/* The composer follows immediately, so its own rule would double the line. */
.cv-langbar + .cv-composer { border-top: 0; padding-top: .35rem; }

/*
 * "reading EN ⇄" — quiet until it disagrees with the write language.
 *
 * ⛔ The HIGHLIGHT is the whole mechanism (Johan, 2026-08-26). Reading a
 * different language than you write in is rare, so the control is secondary —
 * but the resulting state must never be silent, or the transcript appears in a
 * language nothing on screen explains.
 */
.cv-langread {
  margin-left: auto;
  border: 0; background: transparent; cursor: pointer;
  padding: .1rem .3rem; border-radius: .25rem;
  font: inherit; font-size: .66rem; color: #9ca3af;
}
.cv-langread:hover { color: #4b5563; background: #f3f4f6; }
.cv-langread.is-on {
  color: #92400e; background: #fef3c7; font-weight: 600;
}
.cv-langbar-label { letter-spacing: .01em; }
.cv-langpick { display: inline-flex; border: 1px solid #d1d5db; border-radius: .3rem; overflow: hidden; }
.cv-langopt {
  border: 0; background: #fff; color: #6b7280; cursor: pointer;
  padding: .1rem .4rem; font: inherit; font-size: .68rem; font-weight: 600;
  letter-spacing: .03em;
}
.cv-langopt + .cv-langopt { border-left: 1px solid #e5e7eb; }
/*
 * ⚠ THE CONSOLE'S "SELECTED", not a new colour — Johan, 2026-08-26: "perhaps
 * better to use black for selected, or dark grey - softer".
 *
 * It was #1d4ed8 blue, which is the SEND button's colour: the one thing on the
 * screen that means "this does something irreversible". A selected language is
 * a state, not an action. #1f2530 is what every view chip already uses for
 * is-on, so selection now looks the same wherever it happens.
 */
.cv-langopt.is-on { background: #1f2530; color: #fff; }
.cv-langpreview { display: inline-flex; align-items: center; gap: .25rem; cursor: pointer; }
.cv-langpreview input { margin: 0; }

/*
 * The translation, below the reply field and full width — both texts visible at
 * once, rather than a modal over the conversation being replied to.
 */
/*
 * ⚠ flex: 0 0 auto — it was being SQUASHED. The pane is a flex column, so a
 * block with no flex basis is the first thing compressed when the transcript
 * wants room; the translation showed one clipped line. Johan, 2026-08-26:
 * "preview is not displayed properly".
 */
.cv-preview {
  flex: 0 0 auto;
  margin: .35rem .6rem .4rem; border: 1px solid #cfe0ff; border-radius: .4rem;
  background: #f6f9ff; overflow: hidden;
}
/* Only rendered when the translation is stale — see the component. */
.cv-preview-head {
  padding: .2rem .5rem; font-size: .64rem; font-weight: 600;
  color: #92400e; background: #fef3c7; border-bottom: 1px solid #fde68a;
}
/* The translation and the button that sends it, on one line. */
.cv-preview-row { display: flex; align-items: flex-end; gap: .4rem; padding: .3rem .4rem; }
.cv-preview-row .cv-btn-send { flex: 0 0 auto; }
/* ⚠ A real minimum. `rows={2}` is a hint the flex column was free to ignore,
   and did — the field collapsed to a sliver of its first line. */
.cv-preview-text {
  display: block; flex: 1 1 auto; min-width: 0; min-height: 2.6rem;
  border: 1px solid #cfe0ff; border-radius: .3rem; background: #fff;
  resize: vertical;
  padding: .35rem .5rem; font: inherit; font-size: .8rem; color: #111827;
  line-height: 1.35;
}
.cv-preview-text:focus { outline: 2px solid #bfd4ff; outline-offset: -2px; }
/*
 * ⛔ GREYED BUT KEPT — Johan, 2026-08-25: editing the English must not destroy a
 * translation the agent may have spent time correcting. It stays legible and
 * editable; only the send is blocked, by the button turning back into
 * "Translate again".
 */
/* ⚠ Dimmed but LEGIBLE and still editable — the agent may have corrected this
   by hand, and greying it out must not read as "discarded". Only the send is
   blocked, by the button turning back into "Translate again". */
.cv-preview.is-stale .cv-preview-text { opacity: .6; }

.cv-translate-error {
  flex: 0 0 auto;
  margin: .35rem .6rem .4rem; padding: .35rem .5rem;
  border: 1px solid #fecaca; border-radius: .4rem; background: #fef2f2;
  color: #991b1b; font-size: .72rem;
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
}
.cv-input {
  flex: 1 1 auto; resize: none; border: 1px solid #d1d5db; border-radius: .4rem;
  padding: .4rem .5rem; font: inherit; font-size: 16px;  /* 16px stops iOS auto-zoom */
  line-height: 1.3; max-height: 8rem; min-height: 36px; overflow-y: auto;
}
/*
 * ⚠ SMALLER ONLY WHERE THERE IS A MOUSE — Johan, 2026-08-23 asked for a smaller
 * font here, and the 16px above cannot simply be lowered: below 16px iOS zooms
 * the whole page when the field takes focus, and he is reading this on an
 * iPhone. `pointer: fine` is the precise test — a desktop mouse, not a screen
 * width, so an iPad on a wide layout keeps its 16px and does not zoom either.
 */
@media (pointer: fine) {
  .cv-input { font-size: .84rem; }
}
.cv-cantsend { margin: 0; font-size: .76rem; color: #92400e; }
/* What a message on a Done chat with a courtesy follow-up turns into — Johan, 2026-09-16. */
.cv-mailhint { margin: 0 .6rem .25rem; font-size: .74rem; color: #1e40af; }
.cv-actionpop-mailhint { font-size: .7rem; color: #6b7280; margin-top: .2rem; }
.cv-vmenu-optional { font-weight: 400; text-transform: none; letter-spacing: 0; color: #9ca3af; margin-left: .3rem; }
/* The face on the rating line stays upright — Johan, 2026-09-17: "the face should not be italic". */
.cv-event-face { font-style: normal; }
/* ⚠ Above the composer, not inside it: it has to be read BEFORE typing, and
   amber because it is a warning about what will happen — not an error about
   something that already went wrong. */
.cv-unreachable {
  margin: 0; padding: .35rem .6rem; font-size: .74rem;
  background: #fef3c7; color: #92400e; border-top: 1px solid #fde68a;
}

/* Only ever shown when live updates are NOT working. */
.cv-stale { color: #b45309; font-size: .66rem; font-weight: 600; white-space: nowrap; }

/* Transfer picker — under the header, where the Transfer button is. */
/* The anchor. The picker hangs off the Transfer link rather than sitting in the
   pane's flow, which is what stops it shoving the conversation down. */
.cv-transfer { position: relative; flex: 0 0 auto; }

/* A link, not a button — Johan, 2026-08-19. It opens something rather than
   doing something, which is what a link is for, and it stops competing with
   the answering switch beside it for "the control on this row". */
.cv-link {
  border: 0; background: none; cursor: pointer; padding: .12rem .25rem;
  font-size: .7rem; font-weight: 600; color: #1d4ed8; text-decoration: underline;
  text-underline-offset: 2px; min-height: 22px;
}
.cv-link:hover { color: #1e40af; }
.cv-link.is-on { color: #1e40af; text-decoration: none; background: #eff6ff; border-radius: .25rem; }

.tf-panel {
  /* FLOATS. Reading the last few messages is exactly what an agent does while
     deciding who to hand to, so the panel must not take that away. */
  position: absolute; top: calc(100% + .35rem); right: 0; z-index: 40;
  width: 17rem; max-height: 18rem;
  display: flex; flex-direction: column;
  border: 1px solid #d5dae2; border-radius: .45rem; background: #fff;
  box-shadow: 0 12px 28px -10px rgba(16, 24, 40, .28), 0 2px 6px rgba(16, 24, 40, .08);
}
/* On a phone the pane is the whole screen, so a 17rem panel pinned to the right
   edge can hang off it. Anchor to both edges instead. */
@media (max-width: 860px) {
  .tf-panel { left: 0; right: 0; width: auto; }
}
.tf-head { display: flex; gap: .3rem; padding: .45rem .55rem .3rem; }
/*
 * ⚠ SMALLER — Johan, 2026-08-26: "use much smaller font for the find an agent".
 * It was 16px to stop iOS zooming a focused input, which made it the largest
 * text in a panel of 12px agent names — the search box shouted louder than the
 * list it searches.
 *
 * 16px is kept ONLY on coarse pointers, where the zoom actually happens. A
 * mouse never triggers it, so the console gets the size that fits.
 */
.tf-filter {
  flex: 1 1 auto; border: 1px solid #d1d5db; border-radius: .3rem;
  padding: .28rem .45rem; font: inherit; font-size: .78rem;
}
@media (pointer: coarse) { .tf-filter { font-size: 16px; } }
/*
 * ⚠ A BIGGER TARGET — it is now the only VISIBLE way out (the Esc hint is
 * gone), and it was a 1.1rem glyph in a 0.3rem box. Click-outside still works
 * and is what most people use; this is for the ones who go looking.
 */
.tf-close {
  flex: 0 0 auto; border: none; background: none; cursor: pointer;
  font-size: 1.45rem; line-height: 1; color: #6b7280;
  width: 1.9rem; height: 1.9rem; border-radius: .3rem;
  display: inline-flex; align-items: center; justify-content: center;
}
.tf-close:hover { background: #f3f4f6; color: #374151; }
.tf-list { overflow-y: auto; padding: 0 .4rem; }
.tf-item {
  display: flex; align-items: baseline; gap: .5rem; width: 100%;
  text-align: left; border: none; background: none; cursor: pointer;
  padding: .35rem .4rem; border-radius: .3rem; font-size: .82rem; color: #1f2937;
}
.tf-item:hover:not(:disabled) { background: #e0e7ff; }
.tf-item:disabled { cursor: default; opacity: .7; }
.tf-item.is-active { background: #eff6ff; }
.tf-item.is-current { background: #f3f4f6; }
.tf-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tf-you { color: #6b7280; font-size: .72rem; }
.tf-avail { flex: 0 0 auto; font-size: .66rem; }
/* Availability is the point of this list, so it carries the only colour —
   the presence scheme's, see `.ag-dot`. */
.tf-avail-focused   { color: var(--presence-green); font-weight: 600; }
.tf-avail-available { color: var(--presence-green); }
.tf-avail-busy      { color: var(--presence-red); }
.tf-avail-offline   { color: var(--presence-grey); }
.tf-avail-unknown   { color: #9ca3af; font-style: italic; }
.tf-busy { flex: 0 0 auto; color: #6b7280; }
.tf-msg { margin: .45rem .5rem; font-size: .78rem; color: #6b7280; }
.tf-msg.tf-error { color: #b91c1c; }

/* Attachments. Images preview inline; everything else is a labelled link. */
.cv-attach {
  display: flex; align-items: baseline; gap: .4rem;
  text-decoration: none; color: inherit;
}
a.cv-attach:hover .cv-attach-name { text-decoration: underline; }
.cv-attach-name { font-size: .82rem; font-weight: 500; }
.cv-attach-size { font-size: .68rem; opacity: .7; }
.cv-attach-img { display: block; margin-bottom: .3rem; }
.cv-attach-img img {
  display: block; max-width: 100%; max-height: 14rem;
  border-radius: .3rem; background: #fff;
}
.cv-file-input { display: none; }
/*
 * ⚠ NO FRAME on either icon — Johan, 2026-08-21. They are glyphs, not buttons
 * that happen to hold a glyph, and a box round each one made the composer read
 * as three controls of equal weight when only Send is the act. The chrome comes
 * back on hover, which is where it is actually useful: it says "this is
 * pressable" at the moment you are about to press it.
 *
 * Send keeps its frame and its fill. It is the one thing down here that sends.
 */
.cv-btn-attach, .cv-btn-tpl {
  border-color: transparent; background: transparent; color: #6b7280;
}
.cv-btn-attach:hover:not(:disabled), .cv-btn-tpl:hover {
  background: #f3f4f6; color: #374151;
}
.cv-btn-attach { flex: 0 0 auto; min-width: 2.6rem; }
/* ⚠ Mirrored to match the visitor's own widget — Johan, 2026-08-21. On the
   GLYPH, not the button: the upload percentage shares that element, and a
   mirrored "42%" is not a percentage. */
.cv-clip { display: inline-block; transform: scaleX(-1); }

/* An icon, not the word "Templates" — the label cost more width than the reply
   box could spare, and on a three-pane layout that box is what an agent
   actually needs. The word lives on in the tooltip and the aria-label. */
.cv-btn-tpl {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  min-width: 2.2rem; padding-left: .4rem; padding-right: .4rem; color: #4b5563;
}
/* Open: it is holding a panel over the thread, so it stays lit — a bare glyph
   would leave nothing saying where the popup came from. */
.cv-btn-tpl.is-on { background: #e0e7ff; border-color: #a5b4fc; color: #3730a3; }

/*
 * ⚠ THE PICKER FLOATS, anchored to its button — Johan, 2026-08-21: "a popup
 * instead of pushing rest down".
 *
 * In the flow it shoved the conversation up by its own height, so the message
 * you were answering left the screen at the moment you went looking for words
 * to answer it with. Anchored here it covers the thread instead, exactly as the
 * Assign picker does.
 */
/*
 * THE GRIP — top centre, straddling the pane's own top edge so it reads as
 * belonging to the PANE rather than to the header's first row.
 *
 * ⚠ It is the ONLY thing that starts a drag. The header used to be draggable
 * whole, which turned every attempt to select the session id into a drag.
 */
.cv-grip {
  /* ON the top border, like the rail cards' section titles — Johan,
     2026-09-15: "can the drag icon be placed on the top border of the ACI,
     kind of what we now do with the section titles". Half above, half below,
     on the page ground so the border breaks behind the dots. */
  position: absolute; top: -.5rem; left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 2.4rem; height: 1rem; z-index: 4;
  color: #94a3b8; cursor: grab; border-radius: .3rem;
  background: var(--bs-body-bg, var(--tblr-body-bg, #f2f2f7));
}
.cv-grip:hover { color: #475569; background: #e2e8f0; }
.cv-grip:active { cursor: grabbing; }

.cv-tplwrap { position: relative; flex: 0 0 auto; }
.cv-tplwrap .tpl-panel {
  position: absolute; bottom: calc(100% + .4rem); left: 0;
  /* Wider than the button it hangs off, and never wider than the pane —
     Johan, 2026-09-18: "display it wider and taller"; titles and two lines
     of text per row need the room. */
  width: max(34rem, 100%); max-width: calc(100vw - 2rem);
  border: 1px solid #d1d5db; border-radius: .4rem;
  box-shadow: 0 8px 24px rgba(15, 23, 42, .16);
  z-index: 30;
}
.tpl-panel {
  flex: 0 0 auto; display: flex; flex-direction: column;
  max-height: min(34rem, 70vh); border-top: 1px solid #e5e7eb; background: #f9fafb;
}
.tpl-head { display: flex; gap: .3rem; padding: .4rem .5rem .3rem; }
.tpl-filter {
  flex: 1 1 auto; border: 1px solid #d1d5db; border-radius: .3rem;
  padding: .25rem .45rem; font: inherit; font-size: 16px;  /* no iOS zoom */
}
/* Johan, 2026-09-14: "Search for template", in italic. */
.tpl-filter::placeholder { font-style: italic; color: #9ca3af; }
.tpl-close {
  flex: 0 0 auto; border: none; background: none; cursor: pointer;
  font-size: 1.1rem; line-height: 1; color: #6b7280; padding: 0 .3rem;
}
.tpl-list { overflow-y: auto; padding: 0 .35rem .35rem; }
.tpl-group { margin-bottom: .3rem; }
.tpl-cat {
  font-size: .62rem; text-transform: uppercase; letter-spacing: .05em;
  color: #9ca3af; padding: .25rem .2rem .1rem;
}
.tpl-item {
  display: flex; align-items: flex-start; gap: .4rem; width: 100%;
  text-align: left; border: none; background: none; cursor: pointer;
  padding: .3rem .35rem; border-radius: .3rem; font-size: .8rem; color: #1f2937;
}
.tpl-item.is-active { background: #e0e7ff; }
.tpl-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .05rem; }
.tpl-title { font-weight: 600; }
/* Two lines of the text, then an ellipsis — enough to recognise it, not the whole answer. */
.tpl-text {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; white-space: normal; line-height: 1.3;
}
.tpl-title + .tpl-text { color: #4b5563; font-size: .74rem; }
.tpl-lang {
  flex: 0 0 auto; font-size: .6rem; text-transform: uppercase;
  color: #6b7280; background: #e5e7eb; border-radius: 999px; padding: .05rem .3rem;
}
.tpl-msg { margin: .4rem .35rem; font-size: .76rem; color: #6b7280; }
.tpl-msg.tpl-error { color: #b91c1c; }
.tpl-foot {
  margin: 0; padding: .25rem .55rem .35rem; font-size: .62rem; color: #9ca3af;
  border-top: 1px solid #e5e7eb;
}
.cv-foot-note { margin: 0; padding: 0 .7rem .5rem; font-size: .68rem; color: #9ca3af; }
.cv-empty { padding: 2rem 1rem; text-align: center; color: #6b7280; font-size: .85rem; }
.cv-empty.cv-error { color: #b91c1c; }

/* Phone: one pane at a time. The conversation replaces the list, and the back
   arrow appears — the platform's push/pop rather than a reimplementation. */
/* THE REFINEMENT BAR, under the state row.
   Compact and content-sized — Johan asked for the old console's capability
   "smooth and not too large", and eight equal-width boxes is what made the old
   one heavy. Wraps rather than scrolls: a filter that has scrolled out of sight
   is a filter the agent cannot see is on. */
/* Measured, not the window — see the comment in QueueFilters.tsx. */
.qf-shell { container-type: inline-size; }

/*
 * How wide the filter bar runs at its comfortable size: the sum of the field
 * bases plus their gaps. The fields never GROW (`flex: 0 1 basis`), so the bar
 * stops here however wide the column gets, and shrinks below it when squeezed.
 *
 * Named because the empty-list message is centred on the BAR rather than on the
 * column — see `.queue-empty`. Keep the two in step: change a field's basis and
 * this moves with it.
 */
:root { --queue-bar-width: 53rem; }

/*
 * ONE COLOUR PER STATE, for every surface that names it — Johan, 2026-08-24:
 * "make sure that design carries over to the related pills shown in session
 * list cards and in the ACIs".
 *
 * ⚠ They had DRIFTED. The same state carried three near-identical values
 * depending on where it was drawn — urgent was #c81e1e on the tab, #b91c1c on
 * the row and in the pane; important #b45309 against #92400e. Nobody sees two
 * of those side by side and calls it a bug, and that is exactly why it accretes
 * until the console stops looking like one thing.
 *
 * Three tokens each, because every surface needs the same three answers:
 *   base  the type, and the fill when something is SOLID
 *   tint  the quiet ground a descriptive pill sits on
 *   edge  the border that says "this is one of the four"
 *
 * Change a state's colour here and the tab, the row pill and the pane pill move
 * together — which is the only way they stay together.
 */
:root {
  --st-urgent:    #c81e1e; --st-urgent-tint:    #fdeaea; --st-urgent-edge:    #f0b4b4;
  /*
   * ⚠ AMBER, AND IT CANNOT CARRY WHITE — Johan, 2026-08-24: "urgent and
   * important have too similar colours".
   *
   * He is right, and the cause is worth writing down because it will catch the
   * next person too. Every solid pill used ONE token for both the fill and the
   * type, with white on top — so the fill had to reach 4.5:1 against white.
   * Orange only gets that dark by going red (#d9480f) or brown (#b45309).
   * Optimising for the contrast rule walked the colour straight into red, and
   * then the two loudest buckets differed by nothing a glance can use.
   *
   * The way out is to stop insisting on white. Important fills with a real
   * amber and takes NEAR-BLACK type — 7.4:1, better than any of the white
   * pairs — while the ink token stays dark enough to read as small type on a
   * pale ground. Now the two differ by hue AND by which way round they are,
   * which no amount of squinting can merge.
   */
  --st-important: #b45309; --st-important-tint: #fff7ea; --st-important-edge: #f5d9a6;
  /*
   * ⚠ DARK GREEN, not slate. Slate said "inactive"; Replied is the bucket
   * where things went RIGHT — we answered, nobody is waiting. Green says that
   * without a word, and it is the one bucket an agent should feel good about
   * sitting in.
   */
  --st-replied:   #15803d; --st-replied-tint:   #ebf6ee; --st-replied-edge:   #b6dcc2;
  --st-bot:       #2f5da8; --st-bot-tint:       #e8effa; --st-bot-edge:       #cddcf3;
  --st-action:    #6d28d9; --st-action-tint:    #f3e8ff; --st-action-edge:    #cfc0f0;

  /*
   * ⚠ A FIFTH AND SIXTH TOKEN: what a SOLID surface is filled with, and what
   * is written on it. Split from the base because the base has a second job —
   * small type on a pale ground — and one value cannot be both a legible ink
   * at 12px and a fill you can read white off. Chasing one requirement with a
   * single token is exactly how Important ended up red.
   *
   * Every pair is checked against WCAG AA for small text:
   *   urgent    #fff on #c81e1e  5.9:1
   *   important #42210a on #f59e0b  7.4:1   ← the odd one out, on purpose
   *   replied   #fff on #15803d  4.9:1
   *   bot       #fff on #2f5da8  6.4:1
   *   action    #fff on #6d28d9  8.6:1
   * Add a state and check it here before using it.
   */
  --st-urgent-fill:    #c81e1e; --st-urgent-ink:    #fff;
  /* ⚠ Darker than the amber above, and WHITE on it — Johan, 2026-09-14: "make the orange a
     bit darker so that we can use white text/number also there". #fff on #d97706 is 3.2:1,
     under AA for small text; the pills are bold, and a near-black ink on amber read as a
     different kind of thing from the other solid pills. */
  --st-important-fill: #d97706; --st-important-ink: #fff;
  --st-replied-fill:   #15803d; --st-replied-ink:   #fff;
  --st-bot-fill:       #2f5da8; --st-bot-ink:       #fff;
  --st-action-fill:    #6d28d9; --st-action-ink:    #fff;

  /*
   * ⚠ A FOURTH TOKEN: the edge an EMPTY bucket keeps — Johan, 2026-08-24:
   * "still need to highlight, but less… but 0 so all good".
   *
   * An empty tab drops its tint and its coloured type, but not its identity: a
   * faint edge in its own colour still says WHICH slot is clear, so "Urgent
   * looks good" is read from the shape rather than from the word.
   */
  --st-urgent-soft:    #f6dcdc; --st-important-soft: #fae8c8;
  --st-replied-soft:   #d3e9da; --st-bot-soft:       #e3ebf7;
  --st-action-soft:    #e4dbf8;
}

.qf {
  display: flex; flex-wrap: wrap; align-items: center; gap: .3rem;
  padding: .4rem 0 .1rem;
}
/*
 * ONE ROW UNTIL A THIRD OF THE ROOM IS GONE, then two.
 *
 * The fields are `0 1 basis`: they never grow past their comfortable width and
 * they shrink under pressure to a floor that still lets each one be read.
 *
 *   comfortable  ~53rem    + Clear ~3.2 = ~56.2rem
 *   floor        ~33.7rem  + gaps 2.4 + Clear = ~39.3rem
 *
 * ⚠ Both grew when channel and store moved in from the sidebar. That gives back
 * some of the 20% cut — the trade for those two never disappearing.
 *
 * ⚠ The threshold must clear the FLOOR WITH CLEAR SHOWING, not without it. With
 * `nowrap` an item that no longer fits overflows the container instead of
 * wrapping, and Clear appears exactly when filters are on — which is exactly
 * when the bar matters. 40rem is ~71% of comfortable and just above the floor,
 * so the row compresses all the way down and only then splits.
 */
/*
 * ⚠ ONE ROW, ALWAYS — Johan, 2026-08-23: "Filter should always fit on the main
 * row; make the visitor email field narrower if needed".
 *
 * It used to wrap below 40rem, which put Filter on a line of its own — a whole
 * row of chrome for one button, on the width where rows are scarcest. The
 * fields shrink instead, which is the right trade: a narrower email box is
 * still a usable email box, and Filter is the control that opens everything
 * else.
 *
 * Safe under `nowrap` because every field is `0 1 basis` with an explicit
 * `min-width`, so they compress rather than overflow; the floor below drops
 * that min-width further once the column is genuinely tight.
 */
.qf-shell:not(.is-range) .qf { flex-wrap: nowrap; }
@container (max-width: 40rem) {
  .qf-shell:not(.is-range) .qf-field { min-width: 3rem; }
}
/* An explicit date range adds two date pickers — far more than the row can
   absorb. It wraps whatever the width, rather than crushing everything else. */
.qf-shell.is-range .qf { flex-wrap: wrap; }

.qf-field {
  border: 1px solid #e5e7eb; border-radius: .3rem; background: #fff;
  padding: .25rem .4rem; font-size: .72rem; min-height: 28px;
  /* An explicit min-width is what lets these shrink at all: a flex item
     defaults to `min-width: auto`, which for a text input is its intrinsic
     size, and the row would push past its container rather than compress. */
  flex: 0 1 7.2rem; min-width: 3.6rem;
}
.qf-field::placeholder { color: #9ca3af; }
.qf-field:focus { outline: 2px solid #1d4ed8; outline-offset: -1px; }
/* A set field must be visible as set at a glance — the whole failure mode of a
   filter bar is a stale filter nobody notices. */
.qf-field.is-set { border-color: #1d4ed8; background: #eff6ff; font-weight: 600; }
/* Floors sized to what each control must still be able to SAY: a select has to
   show its longest option, an id needs a few characters to be recognisable. */
/* All 20% narrower than they were — Johan, 2026-08-19: the list column was
   reserving more width than the list needs, at the expense of the panes. The
   FLOORS came down with the bases: a floor left at its old value would simply
   stop the field shrinking and hand the saving back. */
.qf-range { flex-basis: 6.4rem; min-width: 5rem; }
.qf-date { flex-basis: 6.8rem; min-width: 6rem; }
.qf-id { flex-basis: 5.2rem; min-width: 3.4rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
/* Channel and store: the two shortest answers on the row, so the two narrowest
   fields. They earn their place by being ALWAYS present rather than by being
   large. */
.qf-where { flex-basis: 5.5rem; min-width: 4.2rem; }
/* ⚠ GREY WHILE NOTHING IS CHOSEN, matching the text inputs beside it — Johan,
   2026-08-21. A select whose first option is a label reads as a VALUE in normal
   ink, so "Actions (2)" looked like an active filter showing two results. In
   placeholder grey it reads as the prompt it is. */
.qf-action { flex-basis: 8.5rem; min-width: 7rem; }
.qf-action.is-placeholder { color: #9ca3af; }
/*
 * ⚠ VIOLET WHEN SET — Johan, 2026-08-24 named Agent Action as the fourth thing
 * the console is really about, alongside the three buckets.
 *
 * It was the quietest control on the bar: a grey select for the one axis that
 * means somebody has to DO something. Violet is that meaning everywhere else
 * here — the action pill, the pending-transfer mark, the owed stripe — so
 * carrying it when the filter is on costs nothing to learn.
 *
 * Only when SET. An always-violet select would be a filter shouting while it
 * narrows nothing, which is the habit these colours exist to avoid.
 */
.qf-action.is-set {
  border-color: #7c3aed; background: #f5f3ff; color: #5b21b6; font-weight: 600;
}
select.qf-field { flex-basis: 6.8rem; min-width: 5.5rem; }
/* Never shrinks — it is the way out of a filtered state, and a squeezed
   "Clear" is the last thing that should become unreadable. Counted into the
   threshold above for exactly that reason. */
.qf-clear { flex: 0 0 auto; }
.qf-dash { color: #9ca3af; font-size: .75rem; }
.qf-clear {
  border: 0; background: none; color: #1d4ed8; font-size: .72rem;
  cursor: pointer; padding: .25rem .3rem; min-height: 28px;
}
.qf-clear:hover { text-decoration: underline; }
/* ── The filter popup and its pills ────────────────────────────────────────
   Only session id and shopper email stay on the bar; everything else lives in
   the popup, with the active ones shown as pills underneath. Same division
   jBKB list pages use — the fields you type to FIND something stay out, the
   ones that NARROW go behind a button. */
.qf-popwrap { position: relative; flex: 0 0 auto; }
.qf-btn {
  display: inline-flex; align-items: center; gap: .3rem;
  border: 1px solid #d1d5db; background: #fff; border-radius: .3rem;
  font-size: .72rem; padding: .25rem .55rem; min-height: 28px; cursor: pointer;
  color: #374151;
}
.qf-btn:hover { border-color: #c7d2fe; }
/* The clean-up controls, far right of the bar and supervisor-only. Set apart
   because they are not filters: one narrows AND arms the selection, the other
   is a mode. See the rightSlot comment in Queue.tsx. */
.qf-cleanup { display: inline-flex; align-items: center; gap: .3rem; margin-left: .3rem; }

/*
 * The quiet sweep — it states a number and asks once, so it reads as a
 * sentence in the filter bar rather than as another filter chip. Amber while
 * open: a doubt to settle, not a failure (same reasoning as .cv-doubt).
 */
.qf-sweep { display: inline-flex; align-items: center; gap: .35rem; margin-left: .3rem; flex-wrap: wrap; }
.qf-sweep.is-open {
  padding: .15rem .4rem; border: 1px solid #f0dcb8; border-radius: .35rem; background: #fdf1de;
}
.qf-sweep-count { font-size: .7rem; color: #78350f; font-weight: 600; }
.qf-sweep-age { display: inline-flex; align-items: center; gap: .25rem; font-size: .68rem; color: #78350f; }
.qf-sweep-age select { font-size: .68rem; padding: 0 .15rem; }
.qf-sweep-done { font-size: .68rem; color: #15803d; }
.qf-sweep-error { font-size: .68rem; color: #b91c1c; }
.qf-sweep-ask { font-size: .7rem; color: #78350f; }
.qf-btn.is-set { border-color: #1d4ed8; background: #eff6ff; color: #1d4ed8; font-weight: 600; }
.qf-btn-count {
  background: #1d4ed8; color: #fff; border-radius: 999px;
  font-size: .6rem; font-weight: 700; padding: 0 .3rem; min-width: 1rem; text-align: center;
}
.qf-pop {
  position: absolute; z-index: 30; top: calc(100% + .3rem); left: 0;
  background: #fff; border: 1px solid #e5e7eb; border-radius: .4rem;
  box-shadow: 0 8px 24px rgba(0,0,0,.12);
  padding: .5rem; min-width: 19rem;
  display: flex; flex-direction: column; gap: .35rem;
}
.qf-poprow { display: flex; align-items: center; gap: .45rem; }
.qf-poprow .qf-field { flex: 1 1 auto; min-width: 0; }
.qf-poplabel { flex: 0 0 5.6rem; font-size: .7rem; color: #6b7280; }
.qf-poprow-dates .qf-field { flex: 1 1 0; }
/*
 * ⚠ THE CHECKBOX ROW HAD NO STYLE AT ALL — Johan, 2026-08-23: "make 'was
 * involved' smaller font, a bit of space before the checkbox, and vertically
 * aligned with the checkbox".
 *
 * Every other row in this popup puts a 5.6rem label against a full-width field.
 * This one put a bare input and a full-size word straight after the label, so
 * it read as a different kind of control and sat off the baseline the rest of
 * the column establishes.
 *
 * The indent lines the checkbox up with the left edge of the fields above it,
 * so the popup reads as one column of answers rather than six rows and an
 * exception.
 */
.qf-poprow-check .qf-checkwrap {
  display: inline-flex; align-items: center; gap: .4rem;
  padding-left: .35rem;
}
.qf-poprow-check .qf-checkwrap input[type="checkbox"] {
  margin: 0; flex: 0 0 auto;
}
/* Same size and colour as the label opposite it — it is the other half of the
   same sentence ("Agent … was involved"), not a heavier thing beside it. */
.qf-poprow-check .qf-checkwrap > span {
  font-size: .7rem; color: #4b5563; line-height: 1;
}

/* The pills ARE the visible state of the popup — see FilterPill. */
.qf-pills { display: flex; flex-wrap: wrap; gap: .3rem; padding: .3rem 0 0; }
.qf-pill {
  display: inline-flex; align-items: center; gap: .25rem;
  background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 999px;
  font-size: .66rem; padding: .1rem .2rem .1rem .45rem; color: #1e40af;
  max-width: 16rem;
}
.qf-pill-label { color: #60a5fa; }
.qf-pill-value { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qf-pill-x {
  border: 0; background: none; cursor: pointer; color: #60a5fa;
  font-size: .85rem; line-height: 1; padding: 0 .2rem;
}
.qf-pill-x:hover { color: #1e40af; }

/* ── Paging, in jBKB's shape ───────────────────────────────────────────────
   Same order and wording as the list pages there: Records · Rows per page ·
   Page X of Y · « ‹ › ». Right-aligned under the list, where you arrive after
   scrolling to the end. */
.qp {
  display: flex; align-items: center; justify-content: flex-end;
  gap: 1rem; padding: .45rem .2rem .1rem;
  font-size: .72rem; color: #6b7280; flex-wrap: wrap;
}
.qp-num { color: #111827; font-variant-numeric: tabular-nums; }
.qp-size { display: flex; align-items: center; gap: .35rem; }
.qp-size select {
  border: 1px solid #d1d5db; border-radius: .3rem; background: #fff;
  font: inherit; font-size: .72rem; padding: .15rem .3rem; min-height: 26px;
}
.qp-page { display: inline-flex; align-items: center; gap: .35rem; font-variant-numeric: tabular-nums; }
/* A quiet spinner rather than a moving number — the page label must not jump
   while a fetch is in flight. */
.qp-busy {
  width: .6rem; height: .6rem; border-radius: 50%;
  border: 2px solid #bfdbfe; border-top-color: #1d4ed8;
  animation: qp-spin .7s linear infinite;
}
@keyframes qp-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .qp-busy { animation: none; } }
.qp-nav { display: inline-flex; gap: .25rem; }
.qp-btn {
  border: 1px solid #d1d5db; background: #fff; border-radius: .3rem;
  min-width: 26px; min-height: 26px; cursor: pointer;
  font: inherit; font-size: .78rem; color: #374151; line-height: 1;
}
.qp-btn:hover:not(:disabled) { border-color: #c7d2fe; background: #f8faff; }
.qp-btn:disabled { opacity: .4; cursor: not-allowed; }

/* ⛔ THE BUTTON GETS ITS OWN LINE — Johan, 2026-08-25: "move clear filters to
   next row". Inline it sat mid-sentence, so the way out only appeared after
   reading the explanation. A column centres both and puts the action under the
   reason, which is the order they are used in. */
.queue-empty-why {
  font-size: .78rem; color: #6b7280; margin-top: .3rem;
  display: flex; flex-direction: column; align-items: center; gap: .4rem;
}

/* "All" is a real choice with a real number, so it reads like the other tabs
   rather than like an empty one — Johan, 2026-08-20: "should display the number
   of sessions and also not be faded". */
.queue-tab-all { color: #374151; }
.queue-tab-all .queue-tabcount { color: #374151; }

/* The lookups sit apart from the state pills — they are not work. */
.queue-tab-lookup { font-style: normal; padding-left: .45rem; padding-right: .45rem; }
.queue-tab-ico { width: .95rem; height: .95rem; display: block; }

/* THE PHONE'S TAB STRIP. Rendered only on a phone (React decides, not CSS —
   see useIsPhone), so it needs no desktop rules beyond not existing there. */
.cv-tabs {
  display: flex; gap: .25rem; overflow-x: auto; padding: .35rem .5rem;
  background: #f9fafb; border-bottom: 1px solid #e5e7eb;
  position: sticky; top: 0; z-index: 2;
  /* Snapping keeps a half-scrolled tab from being the thing under the thumb. */
  scroll-snap-type: x proximity;
}
.cv-tab {
  scroll-snap-align: start;
  display: flex; align-items: center; gap: .3rem; flex: 0 0 auto;
  max-width: 9rem; min-height: 34px;   /* a thumb target, not a mouse target */
  border: 1px solid #e5e7eb; background: #fff; border-radius: .3rem;
  padding: .25rem .5rem; font-size: .75rem; cursor: pointer;
}
.cv-tab.is-on { border-color: #1d4ed8; background: #eff6ff; font-weight: 600; }
/* Set apart from the conversations — it is a different KIND of destination,
   and it should not read as one more chat in the row. */
.cv-tab-queue { background: #f3f4f6; color: #374151; margin-right: .3rem; }
.cv-tab-queue.is-on { background: #eff6ff; }
.cv-tab-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cv-tab-dot {
  width: .45rem; height: .45rem; border-radius: 50%; flex: 0 0 auto;
  /* Inherits the bucket colour from .sc-*, whose padding would distort it. */
  padding: 0;
}

@media (max-width: 860px) {
  /*
   * ⚠ FLEX, so the tab strip can be ordered FIRST — Johan, 2026-08-21: "after
   * clicking Back the session list opens, but there is no icon to go back to
   * the ACI".
   *
   * It was there all along, and unreachable: the strip is rendered after the
   * queue page in the DOM, so with the list showing it sat BELOW the whole
   * list, off the bottom of the screen. Sticky could not save it — an element
   * only sticks once you have scrolled to it.
   */
  .queue-shell { display: flex; flex-direction: column; }
  /*
   * ⚠ `flex: 0 0 auto` — Johan, 2026-08-23: "this happens sometimes, height of
   * the open ACI buttons too little", with the tab row sliced in half.
   *
   * The shell became a fixed-height flex column when the transcript was made to
   * scroll inside the pane (6d0e5d8). Flex children shrink by default, so once
   * the conversation was tall enough to want more room the strip gave up its
   * own height to it and the tabs were clipped. "Sometimes" was "whenever the
   * content is tall" — which is most conversations.
   *
   * It is navigation: it is the only way back to the other conversations, so it
   * is the one thing here that must never be squeezed.
   */
  .cv-tabs { order: -1; flex: 0 0 auto; }
  .queue-shell.has-open .queue-page { display: none; }
  /*
   * ⚠ NOT edge-to-edge — Johan: "give it some left buffer, even horizontally
   * centre it". Bubbles starting hard against the window edge read as a
   * rendering fault, and on a wide-ish phone the full width makes lines too
   * long to scan comfortably.
   */
  /*
   * ⚠ THE PANE OWNS THE SCREEN AND THE TRANSCRIPT SCROLLS INSIDE IT — Johan,
   * 2026-08-21: "when scrolling in chat should I not still see the header? thus
   * truly scroll in the chat".
   *
   * It was `min-height: calc(100vh - 6rem)` with no maximum, so the pane simply
   * grew to the length of the conversation and the PAGE scrolled. That takes
   * the header, the owner, Done/Assign and the reply box off the top and bottom
   * of the screen — everything you need while reading disappears exactly when
   * you start reading.
   *
   * Bounding the shell instead makes `.cv-transcript`'s own `overflow-y: auto`
   * do the work it was always written to do, so the header and composer stay
   * put and only the messages move.
   *
   * ⚠ `dvh`, not `vh`. Chrome on iOS puts its address bar at the BOTTOM and
   * collapses it as you scroll; `vh` is the LARGEST viewport, so the composer
   * would sit behind that bar until you scrolled. `dvh` tracks the real height.
   * Johan is using Chrome on an iPhone, which is exactly the case `vh` gets
   * wrong.
   */
  .queue-shell { min-height: 0; }
  /* ⚠ NOTHING may push the page wider than the screen. A single over-wide
     child scrolls the whole document sideways, and then every row, every chip
     and the first view button are all clipped at once — which is what Johan
     photographed on 2026-08-21. Belt and braces: let the tab row wrap, and let
     the list column go as narrow as it has to. */
  .queue-shell, .queue-page, .queue-body { max-width: 100%; min-width: 0; }
  /* ⚠ AND drop the fixed basis with it — a 36rem basis that cannot shrink is
     exactly the over-wide child this block exists to prevent. */
  .queue-main-col { min-width: 0; flex: 1 1 auto; }
  .queue-tabs { flex-wrap: wrap; row-gap: .3rem; }
/* Recent and History travel together — see the comment at their JSX. `nowrap`
   here, not on `.queue-tabs`, so the row can still break under real pressure
   without ever orphaning History on a line of its own. */
.queue-lookups {
  display: inline-flex; flex-wrap: nowrap;
  /* ⚠ BETWEEN the two, not before the pair — Johan, 2026-08-25: "add a bit of
     space between Recent and History". They are separate destinations, not two
     halves of one control, and at .3rem they read as a segmented toggle. */
  gap: .3rem;
}
  .queue-tabs-push { flex-basis: 100%; height: 0; }
  .cv-stack {
    max-width: 34rem; margin-left: auto; margin-right: auto;
    padding: 0 .5rem .5rem; width: 100%;
    flex: 1 1 auto; min-height: 0;
  }
  .cv-panes { min-height: 0; }
  .cv-pane {
    position: static; height: 100%; max-height: none; min-height: 0;
    border: 1px solid #e5e7eb; border-radius: .4rem;
    overflow: hidden;
  }
  /* The list scrolls in its own right, for the same reason. */
  .queue-shell .queue-page { min-height: 0; overflow-y: auto; }
  .cv-back { display: block; }
  /*
   * ⚠ NO GRIP ON A PHONE, and it was actively broken here. `.cv-pane` goes
   * `position: static` in this layout, so the absolutely-positioned grip lost
   * its containing block and climbed the tree — it rendered as six dots at the
   * top of the blue navbar, which is what Johan photographed.
   *
   * It has nothing to do anyway: one conversation is on screen at a time, so
   * there is no order to drag it into.
   */
  .cv-grip { display: none; }
  .cv-bubble { max-width: 88%; }
}

/*
 * WHERE THE CONVERSATION IS UP TO — one pill, four values, always present.
 * Replaces `.queue-closed`, which appeared only for a finished session and so
 * had to be noticed before it could be read.
 *
 * ⚠ Sentence case like the bucket pills beside it (Johan, 2026-08-23) and QUIET
 * in every state. None of these is work: they say how the person can be
 * reached, which the agent needs when deciding what to write, not whether to
 * write at all.
 *
 * The two end states share a look, so the WORD is what distinguishes them —
 * "over" is the fact; which kind is the detail.
 */
/*
 * ⛔ TEXT, NOT A FILLED PILL — Johan, 2026-08-26: "just use the text 'online' in
 * green... and perhaps even offline in faded grey".
 *
 * It appears on every row, so a filled chip four times over made presence
 * compete with the bucket pill that says what to DO about the session. As plain
 * words it is read when looked for and ignored otherwise, which is the right
 * weight for a fact that changes nothing on its own.
 *
 * ⚠ ONLY "online" carries colour. Green means somebody is there — the same
 * meaning it has on the agent dots in the sidebar, which is exactly why the
 * green DOT that used to sit here had to go: it made green mean two subjects.
 * The three ended states are all "nobody is there" and differ only in how, so
 * the WORD distinguishes them and the tone does not have to.
 */
.qr-visitor {
  flex: 0 0 auto; font-size: .66rem; font-weight: 600;
  padding: .05rem .1rem; letter-spacing: .01em;
}
.qr-visitor-online  { color: #15803d; }
.qr-visitor-offline,
.qr-visitor-closed,
.qr-visitor-expired { color: #9ca3af; font-weight: 500; }

/* "Load earlier messages" — offered, not just announced. The endpoint pages at
   50, and a note saying the thread was incomplete gave the agent no way to
   complete it. */
.cv-older { display: flex; justify-content: center; padding: .3rem 0 .5rem; }
.cv-older-btn {
  border: 1px solid #d1d5db; background: #fff; border-radius: 999px;
  padding: .2rem .7rem; font-size: .72rem; color: #374151; cursor: pointer;
}
.cv-older-btn:hover:not(:disabled) { background: #f3f4f6; }
.cv-older-btn:disabled { opacity: .6; cursor: default; }

/* Agent note — internal, never seen by the shopper. Sits under the header so it
   is read before the conversation, which is when it is useful. */
.cv-note-open {
  display: block; width: 100%; text-align: left; cursor: pointer;
  border: none; border-bottom: 1px solid #e5e7eb; background: #fffbeb;
  padding: .3rem .6rem; font-size: .76rem; color: #78350f;
}
.cv-note-open:hover { background: #fef3c7; }
.cv-note-open:not(.has-note) { background: transparent; color: #9ca3af; }
.cv-note-open:not(.has-note):hover { background: #f9fafb; }
.cv-note-tag {
  font-size: .6rem; text-transform: uppercase; letter-spacing: .05em;
  background: #fde68a; color: #78350f; border-radius: 999px; padding: .05rem .35rem;
  margin-right: .3rem;
}
.cv-note-add { font-size: .72rem; }
.cv-note-edit {
  border-bottom: 1px solid #e5e7eb; background: #fffbeb; padding: .4rem .6rem;
}
.cv-note-input {
  width: 100%; resize: vertical; border: 1px solid #fcd34d; border-radius: .3rem;
  padding: .3rem .4rem; font: inherit;
  /* ⚠ 16px IS LOAD-BEARING ON TOUCH: iOS Safari zooms the whole page when a
     focused field is smaller, and the zoom does not undo itself. Kept as the
     default so a phone never gets the smaller size by accident. */
  font-size: 16px;
  background: #fff;
}
/*
 * ⛔ SMALLER ON A REAL POINTER — Johan, 2026-08-25: "display the note text in
 * smaller font (when opening)". 16px in a pane whose every other control is
 * .74rem made the note look like a different application.
 *
 * `pointer: fine` is the guard: a mouse cannot trigger the iOS zoom, so the
 * only devices that keep 16px are the ones that need it.
 */
@media (pointer: fine) {
  .cv-note-input { font-size: .78rem; }
}
.cv-note-actions {
  display: flex; align-items: center; justify-content: flex-end; gap: .4rem;
  margin-top: .3rem;
}
/* Clear sits alone at the left, away from Save. */
/* Up against the box it empties — Johan, 2026-09-21 — not centred on the taller buttons. */
.cv-note-clear { margin-right: auto; font-size: .72rem; align-self: flex-start; margin-top: -.15rem; }
.cv-note-clear:disabled { opacity: .4; cursor: default; }
.cv-note-error { flex: 1 1 auto; font-size: .7rem; color: #b91c1c; }
.cv-note-cancel, .cv-note-save {
  border: 1px solid #d1d5db; background: #fff; border-radius: .3rem;
  padding: .2rem .5rem; font-size: .72rem; cursor: pointer; color: #374151;
}
.cv-note-save:not(:disabled) { background: #78350f; border-color: #78350f; color: #fff; }
.cv-note-cancel:disabled, .cv-note-save:disabled { opacity: .6; cursor: default; }

/* The handover reason — WHY the bot passed this over. Distinct from the amber
   transfer flag: that says a handover happened, this says what for. */
/* The handover reason, exactly as jBKE authored it — "InfoRequest", not
   "INFOREQUEST". It is one of five fixed values and the camel case is part of
   how it is recognised. */
.queue-reason {
  font-size: .64rem; letter-spacing: .01em;
  background: #ede9fe; color: #5b21b6; border-radius: 999px; padding: .05rem .4rem;
}

/* --- Queue: views sidebar + tabs ------------------------------------------
   Shape from how real agent consoles work (Intercom Inbox, Front, Zendesk,
   Help Scout, Gmail): views on the left, a filter strip above the list.

   Colour from jBKB, which reserves BLUE for actions and uses near-black for
   selection. A blue selected row and a blue "Send" button teach two different
   meanings for one colour, and the one that loses is the button. */

.queue-body { display: flex; gap: 1rem; align-items: flex-start; }

/* Narrower, and everything left-justified. The counts used to be pushed to the
   right edge, which set the width from the longest label plus a gap — space the
   list needs more. Left-justified they sit next to the name and the whole rail
   costs ~20% less. */
/*
 * ⚠ FIXED, AND IT MUST STAY FIXED — Johan, 2026-08-23: "the who select column
 * should NOT get wider if the agent name is long".
 *
 * It did, despite the `0 0` basis: a flex item defaults to `min-width: auto`,
 * so "Agent 1 AllSolutions" pushed the whole rail out and the list jumped
 * sideways every time the selection changed. `min-width: 0` is what actually
 * pins it; the basis alone never could.
 *
 * ⚠ 12rem, up from 10.5 — Johan: "I think we can make the column a bit wider
 * as it's fixed width". `LAYOUT.sidebar` in paneFit.ts MUST match: it reserves
 * this width when deciding how many conversation panes fit, and a rail wider
 * than the arithmetic believes is exactly how a pane ends up on top of the
 * session list.
 */
.queue-views {
  /* 11rem, was 12 — Johan, 2026-09-15: a rem for the state row; the cards are a word and a count. paneFit.ts LAYOUT.sidebar MUST match. */
  flex: 0 0 11rem; min-width: 0;
  display: flex; flex-direction: column; gap: .7rem;
}
.queue-viewgroup {
  display: flex; flex-direction: column; gap: .2rem;
  /* A card, so the sidebar reads as a panel rather than as text floating on the
     page ground. */
  background: #fff;
  border: 1px solid #dfe3e8;
  border-radius: .5rem;
  padding: .35rem;
}
.queue-viewlabel {
  font-size: .58rem; text-transform: uppercase; letter-spacing: .07em;
  color: #98a2ad; font-weight: 600; padding: .25rem .45rem .1rem;
}

/*
 * ⚠ FLEX, NOT GRID. The breakdown pills sit on their own line spanning the
 * whole card, and as a grid item spanning both columns their ~124px minimum was
 * distributed BACK into the two columns above — so the name column was sized by
 * the pill row beneath it and "Unassigned" rendered as "U…".
 *
 * As flex items nothing shares a track: the name and count size themselves, and
 * the breakdown is forced onto its own line by a 100% basis instead of by
 * spanning anything.
 */
.queue-view {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: .1rem .4rem;
  width: 100%; text-align: left;
  /* A visible border on EVERY option, not just the select — otherwise only
     "Another agent" looked like something you could choose, and the rest read
     as labels. */
  border: 1px solid #dde2e8;
  /* ⚠ The SAME shade as the agent select's field (#f7f9fb), not white — Johan,
     2026-08-19. White made the unselected options read as the page ground with
     a line drawn round them, while the one control that was obviously
     choosable was the shaded select. Sharing its fill makes the whole rail
     read as one set of things you pick from. */
  background: #f7f9fb;
  border-radius: .35rem; padding: .38rem .5rem; cursor: pointer;
  font: inherit; font-size: .82rem; color: #313a45;
}
.queue-view:hover { background: #e9eef3; border-color: #97a3b1; }
/* SELECTION IS NEAR-BLACK, not blue. Blue belongs to action buttons. */
.queue-view.is-on {
  background: #1c2530; border-color: #1c2530; color: #fff; font-weight: 600;
}
/* Shrinks only when the name genuinely does not fit — not because something
   below it wanted room.

   ⚠ The max-width keeps the count ON THE SAME LINE. Flex decides line breaks
   from an item's natural size BEFORE shrinking it, so a long agent name — and
   they get long, "Agent 1 AllSolutions" — would otherwise claim the whole line
   and drop the count beneath it. Bounding the name means the pair always fits
   together and the name truncates instead. */
.queue-viewname {
  flex: 0 1 auto; min-width: 0; max-width: calc(100% - 2.4rem);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/*
 * RIGHT-JUSTIFIED and heavier — Johan, 2026-08-19. (Supersedes an earlier call
 * to keep it tucked beside the name.) The number is what the rail is SCANNED
 * for: the names are fixed and learned within a day, the counts are the only
 * thing that changes. Aligned down a column they can be compared at a glance;
 * beside a ragged name they cannot.
 */
.queue-viewcount {
  flex: 0 0 auto; margin-left: auto;
  font-variant-numeric: tabular-nums; font-size: .92rem; font-weight: 700;
  color: #4b5563;
}
.queue-view.is-on .queue-viewcount { color: rgba(255,255,255,.82); }
/* Its own line, by basis rather than by spanning columns — that is the whole
   point of the change above. */
.queue-view .queue-breakdown { flex: 0 0 100%; margin-top: .15rem; }
/* The view cards carry the action pill on the name row (Johan, 2026-09-16), so their breakdown is the three states only. */
/* ⛔ FIXED POSITIONS — Johan, 2026-09-16: "use fixed horizontal positions for all
   the sub pills". Equal thirds on every card, whatever each card holds, so On,
   Owed and Rep sit at the same x down the rail; an absent pill leaves its slot
   empty. Every pill is .6rem now, so a third of the narrowest rail still holds
   "Owed 13" — the max-content floor that let columns drift is gone here. */
/* ⛔ FIXED SLOT WIDTHS, sized to the widest pill each slot can hold ("On 1/1",
   "Ow 14", "R 6", "1 A"), so no pill ever spills into the gap after it — Johan,
   2026-09-16, twice: "missing space between sub pills". Equal thirds let the
   widest pill overflow its column on a narrow rail; these do not. */
.queue-breakdown.is-compact { grid-template-columns: 2.55rem 2.25rem 1.7rem minmax(0, 1fr); gap: .3rem; }
.queue-breakdown.is-compact .sc { white-space: nowrap; padding-left: .2rem; padding-right: .2rem; }
.queue-breakdown.is-compact .sc-tag { margin-right: .14em; }
.queue-breakdown.is-compact .sc-slot-action { justify-content: flex-start; }
/* Sub-rows sit INSIDE a chosen group, so they are quieter — a border on every
   nested row would turn the sidebar into a grid of boxes. */
.queue-view-sub {
  font-size: .78rem; padding-left: 1rem; color: #4b5563;
  border-color: transparent; background: transparent;
}
.queue-view-sub:hover { background: #e9eef3; border-color: #dde2e8; }
.queue-agentlist { display: flex; flex-direction: column; gap: .1rem; }

/* The agent picker is a SELECT and has to look like one.
   With every view row now bordered, a bordered row with a small chevron was not
   enough to say "choose one of these". So it gets the full field treatment jBKB
   uses for its own filters: a label above, a shaded field, and the caret in its
   own segment at the end. */
/* ⚠ THE SAME CARD AS "MINE" — Johan, 2026-08-21: "put the same frame and
   background colour around the Agent + select an agent area as for Mine". It
   was a bare label and a field floating on the group's ground, so the one row
   holding the most sessions read as a form control rather than as a peer of the
   owners above it. Same border, same fill, same radius, same padding. */
/* ⚠ SET APART from Mine and Unassigned — Johan, 2026-08-23: "increase the space
   before that button to keep it separate". Those two are owners; this is the
   scope that CONTAINS them, so the gap is doing real work — it says the card
   below is a different kind of thing, not a third owner.

   Only in the rail: in strip mode the whole row is one line and a margin here
   would just knock the chip out of alignment. */
.queue-views:not(.is-strip) .queue-selectwrap { margin-top: .6rem; }

/* The CBVA picker. A SCOPE rather than a view, so it sits apart from the cards
   and looks like a form control rather than a bucket. */
/* ⚠ A CENTIMETRE clear of Active Auto — Johan, 2026-08-23. The rail's own gap
   is .7rem, so this adds the rest: enough that the picker reads as a different
   KIND of control rather than a sixth view card. */
.queue-views:not(.is-strip) .queue-solgroup { margin-top: 1.7rem; }
.queue-solgroup { gap: .3rem; }
.queue-sollabel {
  font-size: .7rem; color: #6b7280; padding: 0 .1rem;
}
.queue-solselect {
  width: 100%; min-width: 0; font: inherit; font-size: .78rem;
  border: 1px solid #c3ccd6; border-radius: .35rem; background: #f7f9fb;
  padding: .3rem .4rem; color: #313a45; cursor: pointer;
}
.queue-solselect:hover { border-color: #97a3b1; background: #fff; }
/* ⚠ Sideways with the rest of the rail — it is one more chip there, and the
   label would cost a line the strip does not have. */
.queue-views.is-strip .queue-solgroup { flex-direction: row; align-items: center; }
.queue-views.is-strip .queue-sollabel { display: none; }
.queue-views.is-strip .queue-solselect {
  width: auto; max-width: 10rem; padding: .15rem .4rem; font-size: .72rem;
  border-radius: 999px; background: #fff; border-color: #e5e7eb; min-height: 26px;
}
/*
 * ⛔ NO HORIZONTAL PADDING — Johan, 2026-09-01: "the urgency pills are not
 * horizontally positioned same for all who selectors".
 *
 * It was DOUBLE-PADDED and the arithmetic says so. Mine and Unassigned are
 * `.queue-view` directly inside the group, so their pills start at
 * group .35rem + border 1px + view .5rem = 14.6px. All Agents is a
 * `.queue-selectlabel` inside THIS wrapper, so it was
 * group .35rem + border 1px + wrap .5rem + label .5rem = 22.6px — eight pixels
 * out, on the card holding the most sessions.
 *
 * ⚠ The horizontal inset moves to the CHILDREN rather than being removed. The
 * label already carries .5rem of its own (it needs it: its background is the
 * selected state), and the field gets the same by margin. So nothing moves on
 * screen except the pills, which was the point — and the label's dark bar now
 * spans the card properly instead of floating inset within it.
 *
 * ⚠ Do not put it back. This is the third pass over this exact alignment: the
 * pills were pushed right (2026-08-20), then had their left inset zeroed and
 * restored (2026-08-26), and both times the fix was applied to the label while
 * the wrapper underneath went on adding its own.
 */
.queue-selectwrap {
  display: flex; flex-direction: column; gap: .15rem;
  border: 1px solid #dde2e8; background: #f7f9fb;
  border-radius: .35rem; padding: .38rem 0;
}
/* The inset the wrapper no longer supplies — so the field stays exactly where
   it was while the label above it moves into column with the other cards. */
.queue-selectwrap .queue-select { margin: 0 .5rem; width: auto; }
/*
 * The label row already had its own padding for the floating version.
 *
 * ⛔ BUT NOT HORIZONTALLY — Johan, 2026-08-26: "need a bit more padding left of
 * the 3 Urg sub pill". This override zeroed the left inset the base rule gives
 * it, so the breakdown started hard against the card edge — and, worse, in a
 * different column from the sub-pills on Mine and Unassigned, which is exactly
 * what the note below says must not happen.
 */
.queue-selectwrap .queue-selectlabel { padding: .38rem .5rem .25rem; }
/* Same type as Unassigned and Mine, because it names the same kind of thing:
   whose work you are looking at. As a .56rem uppercase caption it read as a
   form label for the field below, not as a third option. */
/* ⚠ A BUTTON now, not a caption — it selects the all-agent-based scope. See
   the markup. Full width and left-aligned so it reads as the card's own title
   rather than as a control floating above the field. */
.queue-selectlabel {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  padding: .38rem .5rem .2rem; color: #313a45;
  width: 100%; text-align: left; background: none; border: 0;
  font: inherit; cursor: pointer; border-radius: .3rem;
}
.queue-selectlabel:hover { background: #eef2f6; }
/* Selected: the same near-black the other view cards use, so "which scope am I
   in" reads the same way everywhere in the rail. */
.queue-selectlabel.is-on { background: #1f2530; color: #fff; }
/*
 * ⛔ THE TOTAL TOO — Johan, 2026-08-26: "all agents total number should be
 * white". `color: #fff` above sets the card's own text, but the count carries
 * its own colour, so on the dark selected card it stayed near-black and was
 * effectively invisible.
 *
 * ⚠ The equivalent rule exists for `.queue-view.is-on .queue-viewcount` and was
 * never written for this one — the All Agents header is a `.queue-selectlabel`,
 * not a `.queue-view`, so it inherited none of that card's selected treatment.
 */
.queue-selectlabel.is-on .queue-viewcount { color: rgba(255,255,255,.82); }
/*
 * ⛔ THE PILL KEEPS ITS OWN FILL ON A SELECTED CARD — Johan, 2026-08-24:
 * "cannot read on All Agents the sub pills".
 *
 * This used to force a near-white plate under them, and that was RIGHT while
 * `.sc-*` meant "coloured type on a pale wash": the white plate gave the
 * coloured glyphs something to sit on when the card went near-black.
 *
 * Then `.sc-*` became a solid fill with white type (2026-08-24, so the pane and
 * list pills would match the tabs) — and this rule survived, repainting the
 * ground white while the white text stayed. White on white. Important was the
 * only readable one, purely because amber's ink is near-black.
 *
 * ⚠ `.sc-*` IS SHARED between the pane's state pill and these sidebar
 * sub-counts. Changing it in one place changes both, and only one of them has a
 * dark ground. That is what made this invisible instead of merely wrong.
 *
 * A hairline separates the fill from the near-black card — green in particular
 * sits close to it — and nothing else is imposed.
 */
.queue-selectlabel.is-on .sc { box-shadow: inset 0 0 0 1px rgba(255,255,255,.35); }
/* ⚠ SAME PLACE AS EVERY OTHER CARD — Johan, 2026-08-20: "the sub pills should
   have the same horizontal position in all the who cards... only Agent must be
   fixed". They sit on their OWN LINE, left-aligned, exactly as
   `.queue-view .queue-breakdown` does. Pushing them right instead put the
   Agent card's numbers in a different column from the three above it, so the
   one thing the breakdown is for — reading a column DOWN the sidebar — did not
   work on the row holding the most sessions. */
.queue-selectlabel .queue-breakdown { flex: 0 0 100%; margin-top: .15rem; }
/* ⚠ `min-width: 0` on both the field and its value, or the name refuses to
   shrink and widens the rail instead of ellipsing — see .queue-views. */
.queue-select {
  min-width: 0;
  display: flex; align-items: stretch; width: 100%;
  border: 1px solid #c3ccd6; border-radius: .35rem;
  background: #f7f9fb; cursor: pointer; font: inherit; font-size: .8rem;
  color: #313a45; overflow: hidden; min-height: 2rem;
}
.queue-select:hover { border-color: #97a3b1; background: #fff; }
/* An ECHO of the card above, not a second selection — see the markup. Reads as
   "yes, that one", quietly, instead of competing with the card that already
   said it. */
.queue-select.is-echo {
  background: #eef2f6; border-color: #c3ccd6; color: #313a45; font-weight: 600;
}
/* ⚠ TRIMMED, not wrapped — Johan chose cut-off over two rows, the same call he
   made for the owner name in the pane header. The full name is on the button's
   title, so nothing is lost. */
.queue-selectvalue {
  flex: 1 1 auto; min-width: 0; text-align: left; padding: .35rem .5rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Its own segment, with a divider — the detail that reads as "select" rather
   than "button with an arrow". */
/* ⚠ Sized to be SEEN. At .8rem the glyph was a speck, and the caret is the one
   mark that says "this opens" — the rest of the field looks like a label. */
.queue-selectcaret {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 1.9rem; border-left: 1px solid #c3ccd6;
  background: #eef2f6; color: #3d4854; font-size: 1.15rem; line-height: 1;
}
.queue-select.is-on {
  border-color: #1c2530; background: #1c2530; color: #fff; font-weight: 600;
}
.queue-select.is-on .queue-selectcaret {
  border-left-color: rgba(255,255,255,.28);
  background: rgba(255,255,255,.12); color: #fff;
}
/* The open list sits under the field as its options. */
/*
 * ⛔ THE SAME TREATMENT AS THE SESSION LIST — Johan, 2026-08-26: "in select
 * agent list need clearer separations between the agents".
 *
 * Identical cause: `.queue-view-sub` is transparent with a transparent border,
 * sitting on a white panel 1.6px apart, so two-line entries — a name and its
 * breakdown pills — ran into each other. Ground the panel, make the rows white.
 *
 * ⚠ Deliberately the same answer as the session cards rather than a different
 * one (dividers, striping): two lists that separate their rows by different
 * means is a second visual language to learn for no gain.
 */
.queue-agentlist {
  display: flex; flex-direction: column; gap: .22rem;
  border: 1px solid #dde2e8; border-top: none;
  border-radius: 0 0 .35rem .35rem;
  padding: .25rem; background: #f4f6f9;
}
/* White ON the ground, like a session card. Hover still overrides both. */
.queue-agentlist .queue-view-sub {
  padding-left: .5rem; background: #fff; border-color: #e8ecf1;
}

/* Tabs above the list. Meaning colours on the label and the underline, as in
   the sidebar pills — the same vocabulary in both places. */
/* ⚠ A FLOOR, where there was none. With `min-width: 0` the list could be
   squeezed to nothing by three open panes — present, scrollable, and unusable.
   20rem still shows the wait, the shopper and the state on every row. */
/* ⚠ CAPPED, not fluid — Johan, 2026-08-20: "make the width of the session
   list column narrower... until what is now the end of the Last message filter
   field". A list row is a scannable summary, not a document: past ~36rem the
   eye travels further for the same information, and the space is better left
   to the conversation panes. */
/* ⚠ A FIXED WIDTH, not a range — Johan, 2026-08-21: "the horizontal space for
   the session list keeps changing depending on the content of the sessions.
   Should not be the case."
   With `flex: 1 1 auto` between a min and a max, and the page sizing to its
   content, a single long shopper email widened the whole column — so the list,
   the filter bar and the tabs all moved between one view and the next. A list
   you scan has to hold still; the row contents adapt to the column, never the
   other way round. */
/*
 * ⚠ IT MUST BE ABLE TO SHRINK — Johan, 2026-08-21, with a screenshot of a pane
 * sitting on top of the list at ~1100px: "never invade space as here".
 *
 * This was `flex: 0 0 36rem`, fixed. But `panesThatFit` allows the list to fall
 * back to `LAYOUT.listMin` (20rem) as the last step before showing no pane at
 * all — so at narrow widths the arithmetic said "one pane fits", the CSS
 * refused to give up a single pixel, and the pane overflowed across the list.
 * The model was right and the layout was not.
 *
 * 36rem stays the width it WANTS; 20rem is the floor it will go to. Below that
 * the phone layout takes over and shows one thing at a time.
 */
/*
 * ⛔ A FIXED TRACK, NOT A FLEXIBLE ONE — Johan, 2026-08-25: "the session list
 * column became narrower when I added a filter selection in History. Should not
 * happen!!"
 *
 * It was `flex: 1 1 36rem; min-width: 20rem`, which does not describe a column
 * — it describes a RANGE. Anything that changed the flex line's totals moved
 * the list somewhere between 20 and 36rem, so the width an agent had learned
 * shifted underneath them for reasons that had nothing to do with the list.
 * Rows are fixed-column grids precisely so the eye can stop re-finding fields;
 * a column that resizes under a filter throws that away.
 *
 * `0 0 36rem` makes it the same width whatever else is on the row and whatever
 * is in it. The narrow-screen rules still win: the media query above sets
 * `min-width: 0`, and strip mode stacks the whole body — this only pins the
 * DESKTOP case, which is the one that was moving.
 *
 * ⚠ `max-width` stays for strip mode, where the column is no longer a flex item
 * in a row and the basis does not apply.
 */
/* 30rem, was 36 — Johan, 2026-09-15: "make the session list a bit narrower" so a third pane fits beside the menu. paneFit.ts LAYOUT.listWidth MUST match. */
/* Grows a little too, to 34rem, with a third of the panes' weight — Johan, 2026-09-15. paneFit.ts keeps 30 as the width it plans with. */
.queue-main-col { flex: 1 1 30rem; min-width: 22rem; max-width: 34rem; }
/* ⚠ The LIST needs its own surface — Johan, 2026-08-21: "the area where
   sessions would be listed should not have the same background as the general
   page". On the page ground the rows floated with nothing holding them, so an
   empty list looked like a rendering failure rather than an empty list. A card
   gives the emptiness somewhere to be. */
/*
 * SELECTION, for archiving. The tick sits beside the row rather than inside it —
 * a checkbox in a <button> is invalid, and every tick would also open the
 * conversation.
 */
.queue-li { display: flex; align-items: center; gap: .45rem; }
.queue-li .queue-row { flex: 1 1 auto; min-width: 0; }
.queue-pick { flex: 0 0 auto; width: 1rem; height: 1rem; cursor: pointer; }

/*
 * ⚠ ONLY WHEN SOMETHING IS TICKED — see the markup. A bar that is always
 * present is a bar nobody reads, and this one carries the most consequential
 * act in the console.
 */
/* ⚠ A HEADER FOR THE LIST, so its checkbox lines up with the row ticks below
   it — that alignment is what makes it read as "all of these". Light rather
   than the near-black it was: it sits ON the list now, not over it. */
.queue-bulk {
  display: flex; align-items: center; gap: .5rem;
  margin-bottom: .35rem; padding: .3rem .5rem;
  background: #eef2f6; color: #313a45; border-radius: .4rem;
  border: 1px solid #dde2e8; font-size: .78rem;
}
.queue-bulk-count { font-weight: 600; }
.queue-bulk-push { flex: 1 1 auto; }
.queue-bulk-clear {
  border: 0; background: none; color: #6b7280;
  font: inherit; font-size: .74rem; text-decoration: underline; cursor: pointer;
}
.queue-bulk-clear:hover { color: #313a45; }
/* ⚠ BLUE, the console's action colour — Johan, 2026-08-23. Deliberately not
   red: archiving is reversible and a visitor's message undoes it by itself, so
   dressing it as destruction would misstate what it does. */
.queue-bulk-go {
  border: 0; background: #1d4ed8; color: #fff; font: inherit; font-weight: 600;
  font-size: .76rem; border-radius: .3rem; padding: .2rem .7rem; cursor: pointer;
}
.queue-bulk-go:hover:not(:disabled) { background: #1e40af; }
.queue-bulk-go:disabled { opacity: .6; cursor: not-allowed; }

/* ⚠ THE ONLY THING THAT SCROLLS in the list column. The pager sits below it and
   stays put, which is the point of a pager. */
/*
 * ⛔ A GROUND FOR THE CARDS TO SIT ON — Johan, 2026-08-26: "should we make the
 * session list cards stand out more? clearer separation between them?"
 *
 * They were white cards on a WHITE panel, separated by 2.4px and a #e5e7eb
 * hairline — nothing for the eye to catch, so three-line rows merged into one
 * wall of text. The panel now carries a light ground and the cards stay white,
 * which is what every inbox does and costs no per-card chrome.
 *
 * ⚠ Deliberately NOT more colour or a left accent stripe: a row already carries
 * Urgent, Online, the channel, the language chip, an agent name and a transfer
 * glyph. Anything added competes with the state pills, which are the part that
 * should be shouting.
 */
.queue-listsurface {
  border: 1px solid #e3e7ec; border-radius: .45rem; background: #f4f6f9;
  padding: .5rem; min-height: 8rem;
  flex: 1 1 auto; overflow-y: auto;
}
/* ── The floating day marker, under History ────────────────────────────────
   The ACI transcript's `.cv-day`, on the session list (Johan, 2026-09-08).
   Sticky against the list surface, which is the scroll container, so the day
   being scrolled through stays named at the top until the next one pushes it
   out. The pill is the same size, colour and shape as the transcript's, so the
   two consoles say "Today" the same way. */
.queue-day {
  position: sticky; top: 0; z-index: 5;
  display: flex; justify-content: center;
  /* The row underneath stays clickable through the pill's empty flanks. */
  pointer-events: none;
  margin: .1rem 0 -.1rem;
}
.queue-day > span {
  pointer-events: auto;
  padding: .15rem .6rem;
  font-size: .66rem; letter-spacing: .04em; text-transform: uppercase;
  color: #4b5563; background: #eef1f5;
  border: 1px solid #dfe3e8; border-radius: 999px;
}
/*
 * "Filtering…", over a list that still shows the previous result.
 *
 * ⚠ STICKY, not absolute. The surface is the scroll container, so an absolutely
 * positioned bar scrolls away with the rows and is gone exactly when the agent
 * scrolls to check whether anything changed. Sticky keeps it against the top
 * edge of whatever is in view for as long as the fetch runs.
 *
 * The negative margins pull it out to the surface's own edges so it reads as a
 * banner on the list rather than a first row in it — which is the one thing it
 * must not be mistaken for.
 */
.queue-refiltering {
  position: sticky; top: -.5rem; z-index: 3;
  display: flex; align-items: center; gap: .4rem;
  margin: -.5rem -.5rem .45rem; padding: .32rem .6rem;
  background: #eef4ff; border-bottom: 1px solid #cfe0ff;
  border-radius: .4rem .4rem 0 0;
  color: #1d4ed8; font-size: .72rem; font-weight: 600; letter-spacing: .01em;
}
.queue-refiltering-cancel {
  margin-left: .5rem; border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; font-size: .72rem; color: #2563eb; text-decoration: underline; text-underline-offset: 2px;
  pointer-events: auto;
}
.queue-refiltering-spin {
  width: .68rem; height: .68rem; border-radius: 50%;
  border: 2px solid #c3d8ff; border-top-color: #1d4ed8;
  animation: queue-refiltering-turn .6s linear infinite;
}
@keyframes queue-refiltering-turn { to { transform: rotate(360deg); } }
/* A spinner is the one thing here that must not move for anyone who has asked
   for less motion; the words already carry the message. */
@media (prefers-reduced-motion: reduce) {
  .queue-refiltering-spin { animation: none; border-top-color: #c3d8ff; }
}
/*
 * ⚠ Bounded to the FILTER BAR's width, like the empty message. The list column
 * runs the full page, so the right-pushed lookups landed far off to the right
 * of everything else on screen — Recent and History floated in space instead of
 * sitting over the last filter. Capped here, the push lands them directly above
 * "Any status", which is the right edge of the controls the agent is reading.
 */
.queue-tabs {
  display: flex; align-items: center; gap: .3rem;
  /* ⚠ THE FULL LIST WIDTH, no cap — Johan, 2026-09-15: the right-hand group's
     edge must be the filter row's edge ("Us first"). */
  width: 100%;
  /* ⚠ NO UNDERLINE now the tabs are cards. A rule under a row of framed
     buttons reads as a container edge they are sitting on rather than as the
     baseline they hang from, which is what a tab strip needs it for. */
  margin-bottom: .9rem;
}
/* ⚠ The states get their OWN box, the filters do not — Johan, 2026-08-21:
   "we must ux differentiate between the urgency selectors and the filters. now
   too similar". Once both rows were framed pills the two acts looked alike, and
   they are not: these choose WHAT THE LIST CONTAINS, the row below NARROWS it.
   Grouping the first is enough to say so without labelling either. */
/* ⚠ WRAPS INTERNALLY TOO. The row wrapping is not enough on its own: these four
   sit in one box, and a box that cannot break is a box that overflows — which
   at the narrowest column means sliding under the conversation pane rather than
   moving down. Johan, 2026-08-23: "do not want this to happen when the ACI
   covers History". */
.queue-stategroup {
  display: inline-flex; align-items: center; gap: .3rem;
  flex-wrap: wrap; row-gap: .25rem; min-width: 0; max-width: 100%;
  border: 1px solid #dde2e8; border-radius: .4rem;
  background: #fff; padding: .2rem .25rem;
}

.queue-tabs-push { flex: 1 1 auto; }
/* The right-hand group: lookups, a hairline, Actions (Johan, 2026-09-15). */
/* ⚠ .8rem, PLAINLY apart — Johan, 2026-09-15, three times: "still no space between Recent and History". .45rem read as none beside pills that carry their own borders. Same gap before Actions. */
.queue-rightgroup { gap: .8rem; }
.queue-rightgroup .queue-lookups { gap: .8rem; }
.queue-refresh-rail { margin: 0 0 .2rem; align-self: stretch; }

/* Sort sits with the lookups rather than with the state tabs: it changes the
   ORDER of whatever is selected, not what is selected.
   ⚠ Label ABOVE the field — Johan, 2026-08-23. Beside it, the word "Sort" cost
   the row width it did not have; stacked it costs none, because the field is
   shorter than the tab beside it is tall. */
/* ⛔ A ROW, not a column — the label is gone (see the JSX), so this is now just
   the select and it sits on the same baseline as every other filter field. As
   a column with a label above it, this was the one control on the row taller
   than the rest and could never align with them. */
.queue-sort {
  display: inline-flex; align-items: center;
  flex: 0 0 auto; line-height: 1.1;
}
.queue-sort-select {
  font: inherit; font-size: .7rem; color: #313a45;
  border: 1px solid #dde2e8; border-radius: .3rem; background: #fff;
  cursor: pointer;
  /* Narrower now it sits on the filter row — Johan, 2026-08-24. The longest
     label is "Visitor first"; anything wider is padding nobody reads. */
  max-width: 6.2rem;
  /* ⚠ AND TALLER — Johan, 2026-08-24: "the sort select field could be taller,
     the label can still fit on top and will not interfere with History".
     It was squeezed to .05rem padding to clear the History tab on the row it
     used to live on; that constraint left with it. Matches `.qf-field`'s 28px
     so the row reads as one set of controls rather than a short one bolted on. */
  padding: .25rem .4rem; min-height: 28px;
}

/* ⚠ THE ONLY `margin-left: auto` ON THIS ROW, which is what right-justifies
   it. `.qf` wraps, so on a narrow shell this drops to the next line and the
   auto margin still holds it right — the filters never get pushed off. */
.qf-sortslot { margin-left: auto; display: inline-flex; align-items: center; }

/* ── The rail's two section labels (Johan, 2026-09-09) ─────────────────── */
.queue-railhead {
  font-size: .62rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: #8a94a3;
  /* ⚠ ON THE CARD'S TOP BORDER, like a fieldset legend — Johan, 2026-09-15,
     twice: the first cut sat above the card. Now the label is INSIDE the card
     and pinned to its top edge, on the page ground so the border breaks
     behind the words. */
  position: absolute; top: -.4rem; left: .6rem; z-index: 1; width: max-content; max-width: calc(100% - 1.2rem);
  margin: 0; padding: 0 .3rem;
  background: var(--bs-body-bg, var(--tblr-body-bg, #f2f2f7)); line-height: 1;
}
.queue-viewgroup-titled { position: relative; margin-top: .45rem; padding-top: .6rem; }
.queue-hours-rail { padding: 0 .2rem; margin-top: .2rem; font-size: .78rem; }
.queue-refresh-tabs { margin-left: auto; align-self: center; }

/* ── The Actions dropdown on the tab row (Johan, 2026-09-09) ─────────────
   Its own section, a little right of the state tabs: the gap says it is a
   different kind of thing from the buckets, the border says it is a field. */
/* Its own section inside the state group: right-justified, a hairline
   between it and the buckets — Johan, 2026-09-12. */
/* ⚠ ORDER, NOT A MARGIN — Johan, 2026-09-12: "right border shall be the same
   position as the right border of Us first". The push between the buckets and
   the lookups takes the free space before any auto margin, so Actions never
   moved. On a wide shell Actions is the LAST item on the row; on a narrow one
   (below) it sits between the buckets and Recent / History. */
.queue-tabs > .queue-stategroup { order: 0; }
.queue-tabs > .queue-tabs-push { order: 1; }
.queue-tabs > .queue-lookups { order: 2; }
.queue-tabs > .queue-actiongroup { order: 3; margin-left: .45rem; }
/* ⚠ The right-hand group is a .queue-stategroup too, so the rule above put it BEFORE the spacer — Johan, 2026-09-15, twice: "still not right justified". */
.queue-tabs > .queue-rightgroup { order: 2; margin-left: auto; }
.queue-actionsel {
  margin-left: 0; align-self: center;
  border: 1px solid #d1d5db; background: #fff; border-radius: .3rem;
  font: inherit; font-size: .74rem; color: #374151;
  /* ⛔ ONE WIDTH, whatever the items say — Johan, 2026-09-16: "don't make the
     actions select wider if the items are wide". A long action text trims. */
  /* 5.75rem, not 7.5 — Johan, 2026-09-18: narrower "so it all fits on one row"
     now that Pinned joined Recent and History. "Actions 12" still reads whole. */
  width: 5.75rem; max-width: 5.75rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: .2rem .3rem; min-height: 28px; cursor: pointer;
}
/* Violet, by the tabs' rule: coloured when there is something, grey when empty; the selected kind underlined like a tab that is on. */
.queue-actionsel.has-items { border-color: var(--st-action-edge); background: var(--st-action-tint); color: var(--st-action); font-weight: 600; }
.queue-actionsel.is-set { border-bottom: 2px solid var(--st-action-fill); }

/* ── Agent / Auto / Both under History ─────────────────────────────────────
   A segmented quick select in the sort control's slot (Johan, 2026-09-08).
   Sized to the Filter button beside it — same min-height, same font — so the
   row stays one line of equal controls. The selected segment takes the same
   grey the neutral tabs use when on, one level below the black of the Who
   cards, because this narrows within a view rather than choosing one. */
.queue-seg {
  display: inline-flex; align-items: stretch;
  border: 1px solid #d1d5db; border-radius: .3rem; background: #fff; overflow: hidden;
}
.queue-segbtn {
  border: none; background: none; cursor: pointer; font: inherit;
  font-size: .72rem; color: #374151; padding: .25rem .6rem; min-height: 26px;
}
.queue-segbtn + .queue-segbtn { border-left: 1px solid #d1d5db; }
.queue-segbtn:hover { background: #f2f4f7; color: #1c2530; }
.queue-segbtn.is-on { background: #4b5563; color: #fff; font-weight: 600; }
.queue-segbtn.is-on:hover { background: #4b5563; color: #fff; }
.queue-sort-select:hover { border-color: #97a3b1; }

/* ⚠ NEVER WIDER THAN THE LIST — Johan, 2026-08-23: "History should not be
   further right than the right border of the session list". The row is nowrap
   by default, so adding the sort control pushed the lookups past the column's
   edge instead of moving them down. */
.queue-tabs { flex-wrap: wrap; row-gap: .3rem; }
.queue-tab {
  border: none; background: none; cursor: pointer; font: inherit;
  font-size: .82rem; color: #5b6673;
  padding: .45rem .65rem; border-bottom: 2px solid transparent;
  margin-bottom: -1px; display: inline-flex; align-items: center; gap: .3rem;
}
.queue-tab:hover { color: #1c2530; background: #f2f4f7; border-radius: .3rem .3rem 0 0; }
.queue-tabcount { font-variant-numeric: tabular-nums; font-size: .74rem; color: #98a2ad; }
.queue-tab.is-on { font-weight: 600; }
.queue-tab.is-on .queue-tabcount { color: inherit; opacity: .75; }

/* ── The state pills as CARDS ──────────────────────────────────────────────
   Johan, 2026-08-21: "would it make sense to use similar design for the
   urgency selectors as for the who selectors. thus cards (but smaller) in
   frames and black when selected?" — yes, and for a reason beyond consistency:
   these are the same KIND of act as picking an owner (choosing what the list
   contains), so they should look like one. Smaller because there are more of
   them and they sit on a row rather than a column. */
.queue-tabs .queue-tab {
  border: 1px solid #dde2e8; background: #f7f9fb; border-radius: .35rem;
  padding: .2rem .5rem; min-height: 26px; margin-bottom: 0;
  display: inline-flex; align-items: center; gap: .3rem;
}
.queue-tabs .queue-tab:hover { background: #e9eef3; border-color: #97a3b1; }
/* Near-black when selected, exactly as the owner cards — blue belongs to
   action buttons. */
/* All, the lookups, and any EMPTY tab: neutral, because none of them is a state
   with a colour of its own. */
/*
 * ⛔ ONE LEVEL LIGHTER THAN THE WHO CHIPS — Johan, 2026-08-26: "dark grey for
 * select rather than black on the urgency, while black on the who... to
 * communicate that they are kind of two levels of select".
 *
 * Both were #1c2530, so "Mine" and "All" sat side by side in identical black
 * and read as one row of equal choices. They are not: the Who selects WHICH
 * PILE, and the tabs narrow WITHIN it — a second-level filter that only means
 * anything once the first is chosen.
 *
 * ⚠ Only the NEUTRAL tabs take this. Urgent / Important / Replied override it
 * with their own meaning colour when selected, and that must stay — a red
 * Urgent is carrying information this grey would erase.
 */
.queue-tabs .queue-tab.is-on {
  background: #4b5563; border-color: #4b5563; color: #fff; font-weight: 600;
}
.queue-tabs .queue-tab.is-on .queue-tabcount { color: #fff; }

/* ⚠ A selected bucket that HAS work goes solid in its own colour — see above.
   These come after `.is-on` deliberately: same specificity, so order decides,
   and the state's colour must win over the neutral. */
.queue-tabs .queue-tab.is-on.queue-tab-urgent    { background: var(--st-urgent-fill);    border-color: var(--st-urgent-fill);    color: var(--st-urgent-ink); }
.queue-tabs .queue-tab.is-on.queue-tab-important { background: var(--st-important-fill); border-color: var(--st-important-fill); color: var(--st-important-ink); }
.queue-tabs .queue-tab.is-on.queue-tab-replied   { background: var(--st-replied-fill);   border-color: var(--st-replied-fill);   color: var(--st-replied-ink); }
.queue-tabs .queue-tab.is-on.queue-tab-urgent .queue-tabcount,
.queue-tabs .queue-tab.is-on.queue-tab-important .queue-tabcount,
.queue-tabs .queue-tab.is-on.queue-tab-replied .queue-tabcount { color: inherit; }
/* ⚠ The count sat as a superscript, riding above the label's baseline. Aligned
   to it now — Johan: "move the numbers in the urgency options down a bit so
   they are vertically aligned with the option labels". */
.queue-tabs .queue-tabcount {
  vertical-align: baseline; position: static; top: auto;
  font-size: .68rem; font-variant-numeric: tabular-nums;
}

/* ⚠ COLOUR TRACKS CONTENT, NOT SELECTION.
   These colours exist to pull the eye toward work. A bucket with nothing in it
   must not wear one — a red "Urgent 0" spends attention on nothing, which is
   how people learn to stop seeing red. So: coloured whenever the count is above
   zero, selected or not; grey when empty. Selection is carried by weight and
   the underline. */
/*
 * ⚠ THREE LEVELS, not two — Johan, 2026-08-24: "perhaps more attention to
 * Urgent and Important… these 3 buckets are kind of the key of everything".
 *
 * They are the triage, and they were carrying the quietest treatment available:
 * coloured TEXT on a white card. So each tab now says two things at once:
 *
 *   empty        grey text, grey border      nothing here, do not look
 *   has work     its colour, 2px border,     something here
 *                pale tint
 *   selected     SOLID in its own colour     you are looking at it
 *
 * ⚠ Selected is the state's OWN colour rather than the near-black every
 * selected thing used to get. A solid red tab says "Urgent, and you are in it"
 * in one glance; near-black said only the second half, and left the colour to
 * be recovered from the tab beside it.
 *
 * ⚠ EMPTY STAYS GREY even when selected — an empty Urgent painted solid red
 * spends the loudest colour in the console on nothing, which is how people
 * learn to stop seeing red. The rule from the row pills, applied here.
 */
.queue-tab-urgent    { color: var(--st-urgent);    border-color: var(--st-urgent-edge);    background: var(--st-urgent-tint); }
.queue-tab-important { color: var(--st-important); border-color: var(--st-important-edge); background: var(--st-important-tint); }
.queue-tab-action    { color: var(--st-action);    border-color: var(--st-action-edge);    background: var(--st-action-tint); }
.queue-tab-replied   { color: var(--st-replied);   border-color: var(--st-replied-edge);   background: var(--st-replied-tint); }
.queue-tab-urgent, .queue-tab-important, .queue-tab-action, .queue-tab-replied {
  border-width: 2px; padding-top: calc(.25rem - 1px); padding-bottom: calc(.25rem - 1px);
}
.queue-tab-urgent .queue-tabcount,
.queue-tab-important .queue-tabcount,
.queue-tab-action .queue-tabcount,
.queue-tab-replied .queue-tabcount { color: inherit; opacity: .8; }

/*
 * ⚠ QUIET, NOT UNREADABLE — Johan, 2026-08-24 described the reading he wants:
 * "Ok, Urgent looks good, nothing there. Important has 5 I need to fix. I have
 * selected Replied and I am working on those."
 *
 * The first clause needs the ZERO to be read. At #a2abb5 on white — 2.33:1,
 * with the count at 1.84 — "Urgent 0" did not say "nothing there", it said
 * DISABLED: an agent would skip past it without learning the thing that makes
 * them relax.
 *
 * Quiet is the job of the tint and the border, which an empty tab simply does
 * not get. The TEXT only has to stay legible. 4.83:1.
 */
/*
 * ⚠ LOUD WHEN THERE IS WORK, even UNSELECTED — Johan, 2026-08-24: "if
 * sessions, the urgent and important buttons must be more attention seeking
 * also when not selected!!!"
 *
 * The three levels were empty / has-work / selected, and the middle one was
 * barely a step: tinted background, coloured text, and the count in the same
 * ink at 80% opacity. So a bucket holding eighteen people waiting looked very
 * nearly like one holding none, and only the digit told them apart — which is
 * exactly backwards, because the digit is the smallest thing on the tab.
 *
 * The COUNT becomes a solid badge in the bucket's own colour, and the label
 * goes semibold. That reads across a room, and it does not borrow the selected
 * state's clothes: selected is still the whole tab filled. Empty is untouched
 * — an empty Urgent must stay quiet or the red stops meaning anything.
 *
 * ⚠ URGENT AND IMPORTANT ONLY. Replied and Bot are not work waiting on us; a
 * badge on them would spend attention on the buckets that do not need it, and
 * four shouting tabs is the same as none.
 */
.queue-tab-urgent:not(.queue-tab-empty):not(.is-on),
.queue-tab-important:not(.queue-tab-empty):not(.is-on) { font-weight: 650; }

/* The count in the bucket's own colour, no badge behind it — Johan, 2026-09-14: "the red and
   orange colours should still be enough attention". A badge plus a split count ("1/2") was a
   blob with no digits in it. */
.queue-tab-urgent:not(.queue-tab-empty):not(.is-on) .queue-tabcount,
.queue-tab-important:not(.queue-tab-empty):not(.is-on) .queue-tabcount { opacity: 1; font-weight: 700; }
.queue-tab-urgent:not(.queue-tab-empty):not(.is-on) .queue-tabcount    { color: var(--st-urgent); }
.queue-tab-important:not(.queue-tab-empty):not(.is-on) .queue-tabcount { color: var(--st-important); }

.queue-tab-empty { color: #6b7280; background: #fff; }
/* Keeps the bucket's own edge, softened — see --st-*-soft. */
.queue-tab-empty.queue-tab-urgent    { border-color: var(--st-urgent-soft); }
.queue-tab-empty.queue-tab-important { border-color: var(--st-important-soft); }
.queue-tab-empty.queue-tab-replied   { border-color: var(--st-replied-soft); }
.queue-tab-empty.queue-tab-action    { border-color: var(--st-action-soft); }
.queue-tab-empty .queue-tabcount { color: #6b7280; opacity: .8; }

/* ⚠ TOKENS, not literals. These four were hardcoded copies of the palette and
   would have kept the OLD amber and slate after the change above — the same
   silent drift that left `.sc-bot` purple while the list was blue. */
.queue-tab-urgent.is-on    { border-bottom-color: var(--st-urgent-fill); }
.queue-tab-important.is-on { border-bottom-color: var(--st-important-fill); }
.queue-tab-action.is-on    { border-bottom-color: var(--st-action-fill); }
.queue-tab-replied.is-on   { border-bottom-color: var(--st-replied-fill); }
/* An empty bucket you have selected still needs to show it is selected. */
.queue-tab-empty.is-on { color: #4b5563; border-bottom-color: #98a2ad; }
.queue-tab.is-on:not([class*="queue-tab-"]) { color: #1c2530; border-bottom-color: #1c2530; }

/* The per-view breakdown. Colour AND initial, never colour alone. */
/* A TABLE, not a list. Fixed slots so each state keeps its position whether or
   not it has a number — a column can then be read down the sidebar, and "3"
   means the same thing on every row. */
/* ⚠ THREE FIXED SLOTS, then the action pushed to the far edge — Johan,
   2026-08-21. The states are mutually exclusive and read as a row that sums;
   the action is a parallel axis that can sit on any of them, and ranging it
   alongside implied a four-way split the numbers never made. The fixed widths
   remain so a column can still be read DOWN the sidebar. Wider than before
   because the labels are now words rather than single letters. */
/*
 * ⛔ THE FIXED COLUMNS MUST BE ABLE TO SHRINK — Johan, 2026-09-01: "the Action
 * pills are outside the area".
 *
 * They could not, and the sums are unforgiving. The rail is 12rem; after the
 * group padding, the card border and the card padding a breakdown has about
 * 161px to live in. Three rigid 2.9rem columns plus their gaps take 145px,
 * leaving 16px for an Act pill that needs about 34 — so it hung outside the
 * card on every row that had one.
 *
 * ⚠ `minmax(0, …)` on all four, not narrower fixed columns. The fixed widths
 * are what let the numbers be read DOWN the sidebar, and they still hold
 * whenever there is room; they now yield when there is not. Every card in the
 * rail is the same width, so they yield by the same amount and the column
 * survives — which a per-card `auto` would have quietly destroyed.
 */
.queue-breakdown {
  display: grid;
  /* ⚠ ALL THE WIDTH — Johan, 2026-09-08: "use all width". The three state
     columns were capped at 2.9rem, so on a wide rail the pills huddled left
     with room to spare, and on a narrow one they still met. Now they share
     whatever the card gives them, equally, with the action column taking only
     what it needs at the far right. */
  /* ⚠ NEVER NARROWER THAN ITS PILL — Johan, 2026-09-15, screenshot: at the
     11rem rail an equal third was narrower than "Owed 16", and the pill ran
     into "Rep 2" through the gap. `max-content` as the floor keeps every column
     at least as wide as what it holds, so the gap is always there; `1fr` above
     it still lets the columns line up down the sidebar when there is room. */
  grid-template-columns: repeat(3, minmax(max-content, 1fr)) minmax(max-content, auto);
  /* ⚠ A visible gap. At .12rem a two-digit "37 Rep" filled its column and
     touched "1 Act" beside it (Johan, 2026-09-07, screenshot). */
  gap: .3rem;
  width: 100%;
  /* The pills measure THEMSELVES against this row — see the @container rules
     below. Digit classes come from Breakdown in Queue.tsx. */
  container-type: inline-size;
}
/*
 * ⚠ SMALLER NUMBERS WHEN THEY DO NOT FIT, and only then — Johan, 2026-09-08:
 * "if needed smaller font for numbers (if 2 or 3 digits)". The row queries its
 * own width: with room to spare every pill keeps the legible .68rem (see the
 * warning at `.sc`); as the rail narrows the three-digit pills step down first,
 * then the two-digit ones, so a "40 Rep" never touches "1 Act" and never spills
 * its column. One-digit pills never change — they always fit.
 *
 * Thresholds: a state column is (width − action − 3 gaps) / 3. At 15rem a
 * three-digit pill at .68rem no longer fits its third; at 13rem a two-digit one.
 */
/* ⚠ THE SMALL SIZE IS NOW THE ONLY SIZE — Johan, 2026-09-15: "perhaps best to
   always use the smaller font also". The .68rem baseline and the two-digit
   step are gone; every pill is .6rem, and only a three-digit one steps down
   further when the row is tight. The pill no longer changes size as the rail
   breathes, which was its own kind of noise. */
@container (max-width: 13rem) {
  .sc.sc-3 { font-size: .54rem; padding-left: .18rem; padding-right: .18rem; }
}
.sc-slot { display: flex; justify-content: flex-start; align-items: center; min-height: 1.05rem; min-width: 0; }
.sc-slot-action { justify-content: flex-end; }
/*
 * ⚠ SIZE IS WHAT MAKES THESE LEGIBLE, not the colour arrangement.
 *
 * Johan, 2026-08-19: the number and its letter were hard to read at .56rem —
 * white type that small on a saturated fill loses; the fill wins and the glyphs
 * disappear into it. The answer then was to flip to coloured type on a pale
 * wash. Since 2026-08-24 `.sc-*` is a solid fill again, shared with the pane
 * pill, so what carries the legibility now is the SIZE and WEIGHT set below —
 * .68rem/700 rather than .56rem — plus Important's near-black ink.
 *
 * ⚠ Do not shrink these back. The original bug was the type size meeting a
 * saturated ground, and only one of those two has changed.
 */
/* Slightly larger now the labels are words — Johan asked for "a bit larger and
   with more text". The room comes from dropping a fixed column. */
.sc {
  /* .6rem, was .68 — Johan, 2026-09-15; see the @container note above. Still
     well above the .56rem that failed on 2026-08-19. */
  font-size: .6rem; font-weight: 700; line-height: 1;
  padding: 0 .24rem; border-radius: .2rem;
  font-variant-numeric: tabular-nums; white-space: nowrap;
  /* ⚠ ONE HEIGHT, TEXT CENTRED — Johan, 2026-09-14: a pill that stepped its font down (sc-2 / sc-3)
     sat lower than its neighbours. Every pill in the row is the same height and centres its text,
     whatever size the digits ended up. */
  display: inline-flex; align-items: center; height: 1.05rem;
}
/* The pane's own pills, from the same tokens — a conversation must not describe
   itself in a different red from the row that opened it. */
/* ⚠ THE PANE PILL AND THE LIST PILL ARE ONE THING — see `.qr-state-*`, which
   these mirror exactly. The pane is where you land after clicking the row, so
   a pill that changed weight on the way would read as a different fact. */
.sc-urgent    { background: var(--st-urgent-fill);    color: var(--st-urgent-ink); }
.sc-important { background: var(--st-important-fill); color: var(--st-important-ink); }
.sc-action    { background: var(--st-action-fill);    color: var(--st-action-ink); }
.sc-replied   { background: var(--st-replied-fill);   color: var(--st-replied-ink); }
/* Online auto chats still assigned to an agent: outlined like the owner pills
   on the pane (`.cv-pill-owner-agent` / `-me`), because it says WHOSE, not how
   urgent. Teal when some of them are the viewer's own. */
.sc-assigned         { background: #f1f5f6; color: #3d5a66; box-shadow: inset 0 0 0 1px #d7e0e3; }
.sc-assigned.is-mine { background: #effcf9; color: #00857a; box-shadow: inset 0 0 0 1px #a8e2d8; }
.sc-tag { opacity: .72; margin-right: .22em; font-size: .88em; font-weight: 600; }

/* On the selected near-black card a pale wash would glare, so the pills invert:
   the colour stays on the type, the ground goes translucent-light. */
/* Same as `.queue-selectlabel.is-on .sc` above — see the note there. */
.queue-view.is-on .sc { box-shadow: inset 0 0 0 1px rgba(255,255,255,.35); }

.queue-stale { color: #b45309; }

/*
 * ⚠ STRIP MODE — the views rail as CHIPS, not as cards on their side.
 *
 * Johan, 2026-08-21: "this is ugly … replace the who options with something
 * much narrower, abbreviate, exclude the sub pills". The first attempt merely
 * set `flex-direction: row` on the existing cards, which kept their padding,
 * their borders and their fixed widths — so "Unassigned" truncated to "Unass…"
 * and the row still cost two lines. Narrower had to mean a DIFFERENT OBJECT,
 * not a squeezed one.
 *
 * So: the same chip the state tabs beside it already are. One line, no cards,
 * no sub-pills, name and count together.
 *
 * ⚠ NOT abbreviated, despite the ask. Without the card padding the full words
 * fit easily — the whole row is about 30rem — and "Unassigned 3" needs no
 * learning where "Unass 3" does. The width came from the chrome, not the words.
 *
 * Applied by class, not media query: the condition is width AND whether
 * anything is open. See `stripViews` in Queue.tsx.
 */
/* ⚠ The body STACKS, so the chips land above the urgency tabs rather than
   beside the list in a column of their own — Johan, 2026-08-21: "move it above
   the urgency selectors". Only the parent can decide this, which is why the
   flag is computed once in Queue.tsx and given to both. */
.queue-body.is-strip { flex-direction: column; gap: .5rem; }

/*
 * NARROW LAYOUT — Johan, 2026-08-26, on the squashed screenshot.
 *
 * ⛔ The roster and the solution select are RENDERED ELSEWHERE at this width
 * (see Queue.tsx), not merely restyled. Inside `.queue-views.is-strip` they
 * inherited `flex-direction: row` and every agent landed on one line, three
 * names deep with their roles above them — the mess in the screenshot. CSS
 * could have unpicked that, but the two blocks genuinely belong somewhere else
 * here: the scope above the list, the roster below it.
 */

/*
 * The solution select, right-justified to the LIST's edge rather than the
 * window's — so it lines up with the thing it scopes, not with the viewport.
 *
 * ⛔ `margin-left: auto` ALONE WAS NOT ENOUGH. It pushes to the right edge of
 * its parent, and the header spans the full page while the list is capped at
 * 36rem — so the select sat far out to the right of the column it belongs to.
 * The header has to take the same cap for the two right edges to meet.
 */
.queue-page.is-strip .queue-head { max-width: 36rem; }
.queue-topsol { margin-left: auto; }
.queue-topsol .queue-solgroup { padding: 0; border: 0; background: none; box-shadow: none; margin: 0; }
.queue-topsol .queue-sollabel { display: none; }

/*
 * The roster, under the list. Capped to the list's width so the page has the
 * same left and right margins all the way down — Johan: "make sure there is
 * space left/right of session list area".
 */
.queue-body.is-strip .queue-roster {
  order: 99; flex: 0 0 auto;
  width: 100%; max-width: 36rem; margin: .25rem 0 0;
}
.queue-body.is-strip .queue-rosterlist { flex-direction: column; }

/*
 * ⛔ THE PAGE SCROLLS, NOT THE LIST — Johan: "space left/right of session list
 * area to scroll entire page and not just the session list".
 *
 * On a wide screen the list is its own scroller so the rail stays put beside
 * it. Stacked, that traps the roster below a list that never ends: you scroll
 * the list to its bottom and the agents are still off-screen. Releasing the
 * inner scroller lets the whole column move together.
 */
.queue-body.is-strip .queue-listsurface {
  overflow-y: visible; flex: 0 0 auto; max-height: none;
}
.queue-body.is-strip .queue-main-col { flex: 0 0 auto; align-self: stretch; max-width: 36rem; }
/* ⚠ The scroll container. Its top padding is the band the sticky row pins
   BELOW — content scrolls through it, so the row's mask above covers what this
   does not remove. Johan: "can also move the sticky header further up". */
.queue-page.is-strip { overflow-y: auto; padding-top: 0; }

/*
 * ⛔ THE TABS STAY PUT while the list scrolls under them.
 *
 * Releasing the list's inner scroller (above) means the whole column moves —
 * which is right, one scroller beats two, and two would send the wheel to
 * whichever area happened to be under the pointer. But it also carried the
 * state tabs off the top of the screen, so switching between Urgent and
 * Important on a long list meant scrolling back up first.
 *
 * ⚠ ONLY THIS ROW. Sticky rows stack, and each one costs vertical space on the
 * screen least able to spare it. The tabs are what an agent moves between
 * constantly; the owner chips are chosen once and the filter row is opened
 * deliberately, so neither earns permanent height. Making all three stick would
 * also need each to know the height of the ones above it, which is a sum that
 * breaks the moment the chips wrap to a second line.
 *
 * ⚠ Opaque background and a little padding, or the list scrolls visibly through
 * it. The negative margin keeps the row flush with the column's edges despite
 * that padding.
 */
.queue-body.is-strip .queue-tabs {
  position: sticky; top: 0; z-index: 20;
  /*
   * ⚠ THE THEME'S OWN BACKGROUND, not a hex matched by eye. A sticky row has to
   * paint something or the list scrolls visibly through it, and a colour merely
   * CLOSE to the page reads as a faint band trailing down the screen.
   */
  background: var(--bs-body-bg, var(--tblr-body-bg, #f2f2f7));
  /*
   * ⚠ Horizontal padding so "All" and "History" are not hard against the band's
   * edges. It insets the row slightly from the list below, which sits flush to
   * the column — enough that the buttons are not touching, not so much that the
   * row looks indented from what it filters.
   */
  padding: .35rem .5rem; margin: 0;
  /* A boundary for cards sliding underneath. */
  box-shadow: 0 .35rem .4rem -.35rem rgba(15, 23, 42, .18);
}
/*
 * ⛔ WHERE THE BLEED ACTUALLY WAS, recorded because I got it wrong twice.
 *
 * Cards showed ABOVE the pinned row, and I assumed stacking and raised the
 * z-index. That could never have worked: the fragment was in the scroll
 * container's own TOP PADDING, and a sticky element pins to the scrollport, so
 * everything above `top: 0` is still scrolling territory — the row cannot cover
 * a band it does not occupy, however high it sits.
 *
 * Then I made the row paint upward with a pseudo-element, which hid the Who
 * chips — because that band is not empty, it is where they live.
 *
 * The fix is simply to have no band: `padding-top: 0` on the scroll container,
 * above. The z-index here is now belt-and-braces rather than the mechanism.
 *
 * ⚠ And only THIS row sticks. Johan, 2026-08-26: "I do not want the Who visible
 * when scrolling... I like the idea of only seeing the urgency ones, cleaner
 * and gives me needed screen space." A wrapper holding both would not have
 * worked anyway: sticky is bounded by its containing block, so tabs inside a
 * short wrapper would unpin the moment the wrapper scrolled past.
 */

/*
 * ⚠ NEVER WIDER THAN THE LIST BELOW IT — Johan, 2026-08-21: "fix so that who
 * selectors do not need more space than the width of the session list".
 *
 * Same max as `.queue-main-col`, and it WRAPS rather than scrolls: a second
 * short row is readable, a horizontally-scrolling row hides views behind a
 * gesture nobody makes with a mouse.
 */
.queue-views.is-strip {
  flex: 0 0 auto; flex-direction: row; align-items: center; flex-wrap: wrap;
  gap: .25rem; max-width: 36rem; overflow: visible; padding-bottom: 0;
  /* ⚠ STRETCH, like .queue-main-col below it. The body top-aligns its children,
     so this row shrank to its chips and "Hours today" sat right after Auto
     (Johan, 2026-09-07, screenshot). Full width up to the same 36rem cap puts
     margin-left:auto at the LIST's right edge. */
  align-self: stretch;
}
.queue-views.is-strip .queue-viewgroup { gap: .25rem; }
/* Hours today rides the views row on a narrow screen, flush with the list's
   right edge (both capped at 36rem). */
.queue-views.is-strip .queue-hours-strip { margin-left: auto; flex: 0 0 auto; }
.queue-views.is-strip .queue-hours-strip .queue-hours { white-space: nowrap; }
.queue-views.is-strip .queue-viewgroup {
  flex-direction: row; align-items: center; gap: .3rem;
  padding: 0; border: 0; background: none; box-shadow: none;
}
/*
 * ⚠ TIGHT ON PURPOSE. Johan, 2026-08-23: "there is a width when the Bot pill
 * moves to the 2nd row — should not happen as there is space on the first row".
 * The strip is capped at the LIST's width by his earlier instruction, so five
 * chips only fit if each is lean: small padding, small gap, no wasted space
 * between name and count.
 */
.queue-views.is-strip .queue-view {
  width: auto; flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: .25rem;
  padding: .12rem .45rem; min-height: 26px;
  border: 1px solid #e5e7eb; border-radius: 999px; background: #fff;
  white-space: nowrap;
}
/*
 * ⚠ THE SELECTED CHIP WAS INVISIBLE — Johan, 2026-08-23: "first who has no
 * text". It had text; it was white on white.
 *
 * `.queue-views.is-strip .queue-view` is three classes and beat
 * `.queue-view.is-on`'s near-black background — but `.is-on`'s white TEXT is
 * set on the same element and survived. So the one chip that matters most, the
 * one you are looking at, rendered blank.
 *
 * ⚠ A lesson worth keeping: when a rule overrides a base, it inherits the
 * responsibility for every state the base defines. Restating the background
 * without restating the state that changes it is how this happens.
 */
.queue-views.is-strip .queue-view.is-on {
  background: #1f2530; border-color: #1f2530; color: #fff;
}
/*
 * ⚠ max-width: none — this is why the first two chips rendered EMPTY. The rail
 * sets `max-width: calc(100% - 2.4rem)` to keep a long agent name off the
 * count; in an auto-width chip that percentage resolves against the chip's own
 * content width, which the name IS, so it collapsed to nothing. Johan: "first
 * two do not have text".
 */
.queue-views.is-strip .queue-viewname { font-size: .72rem; max-width: none; }
.queue-views.is-strip .queue-viewcount { font-size: .72rem; font-weight: 700; }
/* The detail you open the full rail for. The count is the reason to look; the
   split between Urgent and Important is not, at this width. */
.queue-views.is-strip .queue-breakdown,
.queue-views.is-strip .queue-viewlabel,
.queue-views.is-strip .queue-selectlabel { display: none; }
/* The agent picker keeps its list; only its FIELD becomes a chip. */
/* ⚠ The wrapper's card goes — Johan: "remove the ugly box around select an
   agent". In the rail it groups a label, a count and a field; in the strip
   there is only the field, and a box round one control is just a box. */
/* ⚠ Set apart from Mine and UA — Johan, 2026-08-23: "add space between UA and
   that selector pill; Mine and UA are QUICK selectors". They are shortcuts; the
   picker is the full list. The gap is the only thing that can say so once the
   rail's card is gone. */
.queue-views.is-strip .queue-selectwrap {
  border: 0; background: none; padding: 0; border-radius: 0;
  margin-left: .6rem;
}
/* ⚠ And the inset the field carries in the rail comes off with it. In the strip
   the wrapper has no card to be inset FROM, so .5rem of margin would just be a
   gap nobody asked for. (Paired with the padding move on .queue-selectwrap.) */
.queue-views.is-strip .queue-selectwrap .queue-select { margin: 0; }
/*
 * Exactly the chip's own metrics, so the row has ONE height.
 *
 * ⚠ A FIXED height, not min-height — Johan, 2026-08-26: "in any case height of
 * the button shall be same as for Mine, UA and Bot one". Both carried
 * `min-height: 26px` already and still disagreed, because this one's content is
 * taller: a presence dot and a caret beside the text, against a chip holding a
 * word and a number. min-height set a floor the content then exceeded.
 */
.queue-views.is-strip .queue-select {
  width: auto; padding: .15rem .3rem .15rem .55rem;
  height: 26px; min-height: 0; line-height: 1;
  display: inline-flex; align-items: center;
  font-size: .72rem; border-radius: 999px; background: #fff; border-color: #e5e7eb;
}
/* The chips get the same fixed height, so neither side can drift again. */
.queue-views.is-strip .queue-view { height: 26px; min-height: 0; line-height: 1; }

/*
 * ⛔ GREY WHEN A CHIP HOLDS THE SELECTION — Johan, 2026-08-26: "if Mine or
 * Unassigned buttons are selected in this one show 'select agent' or 'agent' in
 * grey".
 *
 * At this width those two are no longer options in this list, so the field has
 * nothing of its own selected. Reading as unset is the honest state; naming
 * them here would point at options that are not in the list it opens.
 */
.queue-views.is-strip .queue-selectvalue.is-placeholder { color: #9ca3af; font-weight: 500; }
/*
 * ⚠ THE SAME WHITE-ON-WHITE AS THE CHIPS, and I missed the twin — Johan,
 * 2026-08-23: "if I select a specific agent, that pill shows no text".
 *
 * The rule above is three classes and beat `.queue-select.is-on`'s near-black
 * background; `.is-on`'s white TEXT is on the same element and survived. So a
 * selected agent rendered as a wide blank pill — exactly what happened to the
 * selected view chip on the same day, in the same way.
 *
 * The lesson, twice over: a rule that restates a base's background inherits
 * responsibility for every STATE the base defines. When one of these gets
 * fixed, look for the others.
 */
.queue-views.is-strip .queue-select.is-on {
  background: #1c2530; border-color: #1c2530; color: #fff;
}
.queue-views.is-strip .queue-select.is-echo {
  background: #eef2f6; border-color: #c3ccd6; color: #313a45;
}
/* The caret has no segment of its own at chip size — it would read as a second
   control glued to the first. */
.queue-views.is-strip .queue-selectcaret {
  border-left: 0; background: none; color: inherit;
}
.queue-views.is-strip .queue-selectcaret { padding: 0 .2rem; border: 0; background: none; }
/* ⚠ TRIMMED, never wrapped — Johan, 2026-08-23: "display agent name, but cut
   the name if it does not fit". `white-space: nowrap` is what actually enforces
   that; without it "Agent 1 AllSolutions" takes a second line and the chip row
   grows taller than everything beside it. The full name is on the button's
   title. */
.queue-views.is-strip .queue-selectvalue {
  max-width: 8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Country flag and conversation language. Both omitted when unknown, so these
   only ever style something real. */
.queue-flag-emoji { font-size: .85rem; line-height: 1; display: inline-block; width: 1.2rem; text-align: center; }
/* The fixed geo slot on a queue row: flag, then environment — Johan, 2026-09-12. */
.qr-geo { flex: 0 0 auto; width: 5.4rem; display: inline-flex; align-items: center; gap: .35rem; margin-right: .3rem; }
/* The environment chip is a fixed box too, so the flag and the chip each have ONE x down the list — Johan, 2026-09-16. */
.qr-geo .qr-env { width: 3.4rem; text-align: center; overflow: hidden; text-overflow: ellipsis; }
/* The owner's slot: fixed width, name right-aligned and trimmed, the presence dot after it. */
.qr-ownerslot { flex: 0 0 7rem; display: inline-flex; align-items: center; justify-content: flex-end; gap: .3rem; min-width: 0; }
.qr-ownerslot .queue-by { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.qr-env {
  font-size: .58rem; text-transform: uppercase; letter-spacing: .04em; white-space: nowrap;
  background: #f3f4f6; color: #4b5563; border-radius: .2rem; padding: .05rem .25rem;
}
.queue-lang {
  font-size: .58rem; text-transform: uppercase; letter-spacing: .04em;
  background: #f3f4f6; color: #4b5563; border-radius: .2rem; padding: .05rem .25rem;
}
.queue-lastmsg { font-size: .66rem; color: #9ca3af; font-variant-numeric: tabular-nums; }

/* Overlay chips — Action requests and Auto are true ALONGSIDE the exclusive
   states, not instead of them, so they read as a separate group. */

/*
 * ⚠ THE ONE PILL THAT NEVER JOINED THE SCHEME — Johan, 2026-08-24: "did you
 * not change the urgent/important/replied selectors to the new colour scheme,
 * plus the pills in list and ACIs?"
 *
 * Urgent, Important, Replied and Action were all converted to tokens. Bot's
 * ACI pill was missed and kept its pre-scheme literals — so a bot-handled
 * session read BLUE in the list (`.qr-state-bot`, `--st-bot`) and PURPLE in
 * the pane, for the same conversation on the same screen.
 *
 * Worse than a mismatch: purple is now `--st-action`, which means "somebody
 * has to DO something". The pane was flagging every auto chat with the colour
 * that means action needed.
 */
.sc-bot { background: var(--st-bot-fill); color: var(--st-bot-ink); }

/* The initial beside each sub-pill number. Slightly lighter than the digit so
   the count still reads first — the letter is there to disambiguate, not to
   compete. */
.sc-tag { opacity: .72; margin-left: .22em; font-size: .88em; font-weight: 600; }

/* The agent picker — a TABLE, not a list of pills.
   Fixed columns with a header each, so a column of numbers can be read down.
   Inline pills of varying width could not be compared between agents at all. */
.queue-agentrow {
  display: grid;
  grid-template-columns: minmax(7rem, 1fr) repeat(4, 3.1rem);
  align-items: center; gap: .2rem;
  width: 100%; text-align: left;
  padding: .3rem .45rem; border-radius: .3rem;
}
.queue-agenthead {
  padding-bottom: .2rem; margin-bottom: .1rem;
  border-bottom: 1px solid #e5e7eb;
}
.ac-h {
  font-size: .55rem; text-transform: uppercase; letter-spacing: .04em;
  text-align: right; font-weight: 600;
}
.ac-n {
  font-size: .78rem; text-align: right;
  font-variant-numeric: tabular-nums; font-weight: 600;
}
/* Same four colours as the pills, as TEXT here — a coloured chip per cell would
   fight the alignment the columns exist to create. All four clear 4.5:1 on
   white as text. */
/* ⚠ TOKENS. These four were the LAST literals in the state family, and they
   name the same buckets as the tabs and pills — so after the palette change
   they would have shown the old amber and grey beside the new orange and
   green, on the same screen. */
.ac-urgent    { color: var(--st-urgent); }
.ac-important { color: var(--st-important); }
.ac-replied   { color: var(--st-replied); }
.ac-action    { color: var(--st-action); }

/* Close the conversation. Distinct from the phone back-chevron: when the list
   and the pane fit side by side there is nothing to go "back" to, so dismissing
   needs its own affordance. */
.cv-close {
  border: none; background: none; cursor: pointer;
  font-size: 1.35rem; line-height: 1; color: #94a3b8;
  padding: 0 .3rem; margin-left: .2rem;
}
.cv-close:hover { color: #334155; }

/* "Enter comment" — quieter than the note itself, so an empty field reads as an
   invitation rather than as content. */
.cv-note-input::placeholder { font-style: italic; font-size: .82rem; color: #a8a29e; }

/* Air above the first row. The list started hard against the filters, which made
   the two read as one block. */
.queue-list { padding-top: .35rem; }

/* A floor under every bubble, so "hi" is not a sliver with a timestamp wider
   than its own message. Best practice in every chat client: short messages keep
   a readable minimum. */
/* Wide enough that a short message and its trailing timestamp sit on ONE line
   rather than the time dropping under two words. */
.cv-bubble { min-width: 10.5rem; }

/* The small formatting a message actually carries. Built as React elements from
   parsed segments — never injected HTML, since the same field carries whatever a
   shopper types. */
.rt-b { font-weight: 600; }
.rt-i { font-style: italic; }
.rt-u { text-decoration: underline; }
.cv-text a { color: inherit; text-decoration: underline; }
.cv-text a:hover { opacity: .85; }

/* Stands in for the state tabs under Bot, so the strip does not collapse to an
   empty rule and the view says what it is. */
.queue-tabnote {
  font-size: .8rem; color: #5b6673; padding: .45rem .2rem;
  font-style: italic;
}

/* An auto session a human has been in. Quiet, because in an agent-served list
   it would be true of everything — it only means something among bot rows. */
.queue-touched {
  font-size: .62rem; letter-spacing: .01em;
  background: #eef2f6; color: #4b5563;
  border-radius: 999px; padding: .05rem .4rem;
}

/* --- Conversation: dates, colours, ticks ---------------------------------- */

/* Each date pins to the top of the scroller until the next pushes it out, so
   scrolling back always shows which day you are reading. Sticky on the
   separator itself, not a floating overlay tracking scroll — nothing to
   measure and nothing to drift. */
/*
 * ONE SECTION PER DAY. This is what makes the separator behave.
 *
 * ⚠ Sticky siblings sharing a container all pin at the same offset and overlap —
 * two date pills sat stacked a few millimetres apart. A sticky element is
 * bounded by its own containing block, so a section per day means the next
 * section's arrival PUSHES the previous pill out instead of landing on it.
 */
/*
 * `padding-top` is the HANDOVER DISTANCE, not decoration. It sits above the
 * pill, so the outgoing separator is pushed out while the incoming one is still
 * this far short of the top: ~20px, the low end of the 5–10mm Johan asked for.
 * Raising it makes the new date take over earlier and opens the gap between
 * days; lowering it delays the handover until they nearly touch.
 */
.cv-daygroup {
  display: flex; flex-direction: column; gap: .35rem;
  padding-top: 1.25rem;
}
/* No leading gap above the very first day in the thread. */
.cv-daygroup:first-of-type { padding-top: .35rem; }

.cv-day {
  position: sticky; top: 0; z-index: 5;
  align-self: center; width: max-content;
  /*
   * ⚠ The space above the pill belongs to the SECTION, not to the pill.
   *
   * A margin here would sit inside the sticky element's own scroll area and
   * delay the handover until the two pills were touching. As section padding it
   * is above the pill, so the outgoing one is pushed out while the incoming one
   * is still this far short of the top — the new date takes over with a gap
   * rather than after a collision, which is what Johan asked for (2026-08-19).
   */
  margin: 0 auto .35rem;
  padding: .15rem .6rem;
  font-size: .66rem; letter-spacing: .04em; text-transform: uppercase;
  color: #4b5563; background: #eef1f5;
  border: 1px solid #dfe3e8; border-radius: 999px;
}

/* ALL THREE are the old ACI's own colours, so an agent moving between the two
   consoles reads the same conversation the same way.

   ⚠ They were never in this repo's CSS. The old console (removed 2026-09-10)
   embedded the shopper widget to render its thread, so the values came from
   the widget repo's `src/themes/sans-blue.css.ts` as theme variables:
   `bubbleEngine` #4675bf (the bot), `bubbleAgent` #657e9a (a person),
   `bubbleHost` #c8dcfb (the visitor). These three are now the only copy.

   ⚠ The theme is per client, so these are sans-blue's. A client on a different
   widget theme sees different bubbles in their own widget; the agent console is
   always this one. */
/*
 * ⚠ FOUR SPEAKERS, FOUR COLOURS — Johan, 2026-08-21: "a new colour for Agent …
 * perhaps different colours for agent X and agent you/me".
 *
 * Agent messages were #657e9a, a grey-blue near enough the bot's #4675bf that a
 * handed-over conversation read as one long bot monologue. Now:
 *
 *   #4675bf  bot        blue, unchanged — Johan: "dark blue for bot is good"
 *   #0d9488  YOU        bright teal
 *   #3d5a66  another agent   the same teal family, darker and drained
 *   #c8dcfb  the visitor     pale blue, unchanged
 *
 * The two agent colours share a family because "a human wrote this" is the
 * first thing to read; brightness carries whether that human was you.
 */
.cv-from-bot { background: #4675bf; color: #fff; }
/* #0b8378 gives white text 4.63:1 — the same legibility as the bot's #4675bf
   (4.61), so neither of our two voices is harder to read than the other. */
.cv-from-me { background: #0b8378; color: #fff; }
.cv-from-agent { background: #3d5a66; color: #fff; }
.cv-from-shopper { background: #c8dcfb; color: #10131a; }
.cv-from-system { background: #f2f4f7; color: #4b5563; }
.cv-bubble .cv-label { color: rgba(255,255,255,.78); }
.cv-from-shopper .cv-label { color: #3f5170; }
.cv-from-me .cv-label, .cv-from-me .cv-time { color: rgba(255,255,255,.85); }
.cv-bubble .cv-time { color: rgba(255,255,255,.72); }
.cv-from-shopper .cv-time { color: #4a5a75; }
.cv-bubble.is-failed { background: #fee2e2; color: #991b1b; }
.cv-bubble.is-failed .cv-time { color: #b91c1c; }
/* The peek is the bubble's text in the bubble's colours — the time's fade
   above must not reach it, so its colour is restated per palette row. */
.cv-bubble .cv-peek { color: #fff; }
.cv-from-shopper .cv-peek { color: #10131a; }
.cv-from-system .cv-peek { color: #4b5563; }
.cv-bubble.is-failed .cv-peek { color: #991b1b; }
.cv-text a { color: inherit; }

/* Delivery ticks — our side only. */
.cv-ticks { margin-left: .3rem; letter-spacing: -.08em; opacity: .85; }

/* The offered buttons: ONE bubble, no header, no timestamp, stacked. They
   belong to the message above rather than to a turn of their own. */
.cv-optionbox {
  display: flex; flex-direction: column; align-items: stretch; gap: .25rem;
  max-width: 78%; padding: .35rem;
  background: #eef1f8; border: 1px solid #d7deec; border-radius: .5rem;
}
.cv-option {
  display: block; text-align: center;
  border: 1px dashed #9aa9c9; border-radius: .35rem;
  padding: .22rem .5rem; font-size: .78rem; color: #3c4a66; background: #fff;
}

/* Events are notes in the margin, not messages: italic, no fill. */
.cv-event {
  align-self: center; text-align: center;
  margin: .3rem auto; padding: .1rem .3rem;
  font-size: .74rem; font-style: italic; color: #6b7280;
  background: none; border: none;
}
.cv-event-time { margin-left: .4rem; font-style: normal; color: #9ca3af; font-size: .68rem; }

/*
 * A run of comings and goings — Johan, 2026-09-28. One line instead of eleven
 * rows, so the COLOUR has to carry the direction: green came online, red left
 * the page. Wrapping, because a restless afternoon is a lot of times.
 */
.cv-presence-run {
  margin-left: .4rem; font-style: normal; font-size: .68rem;
  display: inline-flex; flex-wrap: wrap; justify-content: center; gap: .1rem .45rem;
}
.cv-presence-on { color: #15803d; }
.cv-presence-off { color: #b91c1c; }

/* The send guard's question. Amber, because it is a doubt rather than a failure
   — an error red would say the message could not be sent, and it can. */
.cv-doubt {
  flex: 0 0 auto; display: flex; flex-wrap: wrap; align-items: center;
  gap: .4rem .6rem; padding: .5rem .7rem;
  background: #fdf1de; border-bottom: 1px solid #f0dcb8;
  font-size: .78rem; color: #7a4a06; line-height: 1.45;
}
.cv-doubt-acts { display: flex; gap: .35rem; margin-left: auto; flex: 0 0 auto; }

/* An action that has been dealt with. Green and quiet: it is a record, not a
   thing to act on — the loud purple belongs to the ones still outstanding. */
.sc-actiondone { background: #e7f5ec; color: #15803d; box-shadow: inset 0 0 0 1px #cbe9d6; }
/* A courtesy follow-up: amber outline, lighter than a task — Johan, 2026-09-15. */
.sc-courtesy, .cv-pill.sc-courtesy { background: #fdf3e3; color: #b45309; box-shadow: inset 0 0 0 1px #f0c58a; }
.cv-action-item { display: inline-flex; align-items: center; gap: .3rem; }
/* The popups hang from their ROW and sit centred in the pane — Johan, 2026-09-16: "make sure the complete action popup is displayed, best to keep it centred in the ACI". Anchored to the pill they ran past the pane's edge and under the next pane. */
.cv-head-action, .cv-head-state { position: relative; }
/* The pill as a button: same look, a hand cursor, a ring while its popup is open. */
/* ⚠ NOT `font: inherit` — the shorthand reset the size to the header's and made the pill twice the others (Johan, 2026-09-16: "a very ugly pill, must be smaller"). Only the family is inherited; size, weight and padding stay .cv-pill's. */
.cv-pill-btn { border: 0; font-family: inherit; cursor: pointer; margin: 0; transition: box-shadow .12s, filter .12s; }
/* The pill says it can be clicked — Johan, 2026-09-16: "mouse over effect on it? How?". A ring in the pill's own ink and a touch darker, no new colour. */
/*
 * ⚠ A LONG TASK NAME STOPS AT ~10rem WITH "…" — Johan, 2026-09-21: "max this
 * width: 'Test task is a not lot...'". By width,
 * not word count: words vary too much for a count to fit. The whole name is in
 * the popup; the due date beside the pill stays visible.
 */
.cv-action-item { min-width: 0; max-width: 100%; }
.cv-action-item .cv-pill-btn { max-width: 10rem; min-width: 0; flex: 0 1 auto; }
/* Spans both columns of the facts grid — in one cell it shifted every label onto the wrong value. */
.cv-facts-name { grid-column: 1 / -1; font-weight: 600; margin-bottom: .2rem; white-space: normal; overflow-wrap: anywhere; }
.cv-pill-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cv-pill-btn:hover { filter: brightness(.93); box-shadow: 0 0 0 2px currentColor; }
.cv-pill-btn.is-open { outline: 2px solid #4f46e5; outline-offset: 1px; }
.cv-actionpop {
  position: absolute; z-index: 40; top: calc(100% + .25rem); left: 50%; transform: translateX(-50%);
  width: min(20rem, calc(100% - .5rem));
  /* Stronger edge than the pane's chrome — Johan, 2026-09-16: "need more contrast". */
  background: #fff; border: 1.5px solid #64748b; border-radius: .4rem; padding: .5rem .6rem;
  box-shadow: 0 10px 28px rgba(28,37,48,.28);
  display: flex; flex-direction: column; gap: .35rem; font-size: .74rem; color: #1c2530; white-space: normal;
}
.cv-actionpop-what { font-weight: 600; }
/* A courtesy follow-up is told by colour, not by a row — the heading takes the pill's amber. */
.cv-actionpop-what.is-courtesy { color: #b45309; }
.cv-actionpop-meta { color: #4b5563; font-size: .7rem; display: flex; flex-direction: column; gap: .1rem; }
.cv-actionpop-row { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-top: .1rem; }
.cv-actionpop-remove { color: #b91c1c; margin-left: auto; }
.cv-actionpop-confirm { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; font-size: .72rem; color: #7f1d1d; background: #fde8e8; border-radius: .3rem; padding: .35rem .5rem; }
.cv-actionpop-confirm span { flex: 1 1 100%; }
.cv-actionpop-confirm .cv-note-cancel { margin-left: auto; }
.cv-note-save.cv-actionpop-confirmdel:not(:disabled) { background: #b91c1c; border-color: #b91c1c; }
.cv-actionpop-x { position: absolute; top: .2rem; right: .3rem; border: 0; background: none; color: #6b7280; font-size: 1.1rem; line-height: 1; padding: .1rem .3rem; cursor: pointer; border-radius: .25rem; }
.cv-actionpop-x:hover { color: #1c2530; background: #f3f4f6; }
.cv-actionpop-what { padding-right: 1.4rem; }
.cv-actionpop-top { display: flex; align-items: flex-start; gap: .5rem; padding-right: 1.9rem; }
.cv-actionpop-top .cv-actionpop-what { flex: 1 1 auto; min-width: 0; padding-right: 0; }
.cv-actionpop-done {
  flex: 0 0 auto; min-height: 0; padding: .1rem .45rem; font-size: .7rem; white-space: nowrap;
  border-color: #86c5a0; color: #166534; background: #fff;
}
.cv-actionpop-done:hover:not(:disabled) { background: #ecfdf3; }
/*
 * An open task's pill, coloured by WHEN — Johan, 2026-09-21. Four states and
 * no fifth: a task has a date in the past, today, the future, or none at all.
 * The kind (task or courtesy follow-up) is in the words on the pill, which is
 * where a distinction that never needs scanning belongs.
 */
.cv-pill.is-due-overdue { background: #fdeceb; color: #b4342b; box-shadow: inset 0 0 0 1px #f3bdb8; }
.cv-pill.is-due-today   { background: #fdf3e3; color: #b45309; box-shadow: inset 0 0 0 1px #f0c58a; }
.cv-pill.is-due-later   { background: #eef0fe; color: #4338ca; box-shadow: inset 0 0 0 1px #c9cdf8; }
.cv-pill.is-due-none    { background: #f3f4f6; color: #4b5563; box-shadow: inset 0 0 0 1px #e0e3e8; }

.cv-due { font-size: .66rem; font-weight: 600; color: #b45309; white-space: nowrap; }
.cv-due.is-later { color: #6b7280; font-weight: 500; }
.cv-due.is-overdue { font-weight: 700; }
.qr-due { font-size: .66rem; font-weight: 600; color: #b45309; white-space: nowrap; margin-left: .3rem; }
.qr-due.is-later { color: #6b7280; font-weight: 500; }
.qr-due.is-overdue { font-weight: 700; }
.qr-action.is-courtesy { background: #fdf3e3; color: #b45309; box-shadow: inset 0 0 0 1px #f0c58a; }
/* The Add action popup, under the link beside the switch (2026-09-16). */
.cv-addaction-anchor { position: static; flex: 0 0 auto; }
.cv-addaction-link { font-size: .68rem; }
.cv-addaction {
  position: absolute; z-index: 40; top: calc(100% + .25rem); left: 50%; transform: translateX(-50%);
  width: min(20rem, calc(100% - .5rem));
  /* Stronger edge than the pane's chrome — Johan, 2026-09-16: "need more contrast". */
  background: #fff; border: 1.5px solid #64748b; border-radius: .4rem; padding: .55rem .6rem;
  box-shadow: 0 10px 28px rgba(28,37,48,.28);
  display: flex; flex-direction: column; gap: .35rem;
}
.cv-addaction-label { font-size: .62rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #8a94a3; }
.cv-addaction-opt { font-weight: 500; text-transform: none; letter-spacing: 0; color: #9ca3af; }
.cv-addaction-input { font: inherit; font-size: .78rem; border: 1px solid #d7dee7; border-radius: .3rem; padding: .3rem .45rem; background: #f8fafc; }
.cv-addaction-recent { display: flex; flex-wrap: wrap; gap: .25rem; }
/* The used task names under the name field — most-used first, narrowed as you type (Johan, 2026-09-22). */
.cv-addaction-list {
  list-style: none; margin: -.15rem 0 0; padding: .2rem 0; max-height: 11rem; overflow-y: auto;
  border: 1px solid #d7dee7; border-radius: .3rem; background: #fff; box-shadow: 0 6px 16px rgba(28,37,48,.12);
  /* ⚠ Its own type: inside the popup it inherited the uppercase label style (Johan, 2026-09-22). */
  text-transform: none; letter-spacing: normal; font-weight: 400;
}
.cv-addaction-list li { padding: .22rem .5rem; font-size: .74rem; line-height: 1.3; color: #1f2937; cursor: pointer; text-transform: none; letter-spacing: normal; font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-addaction-list li.is-hi, .cv-addaction-list li:hover { background: #eef2ff; }
.cv-addaction-chip { font: inherit; font-size: .68rem; border: 1px solid #d7dee7; border-radius: 999px; padding: .1rem .5rem; background: #fff; cursor: pointer; color: #374151; }
.cv-addaction-chip:hover { background: #eef2f7; }
.cv-addaction-error { font-size: .72rem; color: #b91c1c; }
.cv-addaction-count { font-weight: 500; text-transform: none; letter-spacing: 0; color: #9ca3af; margin-left: .3rem; }
.cv-addaction-count.is-over, .cv-addaction-input.is-over { color: #b91c1c; }
.cv-addaction-input.is-over { border-color: #f87171; }
.cv-addaction-comment { resize: vertical; min-height: 2.6rem; font-size: .74rem; }
.cv-addaction-comment::placeholder { font-style: italic; color: #9ca3af; }
.cv-addaction-due { display: flex; flex-wrap: wrap; gap: .25rem; position: relative; }
.cv-addaction-chip.is-on { border-color: #4f46e5; background: #eef2ff; color: #3730a3; font-weight: 600; }
.cv-addaction-fixed { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.cv-addaction-fixedhint { font-size: .7rem; color: #6b7280; flex-basis: 100%; }
.cv-addaction-unpick { border: 0; background: none; color: #6b7280; font-size: .95rem; line-height: 1; padding: 0 .2rem; cursor: pointer; margin-left: -.15rem; }
.cv-addaction-unpick:hover { color: #1c2530; }
/* The one fixed chip, in the courtesy pill's amber — Johan, 2026-09-16. */
.cv-addaction-chip-courtesy { border-color: #f0c58a; background: #fdf3e3; color: #b45309; }
.cv-addaction-chip-courtesy.is-on { border-color: #b45309; background: #fbe6c6; color: #7c4a03; font-weight: 600; }
/* The calendar's input: present for showPicker(), never typed into. */
.cv-addaction-datehidden { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; bottom: 0; left: 0; }
.cv-addaction-chosen { font-size: .72rem; color: #374151; }
/* The visitor's rating, one face beside the state pill — Johan, 2026-09-16. */
.cv-csat { font-size: .95rem; line-height: 1; cursor: default; }
/* An ignored rating: still a fact on the chat, out of every number — Johan, 2026-09-16. */
.cv-csat.is-ignored { filter: grayscale(1); opacity: .5; }
.cv-push { flex: 1 1 auto; }
.cv-csat-row { flex: 0 0 auto; }
.qr-csat { margin-left: .2rem; font-size: .85rem; flex: 0 0 auto; }
.cv-facts { margin: 0; display: grid; grid-template-columns: max-content 1fr; column-gap: .6rem; row-gap: .12rem; font-size: .7rem; }
.cv-facts-row { display: contents; }
.cv-facts dt { color: #6b7280; font-weight: 500; white-space: nowrap; }
.cv-facts dd { margin: 0; color: #1c2530; white-space: pre-wrap; }
.cv-facts-foot { grid-column: 1 / -1; margin-top: .25rem; color: #6b7280; font-style: italic; }
.cv-actionpop-due { display: flex; flex-direction: column; gap: .3rem; font-size: .72rem; }
.cv-actionpop-dueline { display: flex; align-items: center; gap: .5rem; position: relative; }
.cv-actionpop-note { display: flex; flex-direction: column; gap: .3rem; }
.cv-actionpop-notehead { display: flex; align-items: center; gap: .3rem; font-size: .62rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #8a94a3; }
.cv-actionpop-pen { width: .8rem; height: .8rem; vertical-align: -.1rem; }
.cv-actionpop-editpen { border: 0; background: none; padding: 0 .15rem; cursor: pointer; color: #6b7280; display: inline-flex; }
.cv-actionpop-editpen:hover { color: #1d4ed8; }
.cv-editable {
  display: inline-flex; align-items: center; gap: .3rem; max-width: 100%;
  margin: -.1rem -.25rem; padding: .1rem .25rem; border-radius: .25rem; cursor: pointer;
}
.cv-editable.is-block { display: flex; align-items: flex-start; justify-content: space-between; }
.cv-editable-pen { flex: 0 0 auto; color: #9ca3af; }
.cv-editable:hover, .cv-editable:focus-visible { background: #eef2f7; outline: none; }
.cv-editable:hover .cv-editable-pen, .cv-editable:focus-visible .cv-editable-pen { color: #1d4ed8; }
.cv-actionpop-rename { display: flex; flex-direction: column; gap: .3rem; padding-right: 1.4rem; }
.cv-actionpop-addnote { display: inline-flex; align-items: center; gap: .25rem; }
.cv-actionpop-notetext { font-size: .72rem; color: #374151; white-space: pre-wrap; }
.cv-actionpop-addnote { align-self: flex-start; font-size: .7rem; }
.cv-addaction-row { display: flex; align-items: center; gap: .6rem; margin-top: .15rem; }

/* The action request and its control, on their own row — a parallel axis to the
   states above, so it reads as its own statement rather than one more pill. */
.cv-head-action { gap: .45rem; }
/* The action's own words, under the pill + control. Quiet — it is context for
   a task, not an alert; the pill above is what draws the eye. */
.cv-head-action-info { gap: .35rem; }
.cv-action-info {
  font-size: .68rem;
  line-height: 1.3;
  color: var(--cv-muted, #6b7280);
}
/* The lookups' neighbour: a toggle that NARROWS a view rather than replacing
   it, so it is upright where Recent and History are italic. */
.queue-tab-action { font-weight: 600; }

/* "The agent says their work here is finished" — quiet, because it is a
   statement about intent rather than a thing needing attention. Distinct from
   the answering switch on purpose: done and handed-back are different acts. */
.sc-finalized { background: #eef1f5; color: #4b5563; box-shadow: inset 0 0 0 1px #dfe3e8; }

/*
 * ⚠ SOMETHING IS OWED — Johan, 2026-08-21: "violet for all action needed".
 * An open transfer request or an outstanding agent action.
 *
 * ⚠ WHEN BOTH ARE TRUE, RED WINS — Johan asked the question, 2026-08-21, and
 * the answer is not "which is more urgent" but which would otherwise VANISH.
 * Something owed already carries its own mark on the row: the solid violet
 * transfer pill, or the violet action pill on line three. "Waiting on us" has
 * nothing but this stripe. So the stripe goes to the fact with no other voice.
 *
 * (An earlier version had violet win, on the weaker argument that it was the
 * more specific fact. It was, and it was also the one that could afford to
 * lose.)
 *
 */
.queue-row.is-owed { border-left: 3px solid #7c3aed; }
/*
 * ⚠ NEEDS AN ANSWER — the mark that survived whose-turn being demoted from a
 * bucket boundary to an ordering. Urgent now holds live conversations whoever
 * spoke last, so the ones actually waiting on us have to be visible at a
 * glance; the sort already puts them first, this says why they are there.
 *
 * ⚠ LAST ON PURPOSE. Same specificity as .is-owed, so ORDER is what makes red
 * win when a row is both. Do not reorder these two.
 */
.queue-row.is-waiting { border-left: 3px solid #dc2626; }
/* .queue-wait removed 2026-08-20 — the waiting signal is the row itself. */

/* ─────────────────────────────────────────────────────────────────────────────
 * THE HEADER, ONCE THE HAMBURGER IS OPEN
 *
 * Johan, 2026-08-21: "fix so that the AIM header looks ok in phone when I click
 * the menu button".
 *
 * The navbar is built for a wide screen and collapses below reactstrap's `lg`
 * (991.98px). Everything in it is laid out for that wide row: the nav links sit
 * left, and the account block, the deploy chip and the language picker are all
 * pushed hard RIGHT with `align-items-end` and `me-3`. Stacked into a menu that
 * is now the full width of a phone, the two halves end up on opposite edges
 * with a ragged gutter down the middle, and the tap targets are whatever height
 * their content happened to be.
 *
 * ⚠ Scoped to the collapsed state only. Every rule here is inside the media
 * query and under `.navbar`, so the desktop header — which is fine — is
 * untouched.
 *
 * ⚠ Not verified in a phone-width browser. The zoom on the machine this was
 * written on reports innerWidth 1655 for a 756px window, so a true phone
 * viewport could not be reproduced; these fix the structural causes above.
 * Worth a look on the actual device.
 */
@media (max-width: 991.98px) {
  /* One column, left-aligned, with air between the groups. The `align-items-end`
     in the markup is a desktop instruction and reads as a mistake here. */
  .navbar .navbar-collapse {
    align-items: stretch;
    gap: .75rem;
    padding: .75rem 0 .25rem;
  }
  .navbar .navbar-collapse > * { margin-right: 0 !important; }
  .navbar .navbar-collapse .align-items-end { align-items: flex-start !important; }
  .navbar .navbar-collapse .justify-content-end { justify-content: flex-start !important; }

  /* Full-width rows: a menu item you have to hit exactly is a menu item you
     miss, and on a phone the whole row is the affordance. */
  .navbar .navbar-collapse .nav-link {
    display: block; padding: .5rem .25rem; min-height: 44px;
  }
  .navbar .navbar-collapse .navbar-nav { width: 100%; gap: .1rem; }

  /* The account line reads as one sentence rather than a right-aligned stack. */
  .navbar .navbar-collapse .mb-1 { width: 100%; }

  /* A rule between the navigation and the account/settings block, so the menu
     has two visible halves instead of one long list of unlike things. */
  .navbar .navbar-collapse > .me-3 {
    border-top: 1px solid rgba(255, 255, 255, .18);
    padding-top: .6rem;
  }

  /* The language picker sized itself from a flex row that no longer exists. */
  .navbar .navbar-collapse select { max-width: 12rem; }
}

/* ── Integration sections, inside the conversation ─────────────
 *
 * ⛔ The SHELL for every third-party panel — Gorgias today, Shopify or a
 * client CRM next (Johan, 2026-08-25). Named `cv-intg-*` rather than after any
 * one product so the second integration inherits the look instead of inventing
 * one.
 *
 * Sections STACK, which is why each is quiet: five of these competing for
 * attention above a conversation would bury the thing being read. And a
 * section with nothing to say does not render at all — see IntegrationSection.
 */
/*
 * ⛔ A CLOSED BOX, so it is obvious where the other system's data ENDS —
 * Johan, 2026-08-25: "make it clearer where Gorgias data ends (after 'reopen
 * ticket') and where jb chat starts".
 *
 * It had a top border only, so an expanded ticket's last line ran straight
 * into the transcript's first bubble. Two systems' messages touching, in the
 * same pane, with nothing between them is exactly how someone quotes a Gorgias
 * reply as though it had been said in the chat.
 *
 * Bottom border AND a tinted ground the transcript does not share: the edge
 * says where, the ground says which.
 */
.cv-intg {
  border-top: 1px solid #dbe1e9;
  border-bottom: 2px solid #dbe1e9;
  background: #f4f7fa;
}
.cv-intg-strip {
  width: 100%; display: flex; align-items: center; gap: .4rem;
  padding: .3rem .6rem; font-size: .72rem; color: #475569;
  background: #f7f9fb; border: none; cursor: pointer; text-align: left;
}
.cv-intg-strip:hover, .cv-intg-strip.is-open { background: #eef2f7; }
.cv-intg-name { font-weight: 700; letter-spacing: .02em; }
.cv-intg-count { color: #64748b; }
/* The ONE piece of colour a section gets, and only when it has something that
   asks to be acted on. Spending it by default would make it mean nothing. */
.cv-intg-badge {
  background: var(--st-important-fill); color: var(--st-important-ink);
  border-radius: 999px; padding: 0 .35rem; font-size: .64rem; font-weight: 700;
}
.cv-intg-err { color: var(--st-urgent); font-weight: 600; }
.cv-intg-icon { display: inline-flex; align-items: center; color: #64748b; }
/*
 * ⛔ OPEN vs CLOSED-ONLY, on the strip itself — Johan, 2026-08-25. The icon and
 * the name both take it, because the icon alone is 12px and the name is what
 * the eye lands on.
 *
 * ⚠ Colour is the SECOND signal, never the only one: the badge says "2 open"
 * or "all closed" in words. Amber is the console's "somebody must act", the
 * same token the Important bucket uses — one meaning, one hue.
 */
.cv-intg-strip.has-live .cv-intg-icon,
.cv-intg-strip.has-live .cv-intg-name { color: var(--st-important); }
.cv-intg-strip.is-settled .cv-intg-icon,
.cv-intg-strip.is-settled .cv-intg-name { color: #64748b; }
/* Settled history is background — the badge drops the amber fill it carries
   when something is open, or "all closed" would shout as loudly as "2 open". */
.cv-intg-strip.is-settled .cv-intg-badge {
  background: #e5e7eb; color: #4b5563; font-weight: 600;
}
/* Larger than the text glyph it replaced, and it is the strip's only control —
   Johan, 2026-08-25. */
.cv-intg-caret { margin-left: auto; color: #64748b; flex: 0 0 auto; }
.cv-intg-strip:hover .cv-intg-caret { color: #334155; }
/*
 * ⛔ DRAG THE BOTTOM EDGE to give it more room — Johan, 2026-08-25: "possibly
 * also allow to drag bottom border of Gorgias section to expand it and take
 * space from the jb chat".
 *
 * `resize: vertical` does it natively: the browser draws the grip, handles the
 * drag, and remembers nothing — which is right, because the next conversation
 * is a different question and should start compact again.
 *
 * ⚠ NO JS, deliberately. A hand-rolled splitter here would need pointer
 * capture, a min/max, and its own idea of what the transcript may give up —
 * three things to get wrong for a behaviour the platform already has. It costs
 * `overflow: auto`, which this needs anyway to bound a long ticket.
 *
 * ⚠ The inner message list must NOT also scroll, or a long ticket produces two
 * nested scrollbars and dragging the outer one appears to do nothing.
 */
.cv-intg-body {
  padding: .4rem .6rem .6rem; background: #f4f7fa;
  max-height: 18rem; overflow: auto; resize: vertical;
}
/*
 * The closing line, INSIDE the box — it names what has just ended rather than
 * leaving the reader to infer it from a border. Only when expanded: a collapsed
 * strip needs no epilogue.
 */
.cv-intg-end {
  display: flex; align-items: center; gap: .4rem;
  margin-top: .5rem; padding-top: .35rem; border-top: 1px dashed #c8d2dd;
  font-size: .62rem; text-transform: uppercase; letter-spacing: .04em;
  color: #94a3b8;
}
.cv-intg-error { margin: 0; font-size: .74rem; color: var(--st-urgent); display: flex; gap: .4rem; align-items: baseline; }

/* ── Gorgias-specific: a ticket and its messages ───────────────── */
.cv-gticket { padding: .4rem 0; border-bottom: 1px solid #eef1f5; }
.cv-gticket:last-child { border-bottom: none; }
.cv-gticket-head { display: flex; align-items: baseline; gap: .4rem; }
.cv-gticket-status {
  font-size: .62rem; font-weight: 700; text-transform: uppercase;
  border-radius: 999px; padding: 0 .35rem; letter-spacing: .02em;
}
.cv-gticket-status.is-open   { background: var(--st-important-fill); color: var(--st-important-ink); }
.cv-gticket-status.is-closed { background: #e5e7eb; color: #4b5563; }
.cv-gticket-subject { font-weight: 600; font-size: .78rem; flex: 1 1 auto; min-width: 0; }
.cv-gticket-id { font-size: .68rem; color: #64748b; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.cv-gticket-meta { font-size: .66rem; color: #94a3b8; margin-top: .1rem; }
.cv-gticket-quiet { margin: .3rem 0; font-size: .72rem; color: #94a3b8; }
.cv-gticket-error { margin: .3rem 0; font-size: .72rem; color: var(--st-urgent); display: flex; gap: .4rem; align-items: baseline; }
.cv-gticket-action { margin-top: .3rem; font-size: .72rem; }

.cv-gticket-msgs { margin-top: .3rem; display: flex; flex-direction: column; gap: .3rem; }
/* ⚠ NOT bounded any more — `.cv-intg-body` scrolls and resizes for the whole
   section now. Two nested scroll areas made the outer drag look broken, and
   the composer is protected by the outer max-height either way. */
/* ⛔ THE TRANSCRIPT'S COLOURS, so one meaning has one hue across the pane —
   Johan, 2026-08-25. The old sidebar used translucent gold for the agent and
   magenta for the customer, which appear nowhere else in AIM.
   ⚠ RULE ONLY, no fill: these are quoted from another system, and filling them
   like real bubbles would make reference compete with the conversation. */
.cv-gmsg { border-left: 3px solid var(--cv-from-shopper-edge, #c8dcfb); padding: .15rem .45rem; }
.cv-gmsg.is-customer { border-left-color: #c8dcfb; }
.cv-gmsg.is-agent { border-left-color: #3d5a66; }
/* An internal note was never shown to the customer; the dashed edge says so at
   a glance, so nobody replies as if it had been. */
.cv-gmsg.is-private { border-left-style: dashed; background: #fffbea; }
.cv-gmsg-head { display: flex; align-items: baseline; gap: .35rem; font-size: .64rem; color: #94a3b8; flex-wrap: wrap; }
/* Quieter than the absolute time it sits beside — it is the gloss, not the fact. */
.cv-gmsg-ago { color: #b6bfca; }
.cv-gmsg-who { font-weight: 600; color: #64748b; }
.cv-gmsg-private { color: #92400e; font-weight: 700; text-transform: uppercase; font-size: .58rem; }
.cv-gmsg-body { font-size: .74rem; white-space: pre-wrap; word-break: break-word; }

.cv-gticket-form { display: flex; align-items: center; gap: .4rem; margin-top: .3rem; flex-wrap: wrap; }
.cv-gticket-note {
  flex: 1 1 12rem; min-width: 8rem; font-size: .74rem;
  padding: .2rem .4rem; border: 1px solid #cbd5e1; border-radius: .3rem;
}

/* ── Who is around ─────────────────────────────────────────────
 *
 * Below the CBVA picker in the rail — Johan, 2026-08-25. Quiet by design: it
 * is reference consulted before assigning, not a thing to watch. The dots do
 * the scanning; the words are there because colour alone is not a label.
 */
.queue-roster { margin-top: .75rem; padding: .4rem .5rem; }
.queue-rosterhead {
  font-size: .58rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: #94a3b8; margin-bottom: .25rem;
}
.queue-rosterlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .12rem; }
.queue-rosteritem { display: flex; align-items: center; gap: .35rem; min-width: 0; }
.queue-rosterwho { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.queue-rostername {
  font-size: .72rem; min-width: 0; max-width: 100%; text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  border: 0; background: none; padding: 0; font-family: inherit; color: inherit; cursor: pointer;
}
.queue-rostername:hover { text-decoration: underline dotted; }
.queue-rosterdetail {
  display: flex; flex-direction: column; font-size: .62rem; color: #64748b; line-height: 1.25;
  overflow-wrap: anywhere;
}
.queue-rosterstate { font-size: .6rem; color: #94a3b8; text-transform: lowercase; }
/* Name and, after it, the planned hours today from the shift rota (2026-09-08).
   Muted and tabular so seven rows of "09:00–18:00" line up; "off" a shade
   darker because it is somebody's decision, not the absence of one. */
.queue-rosterline { display: flex; align-items: baseline; gap: .35rem; min-width: 0; }
.queue-rostershift {
  font-size: .62rem; color: #94a3b8; white-space: nowrap; flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}
/* Right-justified on the roster line — Johan, 2026-09-09. Tabular digits in
   one column read as a timetable; trailing each name they read as noise. */
.queue-rosterline .queue-rostershift { margin-left: auto; }
.queue-rostershift.is-off { color: #b45309; }
.queue-agentlist .queue-rostershift { margin-left: .25rem; }
/* Only the state that means "can take work now" gets any colour — spending it
   on all four would make none of them mean anything. */
.queue-rosterstate.is-available { color: #15803d; }
/* One line instead of ten — and it says something, because "nobody is around"
   is exactly when a transfer will sit unanswered. */
.queue-rosterempty { font-size: .72rem; color: #94a3b8; font-style: italic; }
/*
 * Grouped by role — Johan, 2026-08-25. Role answers a different question from
 * presence: presence says who COULD take something, role says who it is
 * appropriate to give it to. A supervisor and a junior agent are both
 * "available" and are not interchangeable.
 *
 * The heading is deliberately quieter than the names under it: it is a divider,
 * and a rail this narrow cannot afford two things competing to be read first.
 */
.queue-rostergroup + .queue-rostergroup { margin-top: .35rem; }
.queue-rosterrole {
  font-size: .58rem; text-transform: uppercase; letter-spacing: .04em;
  color: #b6bfca; margin-bottom: .1rem;
}

/* The Gorgias mark on a session card. Muted by default — it is history, not a
   demand — and only takes colour when a ticket is still open. */
/*
 * ⛔ THE LOGO ALONE CARRIES IT — Johan, 2026-08-25: "would be enough to display
 * the Gorgias logo if ticket exists, perhaps green if at least an open ticket
 * and grey if only closed tickets."
 *
 * No count and no label: the card already carries seven facts and an eighth
 * number would be read before it was needed. The mark says "this person has
 * support history"; the colour says whether any of it is live; the pane says
 * the rest.
 *
 * ⚠ Green here is NOT the console's "Replied" green repurposed — it is at 11px
 * beside an email, on a logo shape, nowhere near a bucket pill. It reads as
 * "active", which is what an open ticket is. Grey is history: real, and not
 * asking anything of you.
 *
 * ⚠ It was AMBER for open. That is the "somebody must act" token, and an open
 * Gorgias ticket is context rather than a demand on the AIM agent — spending
 * the act-now colour on it would blunt the buckets that need it.
 */
.qr-gorgias { display: inline-flex; align-items: center; flex: 0 0 auto; }
.qr-gorgias.is-open { color: #15803d; }
.qr-gorgias.is-closed { color: #94a3b8; }

/* ── The visitor's email, as a way in ──────────────────────────
 *
 * ⛔ Reads as plain text until hovered — Johan, 2026-08-25. It is the first
 * thing in the header and mostly there to be READ; a permanently blue,
 * underlined address would make every conversation look like it was asking to
 * be clicked.
 */
.cv-vmenu { position: relative; display: inline-block; min-width: 0; }
/*
 * ⚠ KEEPS `.cv-email`'s OWN SIZE — Johan, 2026-08-25: "should visitor email not
 * be smaller font?". It was .74rem until this became a <button>, which resets
 * to the browser's control font and inherited the header's larger text
 * instead. The address is reference, not a heading, and at full size it
 * competed with the session id above it.
 *
 * ⚠ `.cv-head-email` reserves a fixed 1.05rem so panes align; a bigger font
 * here also silently overflowed that.
 */
.cv-vmenu-trigger {
  font: inherit; font-size: .74rem; color: #4b5563;
  background: none; border: none; padding: 0;
  cursor: pointer; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.cv-vmenu-trigger:hover, .cv-vmenu-trigger.is-open { color: #1d4ed8; text-decoration: underline; }

.cv-vmenu-pop {
  /* Fixed by inline style from the trigger's rect (see VisitorMenu); the
     absolute fallback is only for the first paint before it is measured. */
  position: absolute; z-index: 60; top: calc(100% + .25rem); left: 0;
  /* ⚠ WIDER — Johan, 2026-08-25. The preview quotes the agent's own message
     and lists To/From/Subject; at 17rem the From address wrapped onto three
     lines and pushed the buttons below the fold. */
  min-width: 24rem; max-width: 32rem;
  background: #fff; border: 1px solid #d7dee7; border-radius: .4rem;
  box-shadow: 0 6px 20px rgba(16,19,26,.16); padding: .3rem;
}
.cv-vmenu-item {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: .35rem .45rem; border-radius: .3rem; cursor: pointer;
  font-size: .78rem; color: #1c2530;
}
.cv-vmenu-item:hover:not(:disabled) { background: #eef2f7; }
.cv-vmenu-item:disabled { opacity: .6; cursor: default; }
/* What the action will actually do, under its name — the queue has several
   views and landing in the wrong one reads as a failed click. */
.cv-vmenu-sub { display: block; font-size: .66rem; color: #94a3b8; margin-top: .05rem; }

.cv-vmenu-confirm { padding: .4rem .45rem; }
.cv-vmenu-title { font-size: .74rem; font-weight: 700; margin-bottom: .3rem; }
.cv-vmenu-fields {
  display: grid; grid-template-columns: auto 1fr; gap: .1rem .5rem;
  margin: 0 0 .35rem; font-size: .72rem;
}
.cv-vmenu-fields dt { color: #94a3b8; }
.cv-vmenu-fields dd { margin: 0; overflow-wrap: anywhere; }
.cv-vmenu-label { font-size: .66rem; color: #94a3b8; }
/* The agent's own words, quoted — bounded so a long reply cannot push the
   Send button out of the popup. */
.cv-vmenu-quote {
  margin: .15rem 0 .35rem; padding: .2rem .45rem; border-left: 3px solid #cbd5e1;
  font-size: .72rem; color: #475569; white-space: pre-wrap;
  max-height: 7rem; overflow-y: auto;
}
.cv-vmenu-note { margin: .25rem 0 0; font-size: .66rem; color: #94a3b8; }
/* A second send is allowed but never silent. */
.cv-vmenu-warn { margin: .25rem 0 0; font-size: .68rem; color: #92400e; }
.cv-vmenu-error { margin: .25rem 0 0; font-size: .7rem; color: var(--st-urgent); }
.cv-vmenu-ok { margin: 0; padding: .4rem .45rem; font-size: .74rem; color: #15803d; }
.cv-vmenu-actions { display: flex; align-items: center; gap: .4rem; margin-top: .45rem; flex-wrap: wrap; }
/* The mailto is an <a>; it must not look different from the button beside it. */
.cv-vmenu-actions .cv-btn { text-decoration: none; }

/* The agent's own account of what they sent — see VisitorMenu. */
.cv-vmenu-noteinput {
  width: 100%; margin-top: .3rem; resize: vertical;
  border: 1px solid #cbd5e1; border-radius: .3rem; padding: .3rem .4rem;
  font: inherit; font-size: 16px;   /* no iOS zoom — see .cv-note-input */
}
@media (pointer: fine) { .cv-vmenu-noteinput { font-size: .76rem; } }

/* ── Add-a-note pencil ─────────────────────────────────────────
 *
 * ⛔ NO BOX — Johan, 2026-08-25: "use a smaller cleaner pen, no square!" The
 * bordered version read as a disabled control rather than an invitation. Bare
 * and muted, it reveals itself on hover like every other quiet affordance in
 * the pane.
 *
 * Sits on the date row beside the owner, so it never claims a line of its own
 * in a pane whose vertical space is the transcript's.
 */
.cv-note-icon {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; padding: 0 .15rem; cursor: pointer;
  color: #b6bfca; flex: 0 0 auto;
}
.cv-note-icon:hover { color: #1d4ed8; }

/* A disabled link still has to look like a link that is off, not like text —
   otherwise "Assign" reads as a label and the agent stops looking for it. */
.cv-link:disabled { opacity: .45; cursor: default; text-decoration: none; }
/* ─────────────────────────────────────────────────────────────────────────────
 * ALERTS — the badge, the mark, the summary and the arrival pulse
 *
 * The alert model agreed with Johan, 2026-09-01. See console/alerts.ts for what
 * counts as alerting and console/useAlerts.ts for when things pulse.
 *
 * ⚠ ONE COLOUR FOR "THIS NEEDS YOU", A SECOND ONLY FOR "TOO LONG".
 *   violet #7c3aed — something is waiting for you
 *   red    #dc2626 — it has been waiting too long, and jumps the queue
 * Colour must encode something that changes what the agent does next; the KIND
 * does not, so it lives in the words ("2 transfer requests"), not in a hue.
 *
 * ⛔ THE ROW'S LEFT STRIPE IS NOT PART OF THIS. Johan decided its colours on
 * 2026-08-21 — red wins over violet there because waiting-on-us has no other
 * voice while an owed session already carries its own pill. Re-pointing it at
 * age would overturn a decision with a reason behind it, so the card keeps its
 * stripe and gains only what it was actually missing: the arrival pulse, and a
 * mark for having waited too long.
 * ────────────────────────────────────────────────────────────────────────── */

/* A count, on a container: the overflow menu, the summary above the list. */
.qa-badge {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
  min-width: 1.15rem; height: 1.15rem; padding: 0 .3rem;
  border-radius: 999px;
  background: #7c3aed; color: #fff;
  font-size: .68rem; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.qa-badge.is-overdue { background: #dc2626; }

/*
 * ONE session needs you, where a count of "1" would be noise.
 *
 * ⚠ FILLED vs HALOED carries "too long", not hue alone — the same device
 * .ag-dot uses for presence, and for the same reason: at this size a hue is a
 * guess, a ring is not.
 */
.qa-mark {
  display: inline-block; flex: 0 0 auto;
  width: .45rem; height: .45rem; border-radius: 999px;
  background: #7c3aed;
  vertical-align: baseline;
}
.qa-mark.is-overdue {
  background: #dc2626;
  box-shadow: 0 0 0 2px #fee2e2;
  /* The halo sits outside the dot, so give it room rather than letting it be
     clipped by a neighbour. */
  margin: 0 2px;
}

/* ── The summary above the list — escalation level 4 ─────────────────────
 *
 * ⚠ CLICKABLE, or it is worse than nothing. Telling an agent that something
 * exists without telling them WHERE is the one thing a count at the top must
 * not do; this jumps to the first row that is alerting.
 */
.qa-summary {
  display: flex; align-items: center; gap: .4rem;
  align-self: flex-start;
  margin: 0 0 .35rem;
  padding: .2rem .5rem .2rem .3rem;
  background: #f5f3ff; border: 1px solid #ddd6fe; border-radius: 999px;
  color: #5b21b6; font-size: .72rem; font-weight: 600;
  cursor: pointer;
}
.qa-summary:hover { background: #ede9fe; border-color: #c4b5fd; }
.qa-summary.is-overdue { background: #fef2f2; border-color: #fecaca; color: #991b1b; }
.qa-summary.is-overdue:hover { background: #fee2e2; border-color: #fca5a5; }
.qa-summary-hint { font-weight: 400; opacity: .75; }

/* ── The pulse ───────────────────────────────────────────────────────────
 *
 * ⚠ FLASH THEN PERSIST. Three pulses, then it stops and the badge carries on
 * alone. Anything that flashes for ever becomes wallpaper — people stop seeing
 * it within a day and the alert quietly stops working while appearing to.
 *
 * Rings only: box-shadow costs no layout, so nothing in the list moves while a
 * row announces itself.
 */
@keyframes qa-pulse-ring {
  0%   { box-shadow: 0 0 0 0 rgba(124, 58, 237, .45); }
  70%  { box-shadow: 0 0 0 .5rem rgba(124, 58, 237, 0); }
  100% { box-shadow: 0 0 0 0 rgba(124, 58, 237, 0); }
}
@keyframes qa-pulse-row {
  0%   { box-shadow: inset 0 0 0 1px rgba(124, 58, 237, .55), 0 0 0 0 rgba(124, 58, 237, .35); }
  70%  { box-shadow: inset 0 0 0 1px rgba(124, 58, 237, 0),   0 0 0 .45rem rgba(124, 58, 237, 0); }
  100% { box-shadow: inset 0 0 0 1px rgba(124, 58, 237, 0),   0 0 0 0 rgba(124, 58, 237, 0); }
}
.qa-badge.is-pulsing,
.qa-mark.is-pulsing,
.qa-summary.is-pulsing { animation: qa-pulse-ring .8s ease-out 3; }
.queue-row.is-pulsing  { animation: qa-pulse-row .8s ease-out 3; }

/*
 * ⚠ Motion sensitivity goes straight to the badge. Nothing is lost — the badge
 * carries the whole fact; the pulse only ever said "look here now". The hook
 * skips it too, so this is the belt to that pair of braces.
 */
@media (prefers-reduced-motion: reduce) {
  .qa-badge.is-pulsing,
  .qa-mark.is-pulsing,
  .qa-summary.is-pulsing,
  .queue-row.is-pulsing { animation: none; }
}

/* ── Alert sounds: the panel on the console toolbar ──────────────────────
 *
 * ⚠ Four switches, and that is all of it. Visual alerting has no settings at
 * all — a badge on something you cannot see interrupts nobody — so this panel
 * covers the entire configurable surface of alerting.
 */
.qs-wrap { position: relative; flex: 0 0 auto; }
/* ⚠ THE SAME BUTTON AS REFRESH — Johan, 2026-09-01: "make it same size as
   refresh". They sit side by side on the toolbar, so any difference in height
   or weight reads as a difference in kind. Values copied from .queue-refresh;
   keep the two in step. */
.qs-btn {
  display: inline-flex; align-items: center; gap: .3rem;
  border: 1px solid #d5dae2; background: #fff;
  border-radius: .3rem; padding: .18rem .55rem; font-size: .72rem;
  color: #374151; cursor: pointer; min-height: 24px;
  font-family: inherit;
}
.qs-btn:hover, .qs-btn.is-on { border-color: #97a3b1; background: #f7f9fb; }
/* The one state worth spotting from across the toolbar. */
.qs-btn.is-silent { color: #b45309; border-color: #fcd9a8; background: #fffaf1; }

/*
 * ⛔ ANCHORED LEFT — Johan, 2026-09-01: "make sure alert setup popup is fully
 * displayed". It was `right: 0`, which was right while the button sat at the
 * far right of the toolbar; moved to the LEFT of Refresh, the same rule pushed
 * a 22rem panel off the left edge of the window and cut the labels in half.
 *
 * ⚠ Left-anchored, and the width clamps against the VIEWPORT rather than the
 * toolbar, so it cannot overhang on a narrow window either.
 */
.qs-panel {
  position: absolute; z-index: 40; top: calc(100% + .3rem); left: 0;
  width: 22rem; max-width: min(22rem, calc(100vw - 2rem));
  padding: .7rem .8rem;
  background: #fff; border: 1px solid #d7dde5; border-radius: .45rem;
  box-shadow: 0 12px 28px -12px rgb(17 24 39 / 28%);
  display: flex; flex-direction: column; gap: .1rem;
}
.qs-title {
  margin: 0 0 .45rem; font-size: .66rem; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase; color: #8d97a3;
}
.qs-opt {
  display: flex; align-items: flex-start; gap: .5rem;
  padding: .3rem 0; font-size: .8rem; color: #1f2937; cursor: pointer;
}
.qs-opt.is-disabled { opacity: .5; cursor: default; }
.qs-opt input { margin-top: .18rem; flex: 0 0 auto; }
.qs-opt-text { display: flex; flex-direction: column; gap: .05rem; min-width: 0; }
.qs-opt-hint { font-size: .7rem; color: #6b7280; }
.qs-divider { height: 1px; background: #eceff3; margin: .4rem 0; }
.qs-note {
  margin: .5rem 0 0; padding-top: .45rem; border-top: 1px solid #eceff3;
  font-size: .7rem; color: #6b7280;
}
/* A failed save must SAY so — an agent who believes a sound is off while it is
   not has been told a lie by a control that looked like it worked. */
.qs-error { margin: .45rem 0 0; font-size: .72rem; color: #b91c1c; }

/* ── AIM is not receiving from a solution ────────────────────────────────
 *
 * ⛔ The outage the console used to hide. Every conversation reaches AIM down
 * one upstream socket per solution; when it is not open the queue simply stays
 * quiet, which is indistinguishable from a slow morning. A Hey Harper transfer
 * request was lost that way on 2026-09-01.
 *
 * ⚠ AMBER, not red. Red in this console means a visitor is waiting on us —
 * spending it on infrastructure would put a system fault in the same voice as a
 * person, and the agent has to be able to tell those apart at a glance.
 */
.queue-upstream {
  display: flex; align-items: flex-start; gap: .5rem;
  margin: 0 0 .6rem; padding: .5rem .7rem;
  /* ⚠ NO WIDER THAN THE SESSION LIST — Johan, 2026-09-16: "max the width
     until the right border of the session list". The bar sits above the whole
     body, so it is capped at rail + gap + the list column's widest (11 + 1 +
     34rem, see .queue-views and .queue-main-col); the panes to the right are
     not what it is about. On a narrow window the list is 30rem and the bar
     runs 4rem past it; the alternative was moving it into the DOM under the
     list column, where it would lose the rail's width. */
  max-width: calc(11rem + 1rem + 34rem);
  background: #fffaf1; border: 1px solid #fcd9a8; border-radius: .35rem;
  font-size: .78rem; line-height: 1.4; color: #7c4a03;
}
.queue-upstream strong { color: #6b3f02; }
.queue-upstream-mark {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 1.05rem; height: 1.05rem; margin-top: .05rem;
  border-radius: 999px; background: #b45309; color: #fff;
  font-size: .7rem; font-weight: 700; line-height: 1;
}
/* The agent's own remedy, inline in the warning rather than beside it — it is
   the last clause of the sentence, not a separate control to go and find. */
.queue-upstream-text { min-width: 0; }
.queue-upstream-retry {
  margin-left: .35rem; padding: .1rem .45rem;
  border: 1px solid #e0a458; border-radius: .25rem; background: #fff;
  font: inherit; font-size: .72rem; font-weight: 600; color: #7c4a03;
  cursor: pointer;
}
.queue-upstream-retry:hover:not(:disabled) { background: #fff4e2; border-color: #b45309; }
.queue-upstream-retry:disabled { opacity: .6; cursor: default; }
/* The answer, on its own line: an outcome that shares a line with the warning
   reads as more warning rather than as a reply to what you just pressed. */
.queue-upstream-note { display: block; margin-top: .3rem; font-weight: 600; }

/* ── Today's support hours, on the console toolbar ───────────────────────
 *
 * Standing context, not a control: it sits with the refresh timestamp and is
 * styled like it — quiet, and never competing with the buttons beside it.
 *
 * ⚠ "closed" is amber, not red. Red in this console means a visitor is waiting;
 * a scheduled day off is not a fault and must not wear the same colour.
 */
.queue-hours {
  display: inline-flex; align-items: baseline; gap: .3rem;
  font-size: .7rem; color: #6b7280; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.queue-hours-label { color: #9ca3af; }
.queue-hours.is-closed { color: #b45309; font-weight: 600; }
/* A dated exception, not the weekly pattern — "closed today" on a Tuesday is
   alarming until you know somebody set it deliberately. */
.queue-hours-exc {
  font-size: .6rem; text-transform: uppercase; letter-spacing: .05em;
  padding: .05rem .25rem; border-radius: 2px;
  background: #fef3c7; color: #92400e;
}
/* Never configured — neither open nor closed. Muted rather than amber: it is
   missing setup, not a fault, and it sits on every agent's screen all day for
   any client nobody has got to yet. */
.queue-hours.is-unset { color: #9ca3af; font-style: italic; }

/* ── Am I taking chats — console toolbar ─────────────────────────────────
 *
 * Sized off .queue-refresh beside it, like the Alerts button: three controls
 * on one row that differ in height read as three unrelated things.
 *
 * ⚠ The dot carries the state as well as the word. Green/amber/grey is the
 * same vocabulary the agent roster and the presence dots already use, so a
 * person's own status looks like their status looks to everybody else.
 */
.qa-avail { position: relative; flex: 0 0 auto; }
.qa-avail-btn {
  display: inline-flex; align-items: center; gap: .35rem;
  border: 1px solid #d5dae2; background: #fff;
  border-radius: .3rem; padding: .18rem .55rem; font-size: .72rem;
  color: #374151; cursor: pointer; min-height: 24px; font-family: inherit;
}
.qa-avail-btn:hover, .qa-avail-btn.is-on { border-color: #97a3b1; background: #f7f9fb; }
.qa-avail-caret { font-size: .8rem; line-height: 1; color: #6b7280; }

/* ⛔ NO DOT STYLES HERE. It is `.ag-dot` from AgentDot, the same component the
   roster, the session rows and the ACI use — so an agent's own control cannot
   drift from how everybody else sees them. It was its own set of classes until
   2026-09-02, and two of the three colours were wrong. */

.qa-avail-list {
  position: absolute; z-index: 40; top: calc(100% + .3rem); left: 0;
  width: 17rem; padding: .25rem;
  background: #fff; border: 1px solid #d7dde5; border-radius: .4rem;
  box-shadow: 0 12px 28px -12px rgb(17 24 39 / 28%);
  display: flex; flex-direction: column; gap: .1rem;
}
.qa-avail-item {
  display: flex; align-items: flex-start; gap: .45rem;
  padding: .4rem .45rem; border: 0; background: none; border-radius: .25rem;
  font: inherit; font-size: .78rem; color: #1f2937; text-align: left; cursor: pointer;
}
.qa-avail-item:hover { background: #f3f4f6; }
.qa-avail-item.is-current { background: #eef2f6; font-weight: 600; }
.qa-avail-item .ag-dot { margin-top: .35rem; }
.qa-avail-text { display: flex; flex-direction: column; gap: .05rem; min-width: 0; }
/* Says what a VISITOR is told, not what the state is called. That is the part
   an agent is actually choosing between. */
.qa-avail-hint { font-size: .68rem; font-weight: 400; color: #6b7280; line-height: 1.35; }
/* ── "Is this still right?" on arrival ───────────────────────────────────
 *
 * The status survives a sign-out, so an agent can arrive already claiming to
 * be available. This asks them to look, for ten seconds, once per session.
 *
 * ⚠ AMBER AND A PULSE, NOT RED. Nothing is wrong — the setting may well be
 * correct. Red would say "you have made a mistake" about a question that has
 * not been answered yet.
 */
@keyframes qa-avail-nudge {
  0%   { box-shadow: 0 0 0 0 rgba(245, 158, 11, .5); }
  70%  { box-shadow: 0 0 0 .4rem rgba(245, 158, 11, 0); }
  100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); }
}
.qa-avail-btn.is-confirming {
  border-color: #f59e0b; background: #fffaf1;
  animation: qa-avail-nudge 1.6s ease-out 4;
}
/* Motion sensitivity keeps the amber edge and drops the pulse — the whole
   point survives, since the colour is what carries it. */
@media (prefers-reduced-motion: reduce) {
  .qa-avail-btn.is-confirming { animation: none; }
}

/* ── Agent noticeboard ─────────────────────────────────────────────────────
   What a supervisor needs the team to know. Above the queue, below the outage
   banner — which outranks it, because "chats are not arriving" beats anything
   a person had to say.

   ⛔ A hint borrows the ACTION violet and an instruction the IMPORTANT amber,
   both already in the state palette. Neither takes the urgent red: nothing on
   this board is more pressing than a waiting visitor, and colouring it as if it
   were is how a banner teaches people to ignore red. */

.notices { display: grid; gap: .4rem; margin: 0 0 .6rem; }

.notice {
  display: flex; align-items: flex-start; gap: .6rem;
  padding: .5rem .7rem; border: 1px solid; border-radius: .35rem;
  font-size: .78rem; line-height: 1.45;
  /* Dismissing fades rather than snapping — the click has to show a cause. */
  transition: opacity .12s ease;
}
.notice.is-going { opacity: .35; pointer-events: none; }

.notice--hint {
  background: var(--st-action-tint); border-color: var(--st-action-edge); color: #3b1f78;
}
.notice--instruction {
  background: var(--st-important-tint); border-color: var(--st-important-edge); color: #7c4a03;
}

.notice-tag {
  flex: 0 0 auto; padding: .1rem .4rem; border-radius: 999px;
  font-size: .62rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
}
.notice--hint .notice-tag { background: var(--st-action-fill); color: var(--st-action-ink); }
.notice--instruction .notice-tag { background: var(--st-important-fill); color: var(--st-important-ink); }

.notice-main { flex: 1 1 auto; min-width: 0; }
.notice-body { margin: 0; white-space: pre-wrap; }
.notice-meta { margin: .2rem 0 0; font-size: .68rem; opacity: .75; }

.notice-image {
  display: block; margin: .45rem 0 0; padding: 0;
  background: none; border: 1px solid rgba(0,0,0,.14); border-radius: .3rem;
  cursor: zoom-in; overflow: hidden; line-height: 0;
}
.notice-image img { display: block; max-height: 140px; max-width: 100%; }

.notice-ack {
  flex: 0 0 auto; padding: .22rem .6rem; border-radius: .3rem;
  border: 1px solid var(--st-important-fill);
  background: var(--st-important-fill); color: var(--st-important-ink);
  font-size: .72rem; font-weight: 600; cursor: pointer;
}
.notice-dismiss {
  flex: 0 0 auto; width: 1.3rem; height: 1.3rem; padding: 0;
  border: 0; border-radius: .25rem; background: transparent;
  color: inherit; opacity: .55; cursor: pointer; font-size: .8rem; line-height: 1;
}
.notice-dismiss:hover { opacity: 1; background: rgba(0,0,0,.06); }
.notice-ack:disabled, .notice-dismiss:disabled { cursor: default; }

.notice-lightbox {
  position: fixed; inset: 0; z-index: 1200;
  display: grid; place-items: center; padding: 2rem;
  background: rgba(0,0,0,.62); cursor: zoom-out;
}
.notice-lightbox img { max-width: 100%; max-height: 100%; border-radius: .3rem; }

/* ── the supervisor's panel ─────────────────────────────────────────────── */

.queue-notices {
  padding: .25rem .6rem; border: 1px solid #cbd5e1; border-radius: .3rem;
  background: #fff; font-size: .78rem; cursor: pointer;
  white-space: nowrap; /* the count stays on the word's row at any width */
}
.queue-notices[aria-expanded="true"] { background: #eef2ff; border-color: #a5b4fc; }

.noticeboard {
  margin: 0 0 .6rem; padding: .7rem .8rem;
  background: #fff; border: 1px solid #e2e8f0; border-radius: .4rem;
  box-shadow: 0 8px 24px -14px rgba(15,23,42,.4);
}
.noticeboard-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: .5rem; }
.noticeboard-close {
  border: 0; background: none; cursor: pointer; padding: 0;
  font: inherit; font-size: .78rem; color: #1d4ed8; text-decoration: underline; text-underline-offset: 2px;
}
.noticeboard-close:hover { color: #1e40af; }
/* ⚠ A BUTTON, not a dashed slot — Johan, 2026-09-09: "clearer button for the
   Write a notice. Narrower blue button?" The full-width dashed box read as
   an empty placeholder rather than an act. Same blue as the reply box's
   primary, sized to its words, left where the eye lands after the title. */
.noticeboard-new {
  padding: .3rem .8rem; border: 1px solid #1d4ed8; border-radius: .3rem;
  background: #1d4ed8; color: #fff; font-size: .78rem; font-weight: 600;
  cursor: pointer; width: auto; min-height: 30px;
}
.noticeboard-new:hover { background: #1e40af; border-color: #1e40af; }
.noticeboard-empty { margin: .6rem 0 0; font-size: .75rem; opacity: .65; }
.noticeboard-list { display: grid; gap: .5rem; margin-top: .6rem; }

.posted { padding: .5rem .6rem; border: 1px solid #eef2f7; border-radius: .35rem; background: #fbfcfe; }
.posted-head {
  display: grid; grid-template-columns: minmax(6.4rem, 1.4fr) minmax(7rem, 1fr) auto;
  align-items: center; gap: .4rem; margin-bottom: .25rem;
}
.posted-actions { display: inline-flex; align-items: center; gap: .4rem; justify-self: end; }
.posted-tag {
  padding: .08rem .38rem; border-radius: 999px;
  font-size: .6rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
}
.posted-tag--hint { background: var(--st-action-tint); color: #3b1f78; }
.posted-tag--instruction { background: var(--st-important-tint); color: #7c4a03; }
.posted-expired { font-size: .65rem; opacity: .6; }
.posted-remove { border: 0; background: none; cursor: pointer; opacity: .5; }
.posted-remove:hover { opacity: 1; }
.posted-confirm {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: .68rem; color: #7c4a03;
}
.posted-confirm-yes, .posted-confirm-no {
  border: 1px solid transparent; border-radius: .25rem; padding: .05rem .4rem;
  font: inherit; font-size: .68rem; cursor: pointer;
}
.posted-confirm-yes { background: #b42318; color: #fff; }
.posted-confirm-no { background: none; border-color: #d0d5dd; }
.posted-reopen {
  border: 1px solid #d0d5dd; border-radius: .25rem; background: #fff;
  padding: .05rem .4rem; font: inherit; font-size: .68rem; cursor: pointer;
}
.posted-reopen:hover { border-color: #2563eb; color: #2563eb; }
.posted--history { opacity: .75; }
.noticeboard-history {
  margin-top: .4rem; border: 0; background: none; padding: 0;
  font-size: .72rem; color: #2563eb; text-decoration: underline; cursor: pointer;
}
.posted-body { margin: 0; font-size: .78rem; white-space: pre-wrap; }
.posted-untracked { margin: 0; font-size: .68rem; opacity: .6; }
.posted-readers {
  justify-self: start; padding: 0; border: 0; background: none;
  font-size: .7rem; color: #2563eb; cursor: pointer; text-decoration: underline;
}

/* Unread first — the useful half of the answer to "does the team know". */
.readers { list-style: none; margin: .35rem 0 0; padding: 0; display: grid; gap: .12rem; }
.readers li {
  display: flex; justify-content: space-between; gap: .5rem;
  font-size: .72rem; padding: .1rem .3rem; border-radius: .2rem;
  background: #fff7ea; color: #7c4a03;
}
.readers li.is-read { background: #ecfdf3; color: #15803d; }
.reader-read { display: inline-flex; align-items: baseline; gap: .25rem; font-size: .66rem; color: #15803d; white-space: nowrap; }
.reader-read i { font-style: italic; opacity: .8; }
.reader-names { display: flex; flex-wrap: wrap; gap: 0 .45rem; align-items: baseline; min-width: 0; }
.reader-names b { font-weight: 600; }
.reader-full { opacity: .8; }
.reader-email { opacity: .6; font-size: .66rem; overflow-wrap: anywhere; }
.readers-failed { margin: .35rem 0 0; font-size: .72rem; color: var(--danger, #b42318); }
.readers-retry {
  background: none; border: 0; padding: 0; margin-left: .25rem;
  font: inherit; color: inherit; text-decoration: underline; cursor: pointer;
}

/* ── compose ───────────────────────────────────────────────────────────── */

/* ── A floating window — see FloatingWindow.tsx ─────────────────────────────
   No backdrop: the page underneath stays live (Johan, 2026-09-09). */
.floatwin {
  position: fixed; z-index: 1200;
  max-height: 88vh; display: flex; flex-direction: column;
  background: #fff; border-radius: .5rem;
  border: 1px solid #d7dde6;
  box-shadow: 0 20px 50px rgba(15, 23, 42, .3);
}
.floatwin-head {
  display: flex; align-items: center; gap: .6rem;
  padding: .55rem .8rem; border-bottom: 1px solid #e5e7eb;
  background: #f8fafc; border-radius: .5rem .5rem 0 0;
  cursor: grab; user-select: none; touch-action: none;
}
.floatwin-head:active { cursor: grabbing; }
.floatwin-title { font-size: .9rem; }
.floatwin-aside { font-size: .7rem; opacity: .65; margin-left: auto; }
.floatwin-close { border: 0; background: none; cursor: pointer; opacity: .6; font-size: .85rem; padding: 0 .1rem; }
.floatwin-close:hover { opacity: 1; }
.floatwin-body { overflow: auto; padding: .8rem 1.1rem 1rem; }
.compose-notice { display: grid; gap: .5rem; }
.compose-head { display: flex; align-items: baseline; justify-content: space-between; }
.compose-scope { font-size: .7rem; opacity: .65; }
.compose-field { display: grid; gap: .2rem; }
.compose-row { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.compose-label { font-size: .68rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; opacity: .7; }
.compose-optional { font-weight: 400; text-transform: none; letter-spacing: 0; opacity: .7; }
.compose-kinds { display: flex; gap: .3rem; }
.compose-kinds button {
  padding: .22rem .7rem; border: 1px solid #cbd5e1; border-radius: .3rem;
  background: #fff; font-size: .75rem; cursor: pointer;
}
.compose-kinds button.is-on { background: #1e293b; border-color: #1e293b; color: #fff; }
.compose-body, .compose-alt, .compose-notice input, .compose-notice select {
  width: 100%; padding: .3rem .45rem; border: 1px solid #cbd5e1; border-radius: .3rem;
  font: inherit; font-size: .78rem;
}
.compose-body { resize: vertical; }
.compose-hint { margin: 0; font-size: .68rem; opacity: .7; }
.compose-error-inline { color: #b42318; opacity: 1; }
.compose-people-head { display: flex; align-items: baseline; gap: .6rem; }
.compose-people-toggle {
  border: 0; background: none; padding: 0; font: inherit; font-size: .72rem;
  color: #2563eb; cursor: pointer; text-decoration: underline;
}
.compose-people-list {
  border: 1px solid #e5e7eb; border-radius: .35rem; padding: .4rem .5rem;
  display: grid; gap: .3rem; max-height: 14rem; overflow: auto;
}
.compose-people-filter { font: inherit; font-size: .75rem; padding: .25rem .4rem; }
.compose-people-actions { display: flex; gap: .3rem; }
.compose-people-actions button {
  border: 1px solid #d0d5dd; border-radius: .25rem; background: #fff;
  padding: .05rem .4rem; font: inherit; font-size: .66rem; cursor: pointer;
}
.compose-people-list ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; }
.compose-people-list li label {
  display: flex; align-items: center; justify-content: flex-start; gap: .5rem;
  font-size: .74rem; cursor: pointer; text-align: left;
}
/* ⚠ `.compose-notice input` is width:100% for text fields; a checkbox must not be. */
.compose-people-list li label input[type="checkbox"] { width: auto; flex: 0 0 auto; margin: 0; }
.compose-people-list li label .reader-names { flex: 1 1 auto; justify-content: flex-start; }
.posted-tagcell { display: inline-flex; align-items: center; gap: .35rem; min-width: 0; }
.posted-mid { display: inline-flex; align-items: baseline; gap: .6rem; min-width: 0; }
.posted-replies {
  padding: 0; border: 0; background: none; font-size: .7rem; color: #7c4a03;
  cursor: pointer; text-decoration: underline; white-space: nowrap;
}
.replies { list-style: none; margin: .35rem 0 0; padding: 0; display: grid; gap: .3rem; }
.replies li { padding: .25rem .4rem; border-left: 2px solid #f1c27d; background: #fffaf2; border-radius: .2rem; }
.reply-who { display: flex; flex-wrap: wrap; gap: 0 .45rem; align-items: baseline; font-size: .7rem; }
.reply-at { opacity: .55; font-size: .64rem; }
.reply-body { margin: .1rem 0 0; font-size: .74rem; white-space: pre-wrap; }
.notice-reply-toggle {
  border: 0; background: none; padding: 0; font: inherit; font-size: .68rem;
  color: #2563eb; cursor: pointer; text-decoration: underline;
}
.notice-replied { color: #15803d; font-style: italic; }
.notice-reply { margin-top: .35rem; display: grid; gap: .3rem; max-width: 40rem; }
.notice-reply textarea { font: inherit; font-size: .78rem; padding: .3rem .4rem; resize: vertical; }
.notice-reply-actions { display: flex; justify-content: flex-end; align-items: center; gap: .6rem; }
.notice-reply-failed { font-size: .68rem; color: #b42318; }
.notice-reply-send {
  border: 1px solid #1e293b; border-radius: .25rem; background: #1e293b; color: #fff;
  padding: .15rem .6rem; font: inherit; font-size: .72rem; cursor: pointer;
}
.notice-reply-send:disabled { opacity: .5; cursor: default; }
.posted-to { font-size: .64rem; opacity: .7; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.compose-hint--warn { color: #b45309; opacity: 1; }
.compose-check { display: flex; align-items: center; gap: .4rem; font-size: .75rem; }
.compose-check input { width: auto; }
.compose-preview { max-height: 110px; max-width: 100%; border-radius: .25rem; border: 1px solid #e2e8f0; }
.compose-error { margin: 0; font-size: .72rem; color: #b91c1c; }
.compose-actions { display: flex; justify-content: flex-end; gap: .4rem; }
.compose-post {
  padding: .28rem .8rem; border: 1px solid #1d4ed8; border-radius: .3rem;
  background: #1d4ed8; color: #fff; font-size: .78rem; cursor: pointer;
}
.compose-post:disabled { opacity: .45; cursor: default; }
.compose-cancel {
  padding: .28rem .7rem; border: 1px solid #cbd5e1; border-radius: .3rem;
  background: #fff; font-size: .78rem; cursor: pointer;
}

.cv-event-email { font-style: normal; font-weight: 600; }

/* ⚠ NARROW SCREENS — Johan, 2026-09-09: "make sure it fits in narrow screen
   mode; make the Recent and History buttons / fonts smaller if needed". The
   tab row now carries the Actions dropdown as well; everything on it shrinks
   a step so the row stays one line where it can, and wraps cleanly where it
   cannot. */
@media (max-width: 860px) {
  .queue-tab { font-size: .74rem; padding: .35rem .45rem; }
  .queue-tab-lookup { font-size: .7rem; padding: .3rem .4rem; }
  .queue-lookups { gap: .3rem; }
  .queue-actionsel { font-size: .68rem; margin-left: .5rem; min-height: 26px; }
}

/* The notice board at about half the screen — Johan, 2026-09-12: "difficult
   to read a message that is as wide as the page". Never narrower than a
   readable column, never wider than the page on a small screen. */
.noticeboard { max-width: min(52vw, 980px); min-width: min(100%, 620px); }

/* The pane's top-right: pin and close on one line, Mark Done under them —
   Johan, 2026-09-12. A little air between, so a hand aiming for × misses it. */
.cv-head-side { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: .35rem; }
.cv-head-done .cv-link { font-size: .74rem; }
/* Pin and close pulled up to the head's top edge, Done right under them, all
   inside the frame's height; the column as narrow as "Resume" — Johan,
   2026-09-15: "move pin and close a bit up so the link Done fits just below
   those ... then give more horizontal space to the session info section". */
.cv-head-side { gap: .1rem; margin-top: -.3rem; }
.cv-head-icons { padding-top: 0; gap: 0; }
.cv-head-icons .cv-icon { padding: .1rem .15rem; }
.cv-head-done .cv-link { font-size: .68rem; line-height: 1.1; padding: 0 .1rem; }
.cv-head-identity { gap: .12rem; }
/* The assigned agent's name doubles as the reassign control: dotted, with a chevron. */
/*
 * ⛔ NO `color: inherit` — Johan, 2026-09-21: "should that not be in violet to
 * draw attention?" It should, and it was meant to: the word carries the same
 * `.by-*` class as the list rows so a session cannot be teal in the list and
 * something else in its own header. This rule is more specific than `.by-*`,
 * so `inherit` quietly repainted every owner word the colour of the row —
 * violet for nobody, teal for you, slate for a colleague, all gone.
 *
 * ⚠ The hover no longer recolours it either. A colour that means "nobody has
 * this" must not turn blue because a mouse passed over it; the underline going
 * solid says the same thing about clickability.
 */
.cv-owner .queue-by.is-reassign {
  border: 0; background: none; padding: 0; font: inherit; font-weight: inherit; cursor: pointer;
  text-decoration: underline dotted; text-underline-offset: 2px;
}
.cv-owner .queue-by.is-reassign:hover,
.cv-owner .queue-by.is-reassign:focus-visible { text-decoration: underline solid; }

/* ── The tab row on a narrow shell (Johan, 2026-09-12) ─────────────────────
   One framed group holding the buckets, Actions and Recent / History, with a
   hairline between each section: the three boxes would not fit side by side
   and stacked they read as three unrelated rows. */
.queue-page.is-strip .queue-tabs {
  border: 1px solid #dde2e8; border-radius: .4rem; background: #fff;
  padding: .2rem .25rem; gap: .35rem; flex-wrap: wrap;
}
.queue-page.is-strip .queue-tabs .queue-stategroup { border: 0; background: none; padding: 0; }
.queue-page.is-strip .queue-tabs .queue-actiongroup { order: 1; margin-left: 0; border-left: 1px solid #dde2e8; border-radius: 0; padding-left: .4rem; }
.queue-page.is-strip .queue-tabs .queue-lookups { order: 2; }
.queue-page.is-strip .queue-tabs .queue-tabs-push { order: 3; }
.queue-page.is-strip .queue-tabs .queue-lookups { border-left: 1px solid #dde2e8; padding-left: .4rem; }

/* ── Working on this ──────────────────────────────────────────────────────
 *
 * What is being DONE about a conversation: the agent's own status, the open
 * tasks, and the running notes — Johan, 2026-09-21. Sits under the heads-up
 * and above the transcript.
 *
 * ⚠ OPEN, IT IS ITS OWN GROUND. Johan: "make sure it is clearly separated from
 * the chat further down". A tinted panel and a hard bottom edge, so nobody
 * reads a task as a message.
 */
/*
 * ⛔ THE CONVERSATION KEEPS THE PANE — Johan, 2026-09-21: whether this should
 * open by itself "depends on if we can also see the last couple of chat
 * messages". It does, so the panel is bounded by the PANE rather than by the
 * window: at most 42% of it, whatever the screen, with the transcript taking
 * the rest and staying scrolled to the newest message.
 *
 * ⚠ Percent of the pane, NOT vh. A vh cap is the same number of pixels in a
 * tall window and a short one, so a short pane went to the panel and the last
 * messages went off the bottom — which is the opposite of what it is for.
 */
/*
 * ⚠ min-height 0, NOT min-content. min-content is the WHOLE notes list, and a
 * min-height beats the 42% max-height — ten notes pushed the chat out of the
 * pane. The chat's 4cm floor (.cv-transcript) is what keeps the two apart.
 *
 * ⛔ NO overflow: hidden, and position: relative — the Add action and task
 * popups anchor HERE (see below). Anchored to the pane they opened under its
 * bottom edge, out of sight: "clicking Add action does nothing", Johan,
 * 2026-09-21. Anchored to .wt, which is outside .wt-scroll, the scroll box
 * does not clip them; overflow on .wt itself would.
 */
.wt {
  position: relative; z-index: 6;  /* above the transcript's sticky day pill (5) */
  flex: 0 1 auto; max-height: 42%; min-height: 0;
  display: flex; flex-direction: column;
  border-bottom: 1px solid #d5dbe2; /* same line as under .cv-head */ background: #fff;
}
.wt-head { flex: 0 0 auto; }
/*
 * ⛔ THE TICKET STATUS DOWN THE LEFT EDGE — Johan, 2026-09-21. The pill's own
 * colour family, so the state is seen without reading, open or closed.
 */
.wt { border-left: 3px solid #d1d5db; }
.wt.is-ticket-in_progress { border-left-color: #60a5fa; }
.wt.is-ticket-follow_up { border-left-color: #e0b04a; }
.wt.is-ticket-done { border-left-color: #3fb3a7; }
.wt-icon-ticket { width: .85rem; height: .85rem; color: #6b7280; flex: 0 0 auto; }
.wt.is-open { background: #f7f8fa; border-bottom: 2px solid #d5dae1; box-shadow: inset 0 -6px 10px -10px rgba(16,19,26,.45); }
/* The row: the toggle takes the width, the status control sits at its right. */
.wt-head { display: flex; align-items: center; gap: .4rem; padding: .4rem .6rem; }
.wt-head:hover { background: #f1f3f6; }
.wt-toggle {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; text-align: left; color: #1f2937;
}
.wt-chev { width: .8rem; height: .8rem; color: #9ca3af; flex: 0 0 auto; }
.wt-title { font-size: .78rem; font-weight: 600; }
/* The AGENT's status. Not the session's state — that is the queue's pill. */
/*
 * ⚠ STRONGER THAN A LABEL — Johan, 2026-10-01: "perhaps make the pills a bit
 * stronger colour". These are not decoration: in the menu each one IS the
 * choice, and tints this pale read as a caption beside the sentence explaining
 * them. Deeper fill, a border you can see, and the weight of a control.
 */
.wt-status {
  font-size: .62rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700;
  padding: .05rem .35rem; border-radius: 999px; white-space: nowrap;
  border: 1px solid #cbd5e1; color: #475569; background: #f8fafc;
}
.wt-status.is-in_progress { color: #1746b8; border-color: #93b4f0; background: #dbe8fe; }
.wt-status.is-follow_up { color: #8a5a06; border-color: #dcae4e; background: #fbe9bf; }
.wt-status.is-done { color: #07675e; border-color: #6cc3b8; background: #cfeae5; }
.wt-status.is-none { color: #64748b; border-color: #cbd5e1; background: #f8fafc; }
/* The status is the control — far right of the row, a menu under it. Johan, 2026-09-21. */
.wt-statuswrap { position: relative; flex: 0 0 auto; }
.wt-statuswrap > .wt-status {
  display: inline-flex; align-items: center; gap: .15rem; cursor: pointer; font-family: inherit;
}
.wt-statuswrap > .wt-status:hover:not(:disabled) { filter: brightness(.95); box-shadow: 0 0 0 1px currentColor; }
.wt-statuswrap > .wt-status:disabled { cursor: default; opacity: .7; }
.wt-status-chev { width: .7rem; height: .7rem; }
.wt-statusmenu {
  position: absolute; right: 0; top: calc(100% + .3rem); z-index: 40; width: 16rem;
  display: flex; flex-direction: column; padding: .25rem;
  background: #fff; border: 1.5px solid #64748b; border-radius: .4rem; box-shadow: 0 10px 28px rgba(28,37,48,.28);
}
/*
 * ⛔ THE WHOLE ROW IS THE BUTTON, SO IT HAS TO LOOK LIKE ONE — Johan,
 * 2026-10-01: "now it is not so clear where to click". The rows were plain text
 * on white, and the only thing with an edge was the pill — which is not the
 * target, it is inside it. An agent reading this menu was guessing.
 *
 * ⚠ THE HOVER IS THE ROW'S OWN COLOUR, not one grey for all three. Closing a
 * ticket and picking it back up are different enough acts that the thing lighting
 * up under the pointer should already say which one is about to happen.
 */
.wt-statusopt {
  display: flex; flex-direction: column; align-items: flex-start; gap: .2rem;
  padding: .4rem .45rem; border: 0; border-radius: .3rem; background: none; cursor: pointer;
  font: inherit; text-align: left;
  box-shadow: inset .18rem 0 0 transparent;
  transition: background .12s ease, box-shadow .12s ease;
}
.wt-statusopt:hover:not(:disabled), .wt-statusopt:focus-visible:not(:disabled) { background: #eef2f7; outline: none; }
.wt-statusopt.is-opt-in_progress:hover:not(:disabled),
.wt-statusopt.is-opt-in_progress:focus-visible:not(:disabled) { background: #eaf1fd; box-shadow: inset .18rem 0 0 #2563eb; }
.wt-statusopt.is-opt-follow_up:hover:not(:disabled),
.wt-statusopt.is-opt-follow_up:focus-visible:not(:disabled) { background: #fdf4dd; box-shadow: inset .18rem 0 0 #d08a0d; }
.wt-statusopt.is-opt-done:hover:not(:disabled),
.wt-statusopt.is-opt-done:focus-visible:not(:disabled) { background: #e6f4f2; box-shadow: inset .18rem 0 0 #0b8378; }
/* The pill lifts with the row, so the thing you aimed at answers the pointer. */
.wt-statusopt:hover:not(:disabled) .wt-status,
.wt-statusopt:focus-visible:not(:disabled) .wt-status {
  box-shadow: 0 1px 3px rgba(15, 23, 42, .18); filter: saturate(1.15);
}
/*
 * ⚠ WHERE YOU ALREADY ARE, told apart from where the pointer is. Both used to
 * be the same grey, so the menu could not say which row was the current status
 * and which was merely under the mouse — and the current one is the one click
 * that does nothing.
 */
.wt-statusopt.is-on { background: #f1f5f9; box-shadow: inset .18rem 0 0 #94a3b8; }
.wt-statusopt:disabled { cursor: not-allowed; }
.wt-statusopt:disabled .wt-status { opacity: .5; }
.wt-statusopt-why { font-size: .7rem; color: #6b7280; line-height: 1.3; }
.wt-statusopt-refused { font-size: .68rem; font-style: italic; color: #b45309; line-height: 1.3; }
/* What Done would send the visitor — green when it asks them, grey when it does not. */
.wt-statusopt-survey { display: block; font-size: .66rem; color: #6b7280; font-style: italic; margin-top: .1rem; }
.wt-statusopt-survey.is-on { color: #15803d; font-style: normal; }
.wt-meta { display: inline-flex; align-items: center; gap: .3rem; flex-wrap: wrap; font-size: .72rem; color: #9ca3af; }
/*
 * The task count takes the colour of the most urgent one — Johan, 2026-09-21.
 * Same four states as the pills inside, so the closed row and the open panel
 * say the same thing in the same language.
 */
.wt-count { font-weight: 600; white-space: nowrap; }
.wt-count.is-due-overdue { color: #b4342b; }
.wt-count.is-due-today   { color: #b45309; }
.wt-count.is-due-later   { color: #4338ca; }
.wt-count.is-due-none    { color: #6b7280; }

.wt-body {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
  padding: 0 .6rem .6rem;
}
/*
 * The tasks and the notes scroll; the box you type in and the buttons below it
 * do not (Johan, 2026-09-21). They take what is left of the panel's own share
 * — no fixed height, so a tall pane shows more and a short one still shows the
 * chat.
 */
.wt-scroll { flex: 1 1 auto; min-height: 2.5rem; overflow-y: auto; overscroll-behavior: contain; }
.wt-add, .wt-actions { flex: 0 0 auto; }
/* Stronger — Johan, 2026-09-21: the two section labels read as faint noise. */
.wt-group {
  margin: .5rem 0 .25rem; font-size: .62rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: #4b5563;
}
/* The label carries its own control — "Add action" beside Tasks. */
.wt-group-row { display: flex; align-items: baseline; gap: .5rem; }
/* The popups open just under the panel's header row, over the lists. */
.wt .cv-addaction, .wt .cv-actionpop { top: 2.1rem; }
.wt-empty, .wt-error { margin: .2rem 0; font-size: .76rem; color: #6b7280; }
.wt-error { color: #b91c1c; }

/* The pane's own task control lives in here now — see the panel. */
.wt-tasks { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; }
/* Tasks that do not fit break onto the next row — Johan, 2026-09-21; never a sideways scroll. */
.wt-tasks .cv-pills { flex-wrap: wrap; row-gap: .3rem; column-gap: .5rem; }

/* The gap BETWEEN notes stays larger than the leading inside one, so where a
   note ends is never in doubt. */
.wt-notes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .35rem; }
/* A narrow byline so the note gets the width — Johan, 2026-09-21; a long name ends in … (whole on hover). */
.wt-note { display: grid; grid-template-columns: 4.75rem minmax(0, 1fr); gap: .5rem; align-items: baseline; }
/*
 * Who, then when, on two lines — and the controls sit OVER the name on hover
 * (Johan, 2026-09-21). The column is already there, so two icons cost nothing.
 */
.wt-note-by { position: relative; display: flex; flex-direction: column; line-height: 1.25; }
.wt-note-who { font-size: .68rem; color: #6b7280; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wt-note-at { font-size: .64rem; color: #9ca3af; font-variant-numeric: tabular-nums; }
.wt-note-body { min-width: 0; }
/* ⚠ Tight leading — Johan, 2026-09-21: "less space between rows in a note".
   A note is two or three wrapped lines of one thought, so it should read as a
   block; the pane's own airier line-height is for a conversation. */
.wt-note-text { font-size: .78rem; line-height: 1.25; color: #1f2937; }
/* Deleted, not gone: the row keeps its place, its author and its time. */
.wt-note.is-void .wt-note-text { font-style: italic; color: #9ca3af; }
.wt-note-edited { font-size: .6rem; white-space: nowrap; }
/* Over the name, on the panel's own ground so it covers it cleanly. */
.wt-note-acts {
  position: absolute; left: 0; top: 0;
  display: inline-flex; align-items: center; gap: .15rem;
  background: #f7f8fa; padding-right: .3rem;
  opacity: 0; pointer-events: none; transition: opacity .12s ease;
}
.wt-note:hover .wt-note-acts, .wt-note:focus-within .wt-note-acts { opacity: 1; pointer-events: auto; }
.wt-icon {
  border: 0; background: none; padding: .05rem; cursor: pointer; line-height: 0;
  color: #9ca3af; border-radius: .25rem;
}
.wt-icon svg { width: .72rem; height: .72rem; }
.wt-icon:hover, .wt-icon:focus-visible { color: #2563eb; background: #eef2f7; }
.wt-icon.is-danger { color: #b4342b; }
.wt-icon.is-danger:hover, .wt-icon.is-danger:focus-visible { color: #fff; background: #b4342b; }
/* Deleting asks first: the words do not come back. */
.wt-note-ask { font-size: .62rem; color: #b4342b; white-space: nowrap; }
.wt-note-edit { display: flex; flex-direction: column; gap: .3rem; }
.wt-note-editacts { display: flex; justify-content: flex-end; align-items: center; gap: .5rem; }
.wt-note-edit textarea {
  width: 100%; resize: vertical; font: inherit; font-size: .78rem; line-height: 1.25;
  padding: .25rem .4rem; border: 1px solid #d1d5db; border-radius: .3rem; background: #fff;
}
.wt-add input {
  flex: 1 1 auto; min-width: 0; font: inherit; font-size: .78rem;
  padding: .25rem .4rem; border: 1px solid #d1d5db; border-radius: .3rem; background: #fff;
}
.wt-add { display: flex; gap: .35rem; margin-top: .45rem; }
.wt-btn {
  border: 1px solid transparent; border-radius: .3rem; cursor: pointer;
  font: inherit; font-size: .72rem; font-weight: 500;
  padding: .2rem .5rem; background: #2563eb; color: #fff; white-space: nowrap;
}
.wt-btn:disabled { opacity: .45; cursor: default; }
.wt-btn-quiet { background: #fff; color: #374151; border-color: #d1d5db; }
.wt-link {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; font-size: .68rem; color: #2563eb; text-decoration: underline; text-underline-offset: 2px;
}

/* The one-line offer after replying on follow-up — see the pane. Asked once. */
.cv-backoffer {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin: 0 .6rem .4rem; padding: .35rem .5rem;
  font-size: .76rem; color: #1f2937;
  background: #eaf1fd; border: 1px solid #bfd3f5; border-radius: .35rem;
}
.cv-backoffer-choices { display: inline-flex; align-items: center; gap: .4rem; margin-left: auto; }
.cv-backoffer-choices .cv-backoffer-yes { margin-left: 0; }
.cv-backoffer-yes:disabled { opacity: .45; cursor: not-allowed; }
.cv-backoffer-yes {
  border: 1px solid transparent; border-radius: .3rem; cursor: pointer;
  font: inherit; font-size: .72rem; font-weight: 500;
  padding: .15rem .5rem; background: #2563eb; color: #fff; margin-left: auto;
}

/* A template answer rendered — the list and the picker (TemplateAnswerHtml.tsx). */
.tpl-html p { margin: 0 0 .2rem; }
.tpl-html ul, .tpl-html ol { margin: 0 0 .2rem; padding-left: 1.1rem; }
.tpl-html ul { list-style: disc; }
.tpl-html ol { list-style: decimal; }
.tpl-html li p { margin: 0; }
.tpl-html a { color: #2563eb; text-decoration: underline; }
.tpl-html-bubble { margin: .25rem 0; text-align: center; font-size: 9px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #b45309; border-top: 1px dashed #f59e0b; line-height: 1.2; padding-top: .1rem; }

/* The answer cell in the template answers list — AnswerCell.tsx. Four lines,
   about the height the Updated/Created column gives every row. */
.ta-answer { display: flex; flex-direction: column; min-height: 100%; }
.ta-answer-text { max-height: 5.2rem; overflow: hidden; font-size: 11px; line-height: 1.3rem; color: var(--foreground, #1e293b); cursor: pointer; }
.ta-answer-text.is-open { max-height: none; }
.ta-answer-more { align-self: flex-start; margin-top: .15rem; border: 0; background: none; padding: 0; cursor: pointer; font: inherit; font-size: 11px; color: #2563eb; text-decoration: underline; text-underline-offset: 2px; }
.tpl-html-field { display: inline-block; border-radius: .2rem; padding: 0 .3rem; background: #fef3c7; color: #92400e; box-shadow: inset 0 0 0 1px #fcd34d; font-size: .95em; line-height: 1.35; }

/*
 * THE TEMPLATE CARD — Johan, 2026-09-22 (TemplateCardView.tsx). Full width of
 * the pane, in place of the reply box: one light panel per bubble, NOT drawn
 * like a sent chat bubble; agent fields amber until filled; Send at the foot.
 */
.tcard { flex: 1 1 auto; width: 100%; display: flex; flex-direction: column; gap: .4rem; min-width: 0; }
.tcard-head { display: flex; align-items: baseline; gap: .5rem; }
.tcard-title { font-size: .78rem; font-weight: 600; color: #1f2937; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tcard-count { font-size: .7rem; color: #6b7280; white-space: nowrap; }
/*
 * How many placeholders, and how many are left — Johan, 2026-10-01. The count is
 * background and stays grey; what remains is the job, so it is orange, bold, and
 * a button that takes the cursor there. Green once none remain: then the count
 * is the good news rather than a fact.
 */
.tcard-progress { display: inline-flex; align-items: baseline; gap: .35rem; white-space: nowrap; }
.tcard-count { font-size: .7rem; color: #6b7280; }
.tcard-count.is-done { color: #15803d; }
.tcard-left { border: 0; background: none; padding: 0; font: inherit; font-size: .7rem; font-weight: 700; color: #c2410c; cursor: pointer; }
.tcard-left:hover { text-decoration: underline; text-underline-offset: 2px; }
/*
 * While editing: Split here, Revert and Apply together, with Cancel template
 * kept apart at the end — Johan, 2026-10-01. Apply is the way back to a sendable
 * answer, so it is the only filled button on the row; the other two are links
 * because they are the rare paths.
 */
.tcard-editrow { margin-left: auto; display: inline-flex; align-items: center; gap: .9rem; }
.tcard-apply {
  display: inline-flex; align-items: center; gap: .25rem;
  border: 0; background: #1d4ed8; color: #fff; font: inherit; font-weight: 600; font-size: .75rem;
  border-radius: .3rem; padding: .22rem .75rem; cursor: pointer;
}
.tcard-apply:hover:not(:disabled) { background: #1e40af; }
.tcard-apply:disabled { opacity: .6; cursor: not-allowed; }
.tcard-apply svg { width: .8rem; height: .8rem; }
/* ⚠ After the edit row, not pushed by it — two `margin-left: auto` would fight. */
.tcard-editrow + .tcard-cancel { margin-left: 1.5rem; }
.tcard-cancel { margin-left: auto; border: 0; background: none; padding: 0; cursor: pointer; font: inherit; font-size: .72rem; color: #2563eb; text-decoration: underline; text-underline-offset: 2px; white-space: nowrap; }
/*
 * ⛔ ONE ANSWER, NOT A STACK OF PANELS — Johan, 2026-10-01. The split is ours,
 * not the agent's: it decides how the visitor receives this, and the agent is
 * filling fields in and adjusting a sentence. So one light block, with the
 * breaks drawn as faint lines inside it.
 */
.tcard-answer {
  border: 1px solid #e2e8f0; border-left: 3px solid #94a3b8; border-radius: .3rem;
  background: #f8fafc; padding: .4rem .55rem; max-height: 18rem; overflow-y: auto;
  font-size: .82rem; line-height: 1.45; color: #1f2937;
}
/* Where one message ends. Worth seeing, not worth managing. */
.tcard-split { border-top: 1px dashed #cbd5e1; margin: .45rem 0; }
.tcard-edit { display: inline-flex; align-items: center; gap: .2rem; border: 0; background: none; padding: 0; cursor: pointer; font: inherit; font-size: .72rem; color: #2563eb; white-space: nowrap; }
.tcard-edit:hover { text-decoration: underline; text-underline-offset: 2px; }
.tcard-edit svg { width: .75rem; height: .75rem; }
.tcard-edit:disabled { opacity: .5; cursor: not-allowed; }
/* Editing the whole answer: the same ground, with the editor in it. */
.tcard-editor { display: flex; flex-direction: column; gap: .3rem; }
.tcard-editbody .ProseMirror {
  border: 1px solid #cbd5e1; border-radius: .3rem; background: #fff;
  padding: .4rem .55rem; min-height: 6rem; max-height: 18rem; overflow-y: auto;
  font-size: .82rem; line-height: 1.45; color: #1f2937; outline: none;
}
.tcard-editbody .ProseMirror p { margin: 0 0 .25rem; }
.tcard-editbody .ProseMirror p:last-child { margin-bottom: 0; }
/* The atom, in both editors: a line that says what it is and cannot be typed into. */
.tcard-editbody .NewResponseBubble {
  display: block; margin: .4rem 0; border-top: 1px dashed #cbd5e1;
  font-size: .6rem; letter-spacing: .06em; text-transform: uppercase; color: #94a3b8;
  text-align: center; user-select: none; padding-top: .15rem;
}
.tcard-text p { margin: 0 0 .15rem; }
.tcard-text p:last-child { margin-bottom: 0; }
.tcard-acts { position: absolute; top: .25rem; right: .25rem; display: inline-flex; gap: .15rem; }
.tcard-bubble.is-editing { padding-right: .55rem; }
/* Under the text box while editing: words, not bare icons (Johan, 2026-09-22). */
.tcard-editacts { display: flex; justify-content: flex-end; gap: .9rem; margin-top: .3rem; }
.tcard-editacts button { display: inline-flex; align-items: center; gap: .2rem; border: 0; background: none; padding: 0; cursor: pointer; font: inherit; font-size: .74rem; color: #2563eb; }
.tcard-editacts button svg { width: .8rem; height: .8rem; }
.tcard-editacts button:hover { text-decoration: underline; text-underline-offset: 2px; }
.tcard-editacts button.is-remove { color: #dc2626; opacity: .75; }
.tcard-editacts button.is-remove:hover { opacity: 1; }
/* An edited bubble, closed: its text as it will go. */
.tcard-plain { white-space: pre-wrap; }
.tcard-acts button { border: 0; background: none; padding: .15rem; border-radius: .25rem; color: #64748b; cursor: pointer; line-height: 0; }
.tcard-acts button:hover { color: #1f2937; background: #e2e8f0; }
.tcard-acts svg { width: .75rem; height: .75rem; } /* the header's heads-up pencil size (AgentNote) */
.tcard-field { font: inherit; font-size: .8rem; padding: 0 .3rem; margin: 0 .05rem; border: 1px solid #f59e0b; border-radius: .25rem; background: #fffbeb; color: #1f2937; line-height: 1.35; max-width: 100%; }
.tcard-field::placeholder { color: #b45309; }
/* ⛔ OBVIOUS WHERE THE CURSOR IS — Johan, 2026-10-01: the card now puts it in
   the first empty placeholder, which only helps if the agent can see that. */
.tcard-field:focus { outline: none; border-color: #d97706; box-shadow: 0 0 0 3px rgba(245,158,11,.35); background: #fff; }
/*
 * ⛔ FILLED IS GREEN, NOT GREY — Johan, 2026-10-01: "a filled in template shall
 * have stronger ux than the grey frame... perhaps some green stuff".
 *
 * Grey said "ordinary"; what the agent wants to see at a glance is which ones
 * are DONE. One language across the card now: amber is outstanding, green is
 * done — the same green the header count turns when none remain.
 */
.tcard-field.is-filled { border-color: #16a34a; background: #f0fdf4; color: #14532d; }
.tcard-field.is-filled:focus { border-color: #15803d; box-shadow: 0 0 0 3px rgba(22,163,74,.3); }

/*
 * A placeholder while the answer is being edited — Johan, 2026-10-01: "chip
 * should show the value! (but UX highlighted so it is easy for agent to see
 * where he filled in the placeholders)". So the chip reads the VALUE and stays
 * amber: the sentence is the one the visitor will read, and the spots the agent
 * filled are still obvious at a glance.
 */
.tcard-chip {
  display: inline-flex; align-items: baseline; gap: .15rem;
  border: 1px solid #f59e0b; border-radius: .25rem; background: #fffbeb;
  padding: 0 .15rem 0 .3rem; margin: 0 .05rem; color: #92400e; white-space: nowrap;
}
/* ⚠ The same green as the card's filled boxes: one language in both modes. */
.tcard-chip.is-filled { color: #14532d; background: #f0fdf4; border-color: #16a34a; }
.tcard-chip-x {
  border: 0; background: none; padding: 0 .1rem; cursor: pointer; line-height: 1;
  font: inherit; font-size: .9em; color: #b45309; opacity: .65;
}
.tcard-chip-x:hover { opacity: 1; }
/* Typed into while editing, so the sentence can be read and filled in one place. */
.tcard-chip-in {
  font: inherit; font-size: inherit; color: inherit; background: transparent;
  border: 0; outline: none; padding: 0; min-width: 2rem;
}
.tcard-chip-in::placeholder { color: #b45309; }
.tcard-chip:focus-within { box-shadow: 0 0 0 3px rgba(245,158,11,.35); }
.tcard-chip.is-filled:focus-within { box-shadow: 0 0 0 3px rgba(22,163,74,.3); }
.tcard-wrap { flex: 1 1 auto; min-width: 0; display: flex; }
.tcard-foot { display: flex; flex-direction: column; align-items: stretch; gap: .35rem; }
/* Fields still empty: Send looks a step back, but stays clickable — it jumps to the first one. */
.tcard-foot > .cv-btn-send[aria-disabled="true"] { opacity: .6; }
/* Send on its own row, under the hint — Johan, 2026-09-22: a long label never squeezes the hint. */
.tcard-foot > .cv-btn-send { align-self: flex-end; }

/* ── The `?` beside a heading, and the text behind it ───────────────────────
   The TITLE sits on the page next to the mark, so a reader knows whether it is
   worth opening before they open it (Johan, 2026-09-30). */
.helptip { position: relative; display: inline-flex; align-items: center; gap: .3rem; }
.helptip-title { font-size: .72rem; color: #6b7280; }
.helptip-btn {
  width: 1.05rem; height: 1.05rem; line-height: 1; border-radius: 999px;
  border: 1px solid #c7d2e0; background: #fff; color: #4b5563;
  font-size: .7rem; font-weight: 700; cursor: pointer; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.helptip-btn:hover, .helptip-btn.is-on { background: #1d4ed8; border-color: #1d4ed8; color: #fff; }
.helptip-pop {
  position: absolute; z-index: 40; top: calc(100% + .35rem); left: 0; width: 26rem; max-width: 80vw;
  display: flex; flex-direction: column; gap: .4rem;
  background: #fff; border: 1px solid #e5e7eb; border-radius: .4rem;
  box-shadow: 0 10px 24px rgba(28,37,48,.16); padding: .6rem .7rem;
  font-size: .78rem; color: #1f2937; text-align: left; white-space: normal;
}
.helptip-pop-head { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; }
.helptip-pop-title { font-weight: 600; }
.helptip-edit, .helptip-cancel {
  border: 0; background: none; color: #1d4ed8; font: inherit; font-size: .72rem; cursor: pointer; padding: 0;
}
.helptip-edit:hover, .helptip-cancel:hover { text-decoration: underline; }
.helptip-body { line-height: 1.45; }
.helptip-body p { margin: 0 0 .4rem; }
.helptip-body em { color: #6b7280; }
.helptip-input, .helptip-body-edit {
  font: inherit; font-size: .78rem; width: 100%; padding: .3rem .4rem;
  border: 1px solid #d7dee7; border-radius: .3rem; background: #fff;
}
.helptip-body-edit { resize: vertical; line-height: 1.45; }
.helptip-actions { display: flex; align-items: center; gap: .6rem; }
.helptip-save {
  border: 0; background: #1d4ed8; color: #fff; font: inherit; font-weight: 600; font-size: .74rem;
  border-radius: .3rem; padding: .2rem .7rem; cursor: pointer;
}
.helptip-save:disabled { opacity: .6; cursor: not-allowed; }
.helptip-error { font-size: .72rem; color: #b91c1c; }
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties {
  @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
    *, :before, :after, ::backdrop {
      --tw-border-spacing-x: 0;
      --tw-border-spacing-y: 0;
      --tw-translate-x: 0;
      --tw-translate-y: 0;
      --tw-translate-z: 0;
      --tw-space-y-reverse: 0;
      --tw-space-x-reverse: 0;
      --tw-border-style: solid;
      --tw-leading: initial;
      --tw-font-weight: initial;
      --tw-tracking: initial;
      --tw-ordinal: initial;
      --tw-slashed-zero: initial;
      --tw-numeric-figure: initial;
      --tw-numeric-spacing: initial;
      --tw-numeric-fraction: initial;
      --tw-shadow: 0 0 #0000;
      --tw-shadow-color: initial;
      --tw-shadow-alpha: 100%;
      --tw-inset-shadow: 0 0 #0000;
      --tw-inset-shadow-color: initial;
      --tw-inset-shadow-alpha: 100%;
      --tw-ring-color: initial;
      --tw-ring-shadow: 0 0 #0000;
      --tw-inset-ring-color: initial;
      --tw-inset-ring-shadow: 0 0 #0000;
      --tw-ring-inset: initial;
      --tw-ring-offset-width: 0px;
      --tw-ring-offset-color: #fff;
      --tw-ring-offset-shadow: 0 0 #0000;
      --tw-duration: initial;
    }
  }
}

@layer theme {
  :root, :host {
    --tw-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
    monospace;
    --tw-color-red-50: oklch(97.1% .013 17.38);
    --tw-color-red-300: oklch(80.8% .114 19.571);
    --tw-color-red-400: oklch(70.4% .191 22.216);
    --tw-color-red-500: oklch(63.7% .237 25.331);
    --tw-color-red-600: oklch(57.7% .245 27.325);
    --tw-color-red-700: oklch(50.5% .213 27.518);
    --tw-color-red-800: oklch(44.4% .177 26.899);
    --tw-color-red-950: oklch(25.8% .092 26.042);
    --tw-color-orange-100: oklch(95.4% .038 75.164);
    --tw-color-orange-800: oklch(47% .157 37.304);
    --tw-color-amber-50: oklch(98.7% .022 95.277);
    --tw-color-amber-100: oklch(96.2% .059 95.617);
    --tw-color-amber-300: oklch(87.9% .169 91.605);
    --tw-color-amber-400: oklch(82.8% .189 84.429);
    --tw-color-amber-500: oklch(76.9% .188 70.08);
    --tw-color-amber-700: oklch(55.5% .163 48.998);
    --tw-color-amber-800: oklch(47.3% .137 46.201);
    --tw-color-amber-900: oklch(41.4% .112 45.904);
    --tw-color-amber-950: oklch(27.9% .077 45.635);
    --tw-color-yellow-50: oklch(98.7% .026 102.212);
    --tw-color-yellow-100: oklch(97.3% .071 103.193);
    --tw-color-yellow-400: oklch(85.2% .199 91.936);
    --tw-color-green-50: oklch(98.2% .018 155.826);
    --tw-color-green-600: oklch(62.7% .194 149.214);
    --tw-color-green-800: oklch(44.8% .119 151.328);
    --tw-color-emerald-50: oklch(97.9% .021 166.113);
    --tw-color-emerald-100: oklch(95% .052 163.051);
    --tw-color-emerald-300: oklch(84.5% .143 164.978);
    --tw-color-emerald-500: oklch(69.6% .17 162.48);
    --tw-color-emerald-600: oklch(59.6% .145 163.225);
    --tw-color-emerald-700: oklch(50.8% .118 165.612);
    --tw-color-emerald-800: oklch(43.2% .095 166.913);
    --tw-color-emerald-900: oklch(37.8% .077 168.94);
    --tw-color-sky-100: oklch(95.1% .026 236.824);
    --tw-color-sky-800: oklch(44.3% .11 240.79);
    --tw-color-blue-50: oklch(97% .014 254.604);
    --tw-color-blue-200: oklch(88.2% .059 254.128);
    --tw-color-blue-400: oklch(70.7% .165 254.624);
    --tw-color-blue-500: oklch(62.3% .214 259.815);
    --tw-color-blue-600: oklch(54.6% .245 262.881);
    --tw-color-blue-700: oklch(48.8% .243 264.376);
    --tw-color-blue-800: oklch(42.4% .199 265.638);
    --tw-color-blue-900: oklch(37.9% .146 265.522);
    --tw-color-blue-950: oklch(28.2% .091 267.935);
    --tw-color-violet-50: oklch(96.9% .016 293.756);
    --tw-color-violet-100: oklch(94.3% .029 294.588);
    --tw-color-violet-300: oklch(81.1% .111 293.571);
    --tw-color-violet-800: oklch(43.2% .232 292.759);
    --tw-color-violet-900: oklch(38% .189 293.745);
    --tw-color-rose-600: oklch(58.6% .253 17.585);
    --tw-color-gray-400: oklch(70.7% .022 261.325);
    --tw-color-gray-600: oklch(44.6% .03 256.802);
    --tw-color-gray-700: oklch(37.3% .034 259.733);
    --tw-color-neutral-200: oklch(92.2% 0 none);
    --tw-color-neutral-800: oklch(26.9% 0 none);
    --tw-color-black: #000;
    --tw-color-white: #fff;
    --tw-spacing: .25rem;
    --tw-container-lg: 32rem;
    --tw-text-xs: .75rem;
    --tw-text-xs--line-height: calc(1 / .75);
    --tw-text-sm: .875rem;
    --tw-text-sm--line-height: calc(1.25 / .875);
    --tw-text-base: 1rem;
    --tw-text-base--line-height: calc(1.5 / 1);
    --tw-text-lg: 1.125rem;
    --tw-text-lg--line-height: calc(1.75 / 1.125);
    --tw-font-weight-normal: 400;
    --tw-font-weight-medium: 500;
    --tw-font-weight-semibold: 600;
    --tw-font-weight-bold: 700;
    --tw-tracking-tight: -.025em;
    --tw-tracking-normal: 0em;
    --tw-tracking-wide: .025em;
    --tw-tracking-wider: .05em;
    --tw-tracking-widest: .1em;
    --tw-leading-tight: 1.25;
    --tw-leading-snug: 1.375;
    --tw-animate-spin: spin 1s linear infinite;
    --tw-animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;
    --tw-default-transition-duration: .15s;
    --tw-default-transition-timing-function: cubic-bezier(.4, 0, .2, 1);
  }
}

@layer base {
  .tw-root, .tw-root *, .tw-root :before, .tw-root :after {
    box-sizing: border-box;
    border-style: solid;
    border-width: 0;
    border-color: var(--border);
  }

  .tw-root button, .tw-root input, .tw-root optgroup, .tw-root select, .tw-root textarea {
    font: inherit;
    color: inherit;
    appearance: none;
    opacity: 1;
    background-color: #0000;
    border-radius: 0;
    margin: 0;
    padding: 0;
  }

  .tw-root ::file-selector-button {
    font: inherit;
    color: inherit;
    appearance: none;
    opacity: 1;
    background-color: #0000;
    border-radius: 0;
    margin: 0;
    padding: 0;
  }

  .tw-root input[type="checkbox"], .tw-root input[type="radio"] {
    appearance: auto;
  }

  .tw-root button, .tw-root [role="button"] {
    cursor: pointer;
  }

  .tw-root button:disabled, .tw-root [role="button"]:disabled {
    cursor: default;
  }

  .tw-root a {
    color: inherit;
    -webkit-text-decoration: inherit;
    -webkit-text-decoration: inherit;
    text-decoration: inherit;
  }

  .tw-root table {
    border-collapse: collapse;
    border-color: inherit;
    text-indent: 0;
  }

  .tw-root img, .tw-root svg, .tw-root video, .tw-root canvas {
    vertical-align: middle;
    display: block;
  }

  .tw-root ::placeholder {
    opacity: 1;
    color: currentColor;
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw-root ::placeholder {
      color: color-mix(in oklab, currentcolor 50%, transparent);
    }
  }

  .tw-root {
    background: var(--background);
    color: var(--foreground);
  }
}

@layer components;

@layer utilities {
  .tw\:pointer-events-auto {
    pointer-events: auto;
  }

  .tw\:pointer-events-none {
    pointer-events: none;
  }

  .tw\:sr-only {
    clip-path: inset(50%);
    white-space: nowrap;
    border-width: 0;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    position: absolute;
    overflow: hidden;
  }

  .tw\:absolute {
    position: absolute;
  }

  .tw\:fixed {
    position: fixed;
  }

  .tw\:relative {
    position: relative;
  }

  .tw\:sticky {
    position: sticky;
  }

  .tw\:inset-0 {
    inset: 0;
  }

  .tw\:inset-x-0 {
    inset-inline: 0;
  }

  .tw\:top-0 {
    top: 0;
  }

  .tw\:top-1 {
    top: var(--tw-spacing);
  }

  .tw\:top-4 {
    top: calc(var(--tw-spacing) * 4);
  }

  .tw\:top-\[41px\] {
    top: 41px;
  }

  .tw\:top-\[50\%\] {
    top: 50%;
  }

  .tw\:top-full {
    top: 100%;
  }

  .tw\:right-0 {
    right: 0;
  }

  .tw\:right-1 {
    right: var(--tw-spacing);
  }

  .tw\:right-2 {
    right: calc(var(--tw-spacing) * 2);
  }

  .tw\:right-4 {
    right: calc(var(--tw-spacing) * 4);
  }

  .tw\:bottom-0 {
    bottom: 0;
  }

  .tw\:left-0 {
    left: 0;
  }

  .tw\:left-1 {
    left: var(--tw-spacing);
  }

  .tw\:left-2 {
    left: calc(var(--tw-spacing) * 2);
  }

  .tw\:left-\[50\%\] {
    left: 50%;
  }

  .tw\:z-10 {
    z-index: 10;
  }

  .tw\:z-20 {
    z-index: 20;
  }

  .tw\:z-30 {
    z-index: 30;
  }

  .tw\:z-50 {
    z-index: 50;
  }

  .tw\:z-\[5\] {
    z-index: 5;
  }

  .tw\:col-span-3 {
    grid-column: span 3 / span 3;
  }

  .tw\:-mx-1 {
    margin-inline: calc(var(--tw-spacing) * -1);
  }

  .tw\:mx-1 {
    margin-inline: var(--tw-spacing);
  }

  .tw\:mx-1\.5 {
    margin-inline: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:mx-auto {
    margin-inline: auto;
  }

  .tw\:my-1 {
    margin-block: var(--tw-spacing);
  }

  .tw\:mt-0\.5 {
    margin-top: calc(var(--tw-spacing) * .5);
  }

  .tw\:mt-1 {
    margin-top: var(--tw-spacing);
  }

  .tw\:mt-1\.5 {
    margin-top: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:mt-2 {
    margin-top: calc(var(--tw-spacing) * 2);
  }

  .tw\:mt-3 {
    margin-top: calc(var(--tw-spacing) * 3);
  }

  .tw\:mt-4 {
    margin-top: calc(var(--tw-spacing) * 4);
  }

  .tw\:mt-6 {
    margin-top: calc(var(--tw-spacing) * 6);
  }

  .tw\:-mr-1 {
    margin-right: calc(var(--tw-spacing) * -1);
  }

  .tw\:mr-1 {
    margin-right: var(--tw-spacing);
  }

  .tw\:mr-1\.5 {
    margin-right: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:mr-2 {
    margin-right: calc(var(--tw-spacing) * 2);
  }

  .tw\:mb-0 {
    margin-bottom: 0;
  }

  .tw\:mb-0\.5 {
    margin-bottom: calc(var(--tw-spacing) * .5);
  }

  .tw\:mb-1 {
    margin-bottom: var(--tw-spacing);
  }

  .tw\:mb-1\.5 {
    margin-bottom: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:mb-2 {
    margin-bottom: calc(var(--tw-spacing) * 2);
  }

  .tw\:mb-3 {
    margin-bottom: calc(var(--tw-spacing) * 3);
  }

  .tw\:-ml-1 {
    margin-left: calc(var(--tw-spacing) * -1);
  }

  .tw\:-ml-3 {
    margin-left: calc(var(--tw-spacing) * -3);
  }

  .tw\:ml-0\.5 {
    margin-left: calc(var(--tw-spacing) * .5);
  }

  .tw\:ml-1 {
    margin-left: var(--tw-spacing);
  }

  .tw\:ml-1\.5 {
    margin-left: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:ml-2 {
    margin-left: calc(var(--tw-spacing) * 2);
  }

  .tw\:ml-6 {
    margin-left: calc(var(--tw-spacing) * 6);
  }

  .tw\:ml-auto {
    margin-left: auto;
  }

  .tw\:block {
    display: block;
  }

  .tw\:contents {
    display: contents;
  }

  .tw\:flex {
    display: flex;
  }

  .tw\:grid {
    display: grid;
  }

  .tw\:hidden {
    display: none;
  }

  .tw\:inline-block {
    display: inline-block;
  }

  .tw\:inline-flex {
    display: inline-flex;
  }

  .tw\:h-0 {
    height: 0;
  }

  .tw\:h-1 {
    height: var(--tw-spacing);
  }

  .tw\:h-1\.5 {
    height: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:h-2 {
    height: calc(var(--tw-spacing) * 2);
  }

  .tw\:h-2\.5 {
    height: calc(var(--tw-spacing) * 2.5);
  }

  .tw\:h-3 {
    height: calc(var(--tw-spacing) * 3);
  }

  .tw\:h-3\.5 {
    height: calc(var(--tw-spacing) * 3.5);
  }

  .tw\:h-4 {
    height: calc(var(--tw-spacing) * 4);
  }

  .tw\:h-5 {
    height: calc(var(--tw-spacing) * 5);
  }

  .tw\:h-6 {
    height: calc(var(--tw-spacing) * 6);
  }

  .tw\:h-7 {
    height: calc(var(--tw-spacing) * 7);
  }

  .tw\:h-8 {
    height: calc(var(--tw-spacing) * 8);
  }

  .tw\:h-9 {
    height: calc(var(--tw-spacing) * 9);
  }

  .tw\:h-10 {
    height: calc(var(--tw-spacing) * 10);
  }

  .tw\:h-14 {
    height: calc(var(--tw-spacing) * 14);
  }

  .tw\:h-24 {
    height: calc(var(--tw-spacing) * 24);
  }

  .tw\:h-\[3px\] {
    height: 3px;
  }

  .tw\:h-\[10px\] {
    height: 10px;
  }

  .tw\:h-\[18px\] {
    height: 18px;
  }

  .tw\:h-\[60vh\] {
    height: 60vh;
  }

  .tw\:h-\[var\(--radix-select-trigger-height\)\] {
    height: var(--radix-select-trigger-height);
  }

  .tw\:h-full {
    height: 100%;
  }

  .tw\:h-px {
    height: 1px;
  }

  .tw\:max-h-48 {
    max-height: calc(var(--tw-spacing) * 48);
  }

  .tw\:max-h-56 {
    max-height: calc(var(--tw-spacing) * 56);
  }

  .tw\:max-h-64 {
    max-height: calc(var(--tw-spacing) * 64);
  }

  .tw\:max-h-96 {
    max-height: calc(var(--tw-spacing) * 96);
  }

  .tw\:max-h-\[6\.5rem\] {
    max-height: 6.5rem;
  }

  .tw\:max-h-\[55\%\] {
    max-height: 55%;
  }

  .tw\:max-h-\[260px\] {
    max-height: 260px;
  }

  .tw\:min-h-0 {
    min-height: 0;
  }

  .tw\:min-h-\[48px\] {
    min-height: 48px;
  }

  .tw\:w-0 {
    width: 0;
  }

  .tw\:w-1\/3 {
    width: 33.3333%;
  }

  .tw\:w-2 {
    width: calc(var(--tw-spacing) * 2);
  }

  .tw\:w-2\.5 {
    width: calc(var(--tw-spacing) * 2.5);
  }

  .tw\:w-3 {
    width: calc(var(--tw-spacing) * 3);
  }

  .tw\:w-3\.5 {
    width: calc(var(--tw-spacing) * 3.5);
  }

  .tw\:w-4 {
    width: calc(var(--tw-spacing) * 4);
  }

  .tw\:w-5 {
    width: calc(var(--tw-spacing) * 5);
  }

  .tw\:w-6 {
    width: calc(var(--tw-spacing) * 6);
  }

  .tw\:w-7 {
    width: calc(var(--tw-spacing) * 7);
  }

  .tw\:w-8 {
    width: calc(var(--tw-spacing) * 8);
  }

  .tw\:w-9 {
    width: calc(var(--tw-spacing) * 9);
  }

  .tw\:w-12 {
    width: calc(var(--tw-spacing) * 12);
  }

  .tw\:w-20 {
    width: calc(var(--tw-spacing) * 20);
  }

  .tw\:w-48 {
    width: calc(var(--tw-spacing) * 48);
  }

  .tw\:w-72 {
    width: calc(var(--tw-spacing) * 72);
  }

  .tw\:w-\[2\.7em\] {
    width: 2.7em;
  }

  .tw\:w-\[3px\] {
    width: 3px;
  }

  .tw\:w-\[4px\] {
    width: 4px;
  }

  .tw\:w-\[22rem\] {
    width: 22rem;
  }

  .tw\:w-\[26px\] {
    width: 26px;
  }

  .tw\:w-\[36px\] {
    width: 36px;
  }

  .tw\:w-\[56px\] {
    width: 56px;
  }

  .tw\:w-\[64px\] {
    width: 64px;
  }

  .tw\:w-\[68px\] {
    width: 68px;
  }

  .tw\:w-\[70px\] {
    width: 70px;
  }

  .tw\:w-\[90px\] {
    width: 90px;
  }

  .tw\:w-\[100px\] {
    width: 100px;
  }

  .tw\:w-\[120px\] {
    width: 120px;
  }

  .tw\:w-\[130px\] {
    width: 130px;
  }

  .tw\:w-\[140px\] {
    width: 140px;
  }

  .tw\:w-\[150px\] {
    width: 150px;
  }

  .tw\:w-\[160px\] {
    width: 160px;
  }

  .tw\:w-\[170px\] {
    width: 170px;
  }

  .tw\:w-\[180px\] {
    width: 180px;
  }

  .tw\:w-\[190px\] {
    width: 190px;
  }

  .tw\:w-\[200px\] {
    width: 200px;
  }

  .tw\:w-\[220px\] {
    width: 220px;
  }

  .tw\:w-\[240px\] {
    width: 240px;
  }

  .tw\:w-\[260px\] {
    width: 260px;
  }

  .tw\:w-\[300px\] {
    width: 300px;
  }

  .tw\:w-\[360px\] {
    width: 360px;
  }

  .tw\:w-\[420px\] {
    width: 420px;
  }

  .tw\:w-\[700px\] {
    width: 700px;
  }

  .tw\:w-auto {
    width: auto;
  }

  .tw\:w-fit {
    width: fit-content;
  }

  .tw\:w-full {
    width: 100%;
  }

  .tw\:w-px {
    width: 1px;
  }

  .tw\:max-w-\[22rem\] {
    max-width: 22rem;
  }

  .tw\:max-w-\[90vw\] {
    max-width: 90vw;
  }

  .tw\:max-w-\[94vw\] {
    max-width: 94vw;
  }

  .tw\:max-w-\[110px\] {
    max-width: 110px;
  }

  .tw\:max-w-\[420px\] {
    max-width: 420px;
  }

  .tw\:max-w-\[520px\] {
    max-width: 520px;
  }

  .tw\:max-w-\[560px\] {
    max-width: 560px;
  }

  .tw\:max-w-\[660px\] {
    max-width: 660px;
  }

  .tw\:max-w-\[680px\] {
    max-width: 680px;
  }

  .tw\:max-w-\[720px\] {
    max-width: 720px;
  }

  .tw\:max-w-\[760px\] {
    max-width: 760px;
  }

  .tw\:max-w-\[820px\] {
    max-width: 820px;
  }

  .tw\:max-w-\[1500px\] {
    max-width: 1500px;
  }

  .tw\:max-w-lg {
    max-width: var(--tw-container-lg);
  }

  .tw\:min-w-0 {
    min-width: 0;
  }

  .tw\:min-w-\[8rem\] {
    min-width: 8rem;
  }

  .tw\:min-w-\[44px\] {
    min-width: 44px;
  }

  .tw\:min-w-\[64px\] {
    min-width: 64px;
  }

  .tw\:min-w-\[86px\] {
    min-width: 86px;
  }

  .tw\:min-w-\[118px\] {
    min-width: 118px;
  }

  .tw\:min-w-\[120px\] {
    min-width: 120px;
  }

  .tw\:min-w-\[140px\] {
    min-width: 140px;
  }

  .tw\:min-w-\[160px\] {
    min-width: 160px;
  }

  .tw\:min-w-\[220px\] {
    min-width: 220px;
  }

  .tw\:min-w-\[380px\] {
    min-width: 380px;
  }

  .tw\:min-w-\[var\(--radix-select-trigger-width\)\] {
    min-width: var(--radix-select-trigger-width);
  }

  .tw\:flex-1 {
    flex: 1;
  }

  .tw\:shrink-0 {
    flex-shrink: 0;
  }

  .tw\:table-fixed {
    table-layout: fixed;
  }

  .tw\:caption-bottom {
    caption-side: bottom;
  }

  .tw\:border-separate {
    border-collapse: separate;
  }

  .tw\:border-spacing-0 {
    --tw-border-spacing-x: 0px;
    --tw-border-spacing-y: 0px;
    border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y);
  }

  .tw\:-translate-x-1\/2 {
    --tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
    translate: var(--tw-translate-x) var(--tw-translate-y);
  }

  .tw\:translate-x-\[-50\%\] {
    --tw-translate-x: -50%;
    translate: var(--tw-translate-x) var(--tw-translate-y);
  }

  .tw\:translate-y-\[-50\%\] {
    --tw-translate-y: -50%;
    translate: var(--tw-translate-x) var(--tw-translate-y);
  }

  .tw\:animate-\[progress-slide_1s_ease-in-out_infinite\] {
    animation: 1s ease-in-out infinite progress-slide;
  }

  .tw\:animate-pulse {
    animation: var(--tw-animate-pulse);
  }

  .tw\:animate-spin {
    animation: var(--tw-animate-spin);
  }

  .tw\:cursor-col-resize {
    cursor: col-resize;
  }

  .tw\:cursor-default {
    cursor: default;
  }

  .tw\:cursor-grab {
    cursor: grab;
  }

  .tw\:cursor-pointer {
    cursor: pointer;
  }

  .tw\:cursor-wait {
    cursor: wait;
  }

  .tw\:touch-none {
    touch-action: none;
  }

  .tw\:list-disc {
    list-style-type: disc;
  }

  .tw\:grid-cols-6 {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }

  .tw\:grid-cols-7 {
    grid-template-columns: repeat(7, minmax(0, 1fr));
  }

  .tw\:grid-cols-\[1\.5rem_minmax\(0\,1fr\)_3\.5rem_minmax\(0\,14rem\)\] {
    grid-template-columns: 1.5rem minmax(0, 1fr) 3.5rem minmax(0, 14rem);
  }

  .tw\:grid-cols-\[auto_auto_auto_1fr\] {
    grid-template-columns: auto auto auto 1fr;
  }

  .tw\:flex-col {
    flex-direction: column;
  }

  .tw\:flex-col-reverse {
    flex-direction: column-reverse;
  }

  .tw\:flex-wrap {
    flex-wrap: wrap;
  }

  .tw\:items-baseline {
    align-items: baseline;
  }

  .tw\:items-center {
    align-items: center;
  }

  .tw\:items-end {
    align-items: flex-end;
  }

  .tw\:items-start {
    align-items: flex-start;
  }

  .tw\:items-stretch {
    align-items: stretch;
  }

  .tw\:justify-between {
    justify-content: space-between;
  }

  .tw\:justify-center {
    justify-content: center;
  }

  .tw\:justify-end {
    justify-content: flex-end;
  }

  .tw\:gap-0 {
    gap: 0;
  }

  .tw\:gap-0\.5 {
    gap: calc(var(--tw-spacing) * .5);
  }

  .tw\:gap-1 {
    gap: var(--tw-spacing);
  }

  .tw\:gap-1\.5 {
    gap: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:gap-2 {
    gap: calc(var(--tw-spacing) * 2);
  }

  .tw\:gap-3 {
    gap: calc(var(--tw-spacing) * 3);
  }

  .tw\:gap-4 {
    gap: calc(var(--tw-spacing) * 4);
  }

  .tw\:gap-6 {
    gap: calc(var(--tw-spacing) * 6);
  }

  .tw\:gap-10 {
    gap: calc(var(--tw-spacing) * 10);
  }

  .tw\:gap-\[2px\] {
    gap: 2px;
  }

  .tw\:gap-\[3px\] {
    gap: 3px;
  }

  :where(.tw\:space-y-0\.5 > :not(:last-child)) {
    --tw-space-y-reverse: 0;
    margin-block-start: calc(calc(var(--tw-spacing) * .5) * var(--tw-space-y-reverse));
    margin-block-end: calc(calc(var(--tw-spacing) * .5) * calc(1 - var(--tw-space-y-reverse)));
  }

  :where(.tw\:space-y-1 > :not(:last-child)) {
    --tw-space-y-reverse: 0;
    margin-block-start: calc(var(--tw-spacing) * var(--tw-space-y-reverse));
    margin-block-end: calc(var(--tw-spacing) * calc(1 - var(--tw-space-y-reverse)));
  }

  :where(.tw\:space-y-1\.5 > :not(:last-child)) {
    --tw-space-y-reverse: 0;
    margin-block-start: calc(calc(var(--tw-spacing) * 1.5) * var(--tw-space-y-reverse));
    margin-block-end: calc(calc(var(--tw-spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)));
  }

  :where(.tw\:space-y-2 > :not(:last-child)) {
    --tw-space-y-reverse: 0;
    margin-block-start: calc(calc(var(--tw-spacing) * 2) * var(--tw-space-y-reverse));
    margin-block-end: calc(calc(var(--tw-spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));
  }

  :where(.tw\:space-y-3 > :not(:last-child)) {
    --tw-space-y-reverse: 0;
    margin-block-start: calc(calc(var(--tw-spacing) * 3) * var(--tw-space-y-reverse));
    margin-block-end: calc(calc(var(--tw-spacing) * 3) * calc(1 - var(--tw-space-y-reverse)));
  }

  :where(.tw\:space-y-4 > :not(:last-child)) {
    --tw-space-y-reverse: 0;
    margin-block-start: calc(calc(var(--tw-spacing) * 4) * var(--tw-space-y-reverse));
    margin-block-end: calc(calc(var(--tw-spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
  }

  :where(.tw\:space-y-5 > :not(:last-child)) {
    --tw-space-y-reverse: 0;
    margin-block-start: calc(calc(var(--tw-spacing) * 5) * var(--tw-space-y-reverse));
    margin-block-end: calc(calc(var(--tw-spacing) * 5) * calc(1 - var(--tw-space-y-reverse)));
  }

  .tw\:gap-x-1 {
    column-gap: var(--tw-spacing);
  }

  .tw\:gap-x-2 {
    column-gap: calc(var(--tw-spacing) * 2);
  }

  .tw\:gap-x-3 {
    column-gap: calc(var(--tw-spacing) * 3);
  }

  .tw\:gap-x-8 {
    column-gap: calc(var(--tw-spacing) * 8);
  }

  :where(.tw\:space-x-2 > :not(:last-child)) {
    --tw-space-x-reverse: 0;
    margin-inline-start: calc(calc(var(--tw-spacing) * 2) * var(--tw-space-x-reverse));
    margin-inline-end: calc(calc(var(--tw-spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
  }

  :where(.tw\:space-x-4 > :not(:last-child)) {
    --tw-space-x-reverse: 0;
    margin-inline-start: calc(calc(var(--tw-spacing) * 4) * var(--tw-space-x-reverse));
    margin-inline-end: calc(calc(var(--tw-spacing) * 4) * calc(1 - var(--tw-space-x-reverse)));
  }

  .tw\:gap-y-0\.5 {
    row-gap: calc(var(--tw-spacing) * .5);
  }

  .tw\:gap-y-1 {
    row-gap: var(--tw-spacing);
  }

  .tw\:gap-y-1\.5 {
    row-gap: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:gap-y-4 {
    row-gap: calc(var(--tw-spacing) * 4);
  }

  .tw\:self-center {
    align-self: center;
  }

  .tw\:self-end {
    align-self: flex-end;
  }

  .tw\:self-start {
    align-self: flex-start;
  }

  .tw\:truncate {
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
  }

  .tw\:overflow-auto {
    overflow: auto;
  }

  .tw\:overflow-hidden {
    overflow: hidden;
  }

  .tw\:overflow-x-auto {
    overflow-x: auto;
  }

  .tw\:overflow-y-auto {
    overflow-y: auto;
  }

  .tw\:rounded {
    border-radius: .25rem;
  }

  .tw\:rounded-\[1px\] {
    border-radius: 1px;
  }

  .tw\:rounded-\[2px\] {
    border-radius: 2px;
  }

  .tw\:rounded-full {
    border-radius: 3.40282e38px;
  }

  .tw\:rounded-md {
    border-radius: calc(var(--radius) - 2px);
  }

  .tw\:rounded-sm {
    border-radius: calc(var(--radius) - 4px);
  }

  .tw\:rounded-r {
    border-top-right-radius: .25rem;
    border-bottom-right-radius: .25rem;
  }

  .tw\:rounded-b-md {
    border-bottom-right-radius: calc(var(--radius) - 2px);
    border-bottom-left-radius: calc(var(--radius) - 2px);
  }

  .tw\:border {
    border-style: var(--tw-border-style);
    border-width: 1px;
  }

  .tw\:border-0 {
    border-style: var(--tw-border-style);
    border-width: 0;
  }

  .tw\:border-2 {
    border-style: var(--tw-border-style);
    border-width: 2px;
  }

  .tw\:border-t {
    border-top-style: var(--tw-border-style);
    border-top-width: 1px;
  }

  .tw\:border-t-\[7px\] {
    border-top-style: var(--tw-border-style);
    border-top-width: 7px;
  }

  .tw\:border-r {
    border-right-style: var(--tw-border-style);
    border-right-width: 1px;
  }

  .tw\:border-b {
    border-bottom-style: var(--tw-border-style);
    border-bottom-width: 1px;
  }

  .tw\:border-l {
    border-left-style: var(--tw-border-style);
    border-left-width: 1px;
  }

  .tw\:border-l-2 {
    border-left-style: var(--tw-border-style);
    border-left-width: 2px;
  }

  .tw\:border-l-\[7px\] {
    border-left-style: var(--tw-border-style);
    border-left-width: 7px;
  }

  .tw\:border-dashed {
    --tw-border-style: dashed;
    border-style: dashed;
  }

  .tw\:border-amber-300 {
    border-color: var(--tw-color-amber-300);
  }

  .tw\:border-amber-400, .tw\:border-amber-400\/50 {
    border-color: var(--tw-color-amber-400);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-amber-400\/50 {
      border-color: color-mix(in oklab, var(--tw-color-amber-400) 50%, transparent);
    }
  }

  .tw\:border-blue-400 {
    border-color: var(--tw-color-blue-400);
  }

  .tw\:border-blue-500 {
    border-color: var(--tw-color-blue-500);
  }

  .tw\:border-blue-700 {
    border-color: var(--tw-color-blue-700);
  }

  .tw\:border-border, .tw\:border-border\/50 {
    border-color: var(--border);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-border\/50 {
      border-color: color-mix(in oklab, var(--border) 50%, transparent);
    }
  }

  .tw\:border-border\/60 {
    border-color: var(--border);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-border\/60 {
      border-color: color-mix(in oklab, var(--border) 60%, transparent);
    }
  }

  .tw\:border-destructive, .tw\:border-destructive\/40 {
    border-color: var(--destructive);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-destructive\/40 {
      border-color: color-mix(in oklab, var(--destructive) 40%, transparent);
    }
  }

  .tw\:border-destructive\/50 {
    border-color: var(--destructive);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-destructive\/50 {
      border-color: color-mix(in oklab, var(--destructive) 50%, transparent);
    }
  }

  .tw\:border-emerald-300 {
    border-color: var(--tw-color-emerald-300);
  }

  .tw\:border-emerald-500\/40 {
    border-color: var(--tw-color-emerald-500);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-emerald-500\/40 {
      border-color: color-mix(in oklab, var(--tw-color-emerald-500) 40%, transparent);
    }
  }

  .tw\:border-gray-400 {
    border-color: var(--tw-color-gray-400);
  }

  .tw\:border-gray-700 {
    border-color: var(--tw-color-gray-700);
  }

  .tw\:border-green-600\/30 {
    border-color: var(--tw-color-green-600);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-green-600\/30 {
      border-color: color-mix(in oklab, var(--tw-color-green-600) 30%, transparent);
    }
  }

  .tw\:border-input, .tw\:border-input\/60 {
    border-color: var(--input);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-input\/60 {
      border-color: color-mix(in oklab, var(--input) 60%, transparent);
    }
  }

  .tw\:border-muted-foreground\/20 {
    border-color: var(--muted-foreground);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-muted-foreground\/20 {
      border-color: color-mix(in oklab, var(--muted-foreground) 20%, transparent);
    }
  }

  .tw\:border-primary, .tw\:border-primary\/30 {
    border-color: var(--primary);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:border-primary\/30 {
      border-color: color-mix(in oklab, var(--primary) 30%, transparent);
    }
  }

  .tw\:border-red-300 {
    border-color: var(--tw-color-red-300);
  }

  .tw\:border-table-header-border {
    border-color: var(--table-header-border);
  }

  .tw\:border-table-row-border {
    border-color: var(--table-row-border);
  }

  .tw\:border-transparent {
    border-color: #0000;
  }

  .tw\:border-violet-300 {
    border-color: var(--tw-color-violet-300);
  }

  .tw\:border-t-red-600 {
    border-top-color: var(--tw-color-red-600);
  }

  .tw\:border-t-transparent {
    border-top-color: #0000;
  }

  .tw\:border-r-amber-300 {
    border-right-color: var(--tw-color-amber-300);
  }

  .tw\:border-l-amber-300 {
    border-left-color: var(--tw-color-amber-300);
  }

  .tw\:border-l-transparent {
    border-left-color: #0000;
  }

  .tw\:border-l-yellow-400 {
    border-left-color: var(--tw-color-yellow-400);
  }

  .tw\:bg-accent {
    background-color: var(--accent);
  }

  .tw\:bg-amber-50, .tw\:bg-amber-50\/60 {
    background-color: var(--tw-color-amber-50);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-amber-50\/60 {
      background-color: color-mix(in oklab, var(--tw-color-amber-50) 60%, transparent);
    }
  }

  .tw\:bg-amber-100 {
    background-color: var(--tw-color-amber-100);
  }

  .tw\:bg-amber-500 {
    background-color: var(--tw-color-amber-500);
  }

  .tw\:bg-background, .tw\:bg-background\/70 {
    background-color: var(--background);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-background\/70 {
      background-color: color-mix(in oklab, var(--background) 70%, transparent);
    }
  }

  .tw\:bg-black\/80 {
    background-color: var(--tw-color-black);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-black\/80 {
      background-color: color-mix(in oklab, var(--tw-color-black) 80%, transparent);
    }
  }

  .tw\:bg-blue-200 {
    background-color: var(--tw-color-blue-200);
  }

  .tw\:bg-blue-500, .tw\:bg-blue-500\/60 {
    background-color: var(--tw-color-blue-500);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-blue-500\/60 {
      background-color: color-mix(in oklab, var(--tw-color-blue-500) 60%, transparent);
    }
  }

  .tw\:bg-blue-600, .tw\:bg-blue-600\/40 {
    background-color: var(--tw-color-blue-600);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-blue-600\/40 {
      background-color: color-mix(in oklab, var(--tw-color-blue-600) 40%, transparent);
    }
  }

  .tw\:bg-blue-600\/80 {
    background-color: var(--tw-color-blue-600);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-blue-600\/80 {
      background-color: color-mix(in oklab, var(--tw-color-blue-600) 80%, transparent);
    }
  }

  .tw\:bg-border\/0 {
    background-color: var(--border);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-border\/0 {
      background-color: color-mix(in oklab, var(--border) 0%, transparent);
    }
  }

  .tw\:bg-destructive, .tw\:bg-destructive\/5 {
    background-color: var(--destructive);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-destructive\/5 {
      background-color: color-mix(in oklab, var(--destructive) 5%, transparent);
    }
  }

  .tw\:bg-destructive\/10 {
    background-color: var(--destructive);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-destructive\/10 {
      background-color: color-mix(in oklab, var(--destructive) 10%, transparent);
    }
  }

  .tw\:bg-emerald-50 {
    background-color: var(--tw-color-emerald-50);
  }

  .tw\:bg-emerald-100 {
    background-color: var(--tw-color-emerald-100);
  }

  .tw\:bg-gray-600 {
    background-color: var(--tw-color-gray-600);
  }

  .tw\:bg-gray-700 {
    background-color: var(--tw-color-gray-700);
  }

  .tw\:bg-green-50 {
    background-color: var(--tw-color-green-50);
  }

  .tw\:bg-muted {
    background-color: var(--muted);
  }

  .tw\:bg-muted-foreground\/10 {
    background-color: var(--muted-foreground);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-muted-foreground\/10 {
      background-color: color-mix(in oklab, var(--muted-foreground) 10%, transparent);
    }
  }

  .tw\:bg-muted-foreground\/25 {
    background-color: var(--muted-foreground);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-muted-foreground\/25 {
      background-color: color-mix(in oklab, var(--muted-foreground) 25%, transparent);
    }
  }

  .tw\:bg-muted\/30 {
    background-color: var(--muted);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-muted\/30 {
      background-color: color-mix(in oklab, var(--muted) 30%, transparent);
    }
  }

  .tw\:bg-muted\/40 {
    background-color: var(--muted);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-muted\/40 {
      background-color: color-mix(in oklab, var(--muted) 40%, transparent);
    }
  }

  .tw\:bg-muted\/50 {
    background-color: var(--muted);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-muted\/50 {
      background-color: color-mix(in oklab, var(--muted) 50%, transparent);
    }
  }

  .tw\:bg-muted\/60 {
    background-color: var(--muted);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-muted\/60 {
      background-color: color-mix(in oklab, var(--muted) 60%, transparent);
    }
  }

  .tw\:bg-neutral-200 {
    background-color: var(--tw-color-neutral-200);
  }

  .tw\:bg-orange-100 {
    background-color: var(--tw-color-orange-100);
  }

  .tw\:bg-popover {
    background-color: var(--popover);
  }

  .tw\:bg-primary, .tw\:bg-primary\/5 {
    background-color: var(--primary);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-primary\/5 {
      background-color: color-mix(in oklab, var(--primary) 5%, transparent);
    }
  }

  .tw\:bg-primary\/10 {
    background-color: var(--primary);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-primary\/10 {
      background-color: color-mix(in oklab, var(--primary) 10%, transparent);
    }
  }

  .tw\:bg-primary\/60 {
    background-color: var(--primary);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-primary\/60 {
      background-color: color-mix(in oklab, var(--primary) 60%, transparent);
    }
  }

  .tw\:bg-primary\/70 {
    background-color: var(--primary);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-primary\/70 {
      background-color: color-mix(in oklab, var(--primary) 70%, transparent);
    }
  }

  .tw\:bg-red-50\/50 {
    background-color: var(--tw-color-red-50);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-red-50\/50 {
      background-color: color-mix(in oklab, var(--tw-color-red-50) 50%, transparent);
    }
  }

  .tw\:bg-red-500\/10 {
    background-color: var(--tw-color-red-500);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-red-500\/10 {
      background-color: color-mix(in oklab, var(--tw-color-red-500) 10%, transparent);
    }
  }

  .tw\:bg-rose-600 {
    background-color: var(--tw-color-rose-600);
  }

  .tw\:bg-secondary {
    background-color: var(--secondary);
  }

  .tw\:bg-sky-100 {
    background-color: var(--tw-color-sky-100);
  }

  .tw\:bg-table-header-bg {
    background-color: var(--table-header-bg);
  }

  .tw\:bg-transparent {
    background-color: #0000;
  }

  .tw\:bg-violet-50 {
    background-color: var(--tw-color-violet-50);
  }

  .tw\:bg-violet-100 {
    background-color: var(--tw-color-violet-100);
  }

  .tw\:bg-white\/20 {
    background-color: var(--tw-color-white);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:bg-white\/20 {
      background-color: color-mix(in oklab, var(--tw-color-white) 20%, transparent);
    }
  }

  .tw\:bg-yellow-50 {
    background-color: var(--tw-color-yellow-50);
  }

  .tw\:bg-yellow-100 {
    background-color: var(--tw-color-yellow-100);
  }

  .tw\:fill-current {
    fill: currentColor;
  }

  .tw\:fill-destructive {
    fill: var(--destructive);
  }

  .tw\:fill-muted-foreground, .tw\:fill-muted-foreground\/15 {
    fill: var(--muted-foreground);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:fill-muted-foreground\/15 {
      fill: color-mix(in oklab, var(--muted-foreground) 15%, transparent);
    }
  }

  .tw\:fill-primary\/70 {
    fill: var(--primary);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:fill-primary\/70 {
      fill: color-mix(in oklab, var(--primary) 70%, transparent);
    }
  }

  .tw\:stroke-destructive {
    stroke: var(--destructive);
  }

  .tw\:p-0 {
    padding: 0;
  }

  .tw\:p-0\.5 {
    padding: calc(var(--tw-spacing) * .5);
  }

  .tw\:p-1 {
    padding: var(--tw-spacing);
  }

  .tw\:p-2 {
    padding: calc(var(--tw-spacing) * 2);
  }

  .tw\:p-2\.5 {
    padding: calc(var(--tw-spacing) * 2.5);
  }

  .tw\:p-3 {
    padding: calc(var(--tw-spacing) * 3);
  }

  .tw\:p-4 {
    padding: calc(var(--tw-spacing) * 4);
  }

  .tw\:p-6 {
    padding: calc(var(--tw-spacing) * 6);
  }

  .tw\:px-0 {
    padding-inline: 0;
  }

  .tw\:px-0\.5 {
    padding-inline: calc(var(--tw-spacing) * .5);
  }

  .tw\:px-1 {
    padding-inline: var(--tw-spacing);
  }

  .tw\:px-1\.5 {
    padding-inline: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:px-2 {
    padding-inline: calc(var(--tw-spacing) * 2);
  }

  .tw\:px-2\.5 {
    padding-inline: calc(var(--tw-spacing) * 2.5);
  }

  .tw\:px-3 {
    padding-inline: calc(var(--tw-spacing) * 3);
  }

  .tw\:px-4 {
    padding-inline: calc(var(--tw-spacing) * 4);
  }

  .tw\:px-8 {
    padding-inline: calc(var(--tw-spacing) * 8);
  }

  .tw\:py-0\.5 {
    padding-block: calc(var(--tw-spacing) * .5);
  }

  .tw\:py-1 {
    padding-block: var(--tw-spacing);
  }

  .tw\:py-1\.5 {
    padding-block: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:py-2 {
    padding-block: calc(var(--tw-spacing) * 2);
  }

  .tw\:py-3 {
    padding-block: calc(var(--tw-spacing) * 3);
  }

  .tw\:py-6 {
    padding-block: calc(var(--tw-spacing) * 6);
  }

  .tw\:py-px {
    padding-block: 1px;
  }

  .tw\:pt-0\.5 {
    padding-top: calc(var(--tw-spacing) * .5);
  }

  .tw\:pt-1 {
    padding-top: var(--tw-spacing);
  }

  .tw\:pt-1\.5 {
    padding-top: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:pt-2 {
    padding-top: calc(var(--tw-spacing) * 2);
  }

  .tw\:pt-3 {
    padding-top: calc(var(--tw-spacing) * 3);
  }

  .tw\:pt-16 {
    padding-top: calc(var(--tw-spacing) * 16);
  }

  .tw\:pr-1 {
    padding-right: var(--tw-spacing);
  }

  .tw\:pr-1\.5 {
    padding-right: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:pr-2 {
    padding-right: calc(var(--tw-spacing) * 2);
  }

  .tw\:pr-3 {
    padding-right: calc(var(--tw-spacing) * 3);
  }

  .tw\:pr-4 {
    padding-right: calc(var(--tw-spacing) * 4);
  }

  .tw\:pr-6 {
    padding-right: calc(var(--tw-spacing) * 6);
  }

  .tw\:pr-8 {
    padding-right: calc(var(--tw-spacing) * 8);
  }

  .tw\:pb-0 {
    padding-bottom: 0;
  }

  .tw\:pb-0\.5 {
    padding-bottom: calc(var(--tw-spacing) * .5);
  }

  .tw\:pb-1 {
    padding-bottom: var(--tw-spacing);
  }

  .tw\:pb-1\.5 {
    padding-bottom: calc(var(--tw-spacing) * 1.5);
  }

  .tw\:pb-3 {
    padding-bottom: calc(var(--tw-spacing) * 3);
  }

  .tw\:pl-0\.5 {
    padding-left: calc(var(--tw-spacing) * .5);
  }

  .tw\:pl-1 {
    padding-left: var(--tw-spacing);
  }

  .tw\:pl-2 {
    padding-left: calc(var(--tw-spacing) * 2);
  }

  .tw\:pl-3 {
    padding-left: calc(var(--tw-spacing) * 3);
  }

  .tw\:pl-4 {
    padding-left: calc(var(--tw-spacing) * 4);
  }

  .tw\:pl-8 {
    padding-left: calc(var(--tw-spacing) * 8);
  }

  .tw\:text-center {
    text-align: center;
  }

  .tw\:text-left {
    text-align: left;
  }

  .tw\:text-right {
    text-align: right;
  }

  .tw\:align-baseline {
    vertical-align: baseline;
  }

  .tw\:align-middle {
    vertical-align: middle;
  }

  .tw\:align-top {
    vertical-align: top;
  }

  .tw\:font-mono {
    font-family: var(--tw-font-mono);
  }

  .tw\:text-base {
    font-size: var(--tw-text-base);
    line-height: var(--tw-leading, var(--tw-text-base--line-height));
  }

  .tw\:text-lg {
    font-size: var(--tw-text-lg);
    line-height: var(--tw-leading, var(--tw-text-lg--line-height));
  }

  .tw\:text-sm {
    font-size: var(--tw-text-sm);
    line-height: var(--tw-leading, var(--tw-text-sm--line-height));
  }

  .tw\:text-xs {
    font-size: var(--tw-text-xs);
    line-height: var(--tw-leading, var(--tw-text-xs--line-height));
  }

  .tw\:text-\[9px\] {
    font-size: 9px;
  }

  .tw\:text-\[10px\] {
    font-size: 10px;
  }

  .tw\:text-\[11px\] {
    font-size: 11px;
  }

  .tw\:leading-\[1\.15rem\] {
    --tw-leading: 1.15rem;
    line-height: 1.15rem;
  }

  .tw\:leading-\[14px\] {
    --tw-leading: 14px;
    line-height: 14px;
  }

  .tw\:leading-\[18px\] {
    --tw-leading: 18px;
    line-height: 18px;
  }

  .tw\:leading-none {
    --tw-leading: 1;
    line-height: 1;
  }

  .tw\:leading-snug {
    --tw-leading: var(--tw-leading-snug);
    line-height: var(--tw-leading-snug);
  }

  .tw\:leading-tight {
    --tw-leading: var(--tw-leading-tight);
    line-height: var(--tw-leading-tight);
  }

  .tw\:font-bold {
    --tw-font-weight: var(--tw-font-weight-bold);
    font-weight: var(--tw-font-weight-bold);
  }

  .tw\:font-medium {
    --tw-font-weight: var(--tw-font-weight-medium);
    font-weight: var(--tw-font-weight-medium);
  }

  .tw\:font-normal {
    --tw-font-weight: var(--tw-font-weight-normal);
    font-weight: var(--tw-font-weight-normal);
  }

  .tw\:font-semibold {
    --tw-font-weight: var(--tw-font-weight-semibold);
    font-weight: var(--tw-font-weight-semibold);
  }

  .tw\:tracking-normal {
    --tw-tracking: var(--tw-tracking-normal);
    letter-spacing: var(--tw-tracking-normal);
  }

  .tw\:tracking-tight {
    --tw-tracking: var(--tw-tracking-tight);
    letter-spacing: var(--tw-tracking-tight);
  }

  .tw\:tracking-wide {
    --tw-tracking: var(--tw-tracking-wide);
    letter-spacing: var(--tw-tracking-wide);
  }

  .tw\:tracking-wider {
    --tw-tracking: var(--tw-tracking-wider);
    letter-spacing: var(--tw-tracking-wider);
  }

  .tw\:tracking-widest {
    --tw-tracking: var(--tw-tracking-widest);
    letter-spacing: var(--tw-tracking-widest);
  }

  .tw\:break-words {
    overflow-wrap: break-word;
  }

  .tw\:whitespace-normal {
    white-space: normal;
  }

  .tw\:whitespace-nowrap {
    white-space: nowrap;
  }

  .tw\:whitespace-pre-line {
    white-space: pre-line;
  }

  .tw\:text-amber-700 {
    color: var(--tw-color-amber-700);
  }

  .tw\:text-amber-800 {
    color: var(--tw-color-amber-800);
  }

  .tw\:text-amber-900 {
    color: var(--tw-color-amber-900);
  }

  .tw\:text-amber-950 {
    color: var(--tw-color-amber-950);
  }

  .tw\:text-blue-500 {
    color: var(--tw-color-blue-500);
  }

  .tw\:text-blue-600 {
    color: var(--tw-color-blue-600);
  }

  .tw\:text-blue-700 {
    color: var(--tw-color-blue-700);
  }

  .tw\:text-blue-900 {
    color: var(--tw-color-blue-900);
  }

  .tw\:text-current {
    color: currentColor;
  }

  .tw\:text-destructive {
    color: var(--destructive);
  }

  .tw\:text-destructive-foreground {
    color: var(--destructive-foreground);
  }

  .tw\:text-emerald-600 {
    color: var(--tw-color-emerald-600);
  }

  .tw\:text-emerald-700 {
    color: var(--tw-color-emerald-700);
  }

  .tw\:text-emerald-800 {
    color: var(--tw-color-emerald-800);
  }

  .tw\:text-emerald-900 {
    color: var(--tw-color-emerald-900);
  }

  .tw\:text-foreground {
    color: var(--foreground);
  }

  .tw\:text-green-800 {
    color: var(--tw-color-green-800);
  }

  .tw\:text-muted-foreground, .tw\:text-muted-foreground\/30 {
    color: var(--muted-foreground);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:text-muted-foreground\/30 {
      color: color-mix(in oklab, var(--muted-foreground) 30%, transparent);
    }
  }

  .tw\:text-muted-foreground\/40 {
    color: var(--muted-foreground);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:text-muted-foreground\/40 {
      color: color-mix(in oklab, var(--muted-foreground) 40%, transparent);
    }
  }

  .tw\:text-muted-foreground\/50 {
    color: var(--muted-foreground);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:text-muted-foreground\/50 {
      color: color-mix(in oklab, var(--muted-foreground) 50%, transparent);
    }
  }

  .tw\:text-muted-foreground\/70 {
    color: var(--muted-foreground);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:text-muted-foreground\/70 {
      color: color-mix(in oklab, var(--muted-foreground) 70%, transparent);
    }
  }

  .tw\:text-muted-foreground\/80 {
    color: var(--muted-foreground);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:text-muted-foreground\/80 {
      color: color-mix(in oklab, var(--muted-foreground) 80%, transparent);
    }
  }

  .tw\:text-orange-800 {
    color: var(--tw-color-orange-800);
  }

  .tw\:text-popover-foreground {
    color: var(--popover-foreground);
  }

  .tw\:text-primary {
    color: var(--primary);
  }

  .tw\:text-primary-foreground {
    color: var(--primary-foreground);
  }

  .tw\:text-primary\/50 {
    color: var(--primary);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:text-primary\/50 {
      color: color-mix(in oklab, var(--primary) 50%, transparent);
    }
  }

  .tw\:text-red-500 {
    color: var(--tw-color-red-500);
  }

  .tw\:text-red-600 {
    color: var(--tw-color-red-600);
  }

  .tw\:text-red-700 {
    color: var(--tw-color-red-700);
  }

  .tw\:text-red-800 {
    color: var(--tw-color-red-800);
  }

  .tw\:text-secondary-foreground {
    color: var(--secondary-foreground);
  }

  .tw\:text-sky-800 {
    color: var(--tw-color-sky-800);
  }

  .tw\:text-table-header-text {
    color: var(--table-header-text);
  }

  .tw\:text-violet-800 {
    color: var(--tw-color-violet-800);
  }

  .tw\:text-violet-900 {
    color: var(--tw-color-violet-900);
  }

  .tw\:text-white, .tw\:text-white\/90 {
    color: var(--tw-color-white);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:text-white\/90 {
      color: color-mix(in oklab, var(--tw-color-white) 90%, transparent);
    }
  }

  .tw\:capitalize {
    text-transform: capitalize;
  }

  .tw\:normal-case {
    text-transform: none;
  }

  .tw\:uppercase {
    text-transform: uppercase;
  }

  .tw\:italic {
    font-style: italic;
  }

  .tw\:tabular-nums {
    --tw-numeric-spacing: tabular-nums;
    font-variant-numeric: var(--tw-ordinal, ) var(--tw-slashed-zero, ) var(--tw-numeric-figure, ) var(--tw-numeric-spacing, ) var(--tw-numeric-fraction, );
  }

  .tw\:line-through {
    text-decoration-line: line-through;
  }

  .tw\:underline {
    text-decoration-line: underline;
  }

  .tw\:decoration-dotted {
    text-decoration-style: dotted;
  }

  .tw\:decoration-2 {
    text-decoration-thickness: 2px;
  }

  .tw\:underline-offset-2 {
    text-underline-offset: 2px;
  }

  .tw\:underline-offset-4 {
    text-underline-offset: 4px;
  }

  .tw\:opacity-0 {
    opacity: 0;
  }

  .tw\:opacity-40 {
    opacity: .4;
  }

  .tw\:opacity-50 {
    opacity: .5;
  }

  .tw\:opacity-60 {
    opacity: .6;
  }

  .tw\:opacity-70 {
    opacity: .7;
  }

  .tw\:opacity-80 {
    opacity: .8;
  }

  .tw\:opacity-100 {
    opacity: 1;
  }

  .tw\:shadow {
    --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, #0000001a), 0 1px 2px -1px var(--tw-shadow-color, #0000001a);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }

  .tw\:shadow-lg {
    --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, #0000001a), 0 4px 6px -4px var(--tw-shadow-color, #0000001a);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }

  .tw\:shadow-md {
    --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, #0000001a), 0 2px 4px -2px var(--tw-shadow-color, #0000001a);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }

  .tw\:shadow-sm {
    --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, #0000001a), 0 1px 2px -1px var(--tw-shadow-color, #0000001a);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }

  .tw\:ring-2 {
    --tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }

  .tw\:ring-amber-500 {
    --tw-ring-color: var(--tw-color-amber-500);
  }

  .tw\:ring-offset-background {
    --tw-ring-offset-color: var(--background);
  }

  .tw\:transition-colors {
    transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
    transition-timing-function: var(--tw-ease, var(--tw-default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--tw-default-transition-duration));
  }

  .tw\:transition-opacity {
    transition-property: opacity;
    transition-timing-function: var(--tw-ease, var(--tw-default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--tw-default-transition-duration));
  }

  .tw\:duration-200 {
    --tw-duration: .2s;
    transition-duration: .2s;
  }

  .tw\:duration-500 {
    --tw-duration: .5s;
    transition-duration: .5s;
  }

  .tw\:outline-none {
    --tw-outline-style: none;
    outline-style: none;
  }

  .tw\:select-none {
    -webkit-user-select: none;
    user-select: none;
  }

  .tw\:select-text {
    -webkit-user-select: text;
    user-select: text;
  }

  @media (hover: hover) {
    .tw\:group-hover\/row\:opacity-100:is(:where(.tw\:group\/row):hover *) {
      opacity: 1;
    }
  }

  .tw\:placeholder\:text-muted-foreground::placeholder {
    color: var(--muted-foreground);
  }

  @media (hover: hover) {
    .tw\:hover\:border-blue-400:hover {
      border-color: var(--tw-color-blue-400);
    }

    .tw\:hover\:border-primary\/30:hover {
      border-color: var(--primary);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:border-primary\/30:hover {
        border-color: color-mix(in oklab, var(--primary) 30%, transparent);
      }
    }

    .tw\:hover\:bg-accent:hover, .tw\:hover\:bg-accent\/40:hover {
      background-color: var(--accent);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-accent\/40:hover {
        background-color: color-mix(in oklab, var(--accent) 40%, transparent);
      }
    }

    .tw\:hover\:bg-accent\/60:hover {
      background-color: var(--accent);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-accent\/60:hover {
        background-color: color-mix(in oklab, var(--accent) 60%, transparent);
      }
    }

    .tw\:hover\:bg-blue-700:hover {
      background-color: var(--tw-color-blue-700);
    }

    .tw\:hover\:bg-border\/60:hover {
      background-color: var(--border);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-border\/60:hover {
        background-color: color-mix(in oklab, var(--border) 60%, transparent);
      }
    }

    .tw\:hover\:bg-destructive\/10:hover {
      background-color: var(--destructive);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-destructive\/10:hover {
        background-color: color-mix(in oklab, var(--destructive) 10%, transparent);
      }
    }

    .tw\:hover\:bg-destructive\/90:hover {
      background-color: var(--destructive);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-destructive\/90:hover {
        background-color: color-mix(in oklab, var(--destructive) 90%, transparent);
      }
    }

    .tw\:hover\:bg-muted:hover, .tw\:hover\:bg-muted\/30:hover {
      background-color: var(--muted);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-muted\/30:hover {
        background-color: color-mix(in oklab, var(--muted) 30%, transparent);
      }
    }

    .tw\:hover\:bg-muted\/70:hover {
      background-color: var(--muted);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-muted\/70:hover {
        background-color: color-mix(in oklab, var(--muted) 70%, transparent);
      }
    }

    .tw\:hover\:bg-primary\/10:hover {
      background-color: var(--primary);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-primary\/10:hover {
        background-color: color-mix(in oklab, var(--primary) 10%, transparent);
      }
    }

    .tw\:hover\:bg-primary\/90:hover {
      background-color: var(--primary);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-primary\/90:hover {
        background-color: color-mix(in oklab, var(--primary) 90%, transparent);
      }
    }

    .tw\:hover\:bg-secondary\/80:hover {
      background-color: var(--secondary);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-secondary\/80:hover {
        background-color: color-mix(in oklab, var(--secondary) 80%, transparent);
      }
    }

    .tw\:hover\:bg-transparent:hover {
      background-color: #0000;
    }

    .tw\:hover\:bg-white\/30:hover {
      background-color: var(--tw-color-white);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:hover\:bg-white\/30:hover {
        background-color: color-mix(in oklab, var(--tw-color-white) 30%, transparent);
      }
    }

    .tw\:hover\:text-accent-foreground:hover {
      color: var(--accent-foreground);
    }

    .tw\:hover\:text-blue-600:hover {
      color: var(--tw-color-blue-600);
    }

    .tw\:hover\:text-blue-700:hover {
      color: var(--tw-color-blue-700);
    }

    .tw\:hover\:text-blue-800:hover {
      color: var(--tw-color-blue-800);
    }

    .tw\:hover\:text-destructive:hover {
      color: var(--destructive);
    }

    .tw\:hover\:text-foreground:hover {
      color: var(--foreground);
    }

    .tw\:hover\:text-muted-foreground:hover {
      color: var(--muted-foreground);
    }

    .tw\:hover\:text-primary:hover {
      color: var(--primary);
    }

    .tw\:hover\:text-red-500:hover {
      color: var(--tw-color-red-500);
    }

    .tw\:hover\:underline:hover {
      text-decoration-line: underline;
    }

    .tw\:hover\:opacity-90:hover {
      opacity: .9;
    }

    .tw\:hover\:opacity-100:hover {
      opacity: 1;
    }

    .tw\:hover\:ring-1:hover {
      --tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
      box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
    }

    .tw\:hover\:ring-blue-400:hover {
      --tw-ring-color: var(--tw-color-blue-400);
    }
  }

  .tw\:focus\:bg-accent:focus {
    background-color: var(--accent);
  }

  .tw\:focus\:text-accent-foreground:focus {
    color: var(--accent-foreground);
  }

  .tw\:focus\:ring-1:focus {
    --tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }

  .tw\:focus\:ring-2:focus {
    --tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }

  .tw\:focus\:ring-ring:focus {
    --tw-ring-color: var(--ring);
  }

  .tw\:focus\:ring-offset-2:focus {
    --tw-ring-offset-width: 2px;
    --tw-ring-offset-shadow: var(--tw-ring-inset, ) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
  }

  .tw\:focus\:outline-none:focus {
    --tw-outline-style: none;
    outline-style: none;
  }

  .tw\:focus-visible\:ring-1:focus-visible {
    --tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }

  .tw\:focus-visible\:ring-ring:focus-visible {
    --tw-ring-color: var(--ring);
  }

  .tw\:focus-visible\:outline-none:focus-visible {
    --tw-outline-style: none;
    outline-style: none;
  }

  .tw\:active\:cursor-grabbing:active {
    cursor: grabbing;
  }

  .tw\:disabled\:pointer-events-none:disabled {
    pointer-events: none;
  }

  .tw\:disabled\:cursor-not-allowed:disabled {
    cursor: not-allowed;
  }

  .tw\:disabled\:opacity-40:disabled {
    opacity: .4;
  }

  .tw\:disabled\:opacity-50:disabled {
    opacity: .5;
  }

  .tw\:disabled\:opacity-60:disabled {
    opacity: .6;
  }

  .tw\:data-\[disabled\]\:pointer-events-none[data-disabled] {
    pointer-events: none;
  }

  .tw\:data-\[disabled\]\:opacity-50[data-disabled] {
    opacity: .5;
  }

  .tw\:data-\[side\=bottom\]\:translate-y-1[data-side="bottom"] {
    --tw-translate-y: var(--tw-spacing);
    translate: var(--tw-translate-x) var(--tw-translate-y);
  }

  .tw\:data-\[side\=left\]\:-translate-x-1[data-side="left"] {
    --tw-translate-x: calc(var(--tw-spacing) * -1);
    translate: var(--tw-translate-x) var(--tw-translate-y);
  }

  .tw\:data-\[side\=right\]\:translate-x-1[data-side="right"] {
    --tw-translate-x: var(--tw-spacing);
    translate: var(--tw-translate-x) var(--tw-translate-y);
  }

  .tw\:data-\[side\=top\]\:-translate-y-1[data-side="top"] {
    --tw-translate-y: calc(var(--tw-spacing) * -1);
    translate: var(--tw-translate-x) var(--tw-translate-y);
  }

  .tw\:data-\[state\=active\]\:border-l-2[data-state="active"] {
    border-left-style: var(--tw-border-style);
    border-left-width: 2px;
  }

  .tw\:data-\[state\=active\]\:border-l-primary[data-state="active"] {
    border-left-color: var(--primary);
  }

  .tw\:data-\[state\=active\]\:bg-primary\/5[data-state="active"] {
    background-color: var(--primary);
  }

  @supports (color: color-mix(in lab, red, red)) {
    .tw\:data-\[state\=active\]\:bg-primary\/5[data-state="active"] {
      background-color: color-mix(in oklab, var(--primary) 5%, transparent);
    }
  }

  .tw\:data-\[state\=checked\]\:bg-primary[data-state="checked"] {
    background-color: var(--primary);
  }

  .tw\:data-\[state\=checked\]\:text-primary-foreground[data-state="checked"] {
    color: var(--primary-foreground);
  }

  .tw\:data-\[state\=indeterminate\]\:bg-primary[data-state="indeterminate"] {
    background-color: var(--primary);
  }

  .tw\:data-\[state\=indeterminate\]\:text-primary-foreground[data-state="indeterminate"] {
    color: var(--primary-foreground);
  }

  .tw\:data-\[state\=open\]\:bg-accent[data-state="open"] {
    background-color: var(--accent);
  }

  .tw\:data-\[state\=open\]\:text-muted-foreground[data-state="open"] {
    color: var(--muted-foreground);
  }

  .tw\:data-\[state\=selected\]\:bg-blue-50[data-state="selected"] {
    background-color: var(--tw-color-blue-50);
  }

  @media (min-width: 40rem) {
    .tw\:sm\:grid-cols-2 {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .tw\:sm\:flex-row {
      flex-direction: row;
    }

    .tw\:sm\:justify-end {
      justify-content: flex-end;
    }

    :where(.tw\:sm\:space-x-2 > :not(:last-child)) {
      --tw-space-x-reverse: 0;
      margin-inline-start: calc(calc(var(--tw-spacing) * 2) * var(--tw-space-x-reverse));
      margin-inline-end: calc(calc(var(--tw-spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
    }

    .tw\:sm\:rounded-lg {
      border-radius: var(--radius);
    }

    .tw\:sm\:text-left {
      text-align: left;
    }
  }

  @media (min-width: 64rem) {
    .tw\:lg\:flex {
      display: flex;
    }

    .tw\:lg\:w-\[300px\] {
      width: 300px;
    }

    .tw\:lg\:flex-nowrap {
      flex-wrap: nowrap;
    }

    :where(.tw\:lg\:space-x-6 > :not(:last-child)) {
      --tw-space-x-reverse: 0;
      margin-inline-start: calc(calc(var(--tw-spacing) * 6) * var(--tw-space-x-reverse));
      margin-inline-end: calc(calc(var(--tw-spacing) * 6) * calc(1 - var(--tw-space-x-reverse)));
    }
  }

  @media (min-width: 80rem) {
    .tw\:xl\:col-start-1 {
      grid-column-start: 1;
    }

    .tw\:xl\:col-start-2 {
      grid-column-start: 2;
    }

    .tw\:xl\:row-start-1 {
      grid-row-start: 1;
    }

    .tw\:xl\:row-start-2 {
      grid-row-start: 2;
    }

    .tw\:xl\:row-start-3 {
      grid-row-start: 3;
    }

    .tw\:xl\:grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,1fr\)\] {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }

    .tw\:xl\:flex-row {
      flex-direction: row;
    }
  }

  @media (prefers-color-scheme: dark) {
    .tw\:dark\:bg-blue-950 {
      background-color: var(--tw-color-blue-950);
    }

    .tw\:dark\:bg-neutral-800 {
      background-color: var(--tw-color-neutral-800);
    }

    .tw\:dark\:bg-red-950\/20 {
      background-color: var(--tw-color-red-950);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:dark\:bg-red-950\/20 {
        background-color: color-mix(in oklab, var(--tw-color-red-950) 20%, transparent);
      }
    }

    .tw\:dark\:text-red-400 {
      color: var(--tw-color-red-400);
    }

    @media (hover: hover) {
      .tw\:dark\:hover\:text-blue-400:hover {
        color: var(--tw-color-blue-400);
      }
    }

    .tw\:dark\:data-\[state\=selected\]\:bg-blue-950\/30[data-state="selected"] {
      background-color: var(--tw-color-blue-950);
    }

    @supports (color: color-mix(in lab, red, red)) {
      .tw\:dark\:data-\[state\=selected\]\:bg-blue-950\/30[data-state="selected"] {
        background-color: color-mix(in oklab, var(--tw-color-blue-950) 30%, transparent);
      }
    }
  }

  .tw\:\[\&_svg\]\:pointer-events-none svg {
    pointer-events: none;
  }

  .tw\:\[\&_svg\]\:size-4 svg {
    width: calc(var(--tw-spacing) * 4);
    height: calc(var(--tw-spacing) * 4);
  }

  .tw\:\[\&_svg\]\:shrink-0 svg {
    flex-shrink: 0;
  }

  .tw\:\[\&_tr\]\:border-b-2 tr {
    border-bottom-style: var(--tw-border-style);
    border-bottom-width: 2px;
  }

  .tw\:\[\&_tr\]\:border-table-header-border tr {
    border-color: var(--table-header-border);
  }

  .tw\:\[\&_tr\:last-child\]\:border-0 tr:last-child {
    border-style: var(--tw-border-style);
    border-width: 0;
  }

  .tw\:\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role="checkbox"]) {
    padding-right: 0;
  }

  .tw\:\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\] > [role="checkbox"] {
    --tw-translate-y: 2px;
    translate: var(--tw-translate-x) var(--tw-translate-y);
  }

  .tw\:\[\&\>span\]\:line-clamp-1 > span {
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    display: -webkit-box;
    overflow: hidden;
  }

  .tw\:\[\&\>svg\]\:size-4 > svg {
    width: calc(var(--tw-spacing) * 4);
    height: calc(var(--tw-spacing) * 4);
  }

  .tw\:\[\&\>svg\]\:shrink-0 > svg {
    flex-shrink: 0;
  }

  .tw\:\[\&\>tr\]\:last\:border-b-0 > tr:last-child {
    border-bottom-style: var(--tw-border-style);
    border-bottom-width: 0;
  }
}

:root {
  --background: oklch(100% 0 0);
  --foreground: oklch(14.5% .005 285);
  --card: oklch(100% 0 0);
  --card-foreground: oklch(14.5% .005 285);
  --popover: oklch(100% 0 0);
  --popover-foreground: oklch(14.5% .005 285);
  --primary: oklch(20.5% .006 285);
  --primary-foreground: oklch(98.5% 0 0);
  --secondary: oklch(96.5% .002 285);
  --secondary-foreground: oklch(20.5% .006 285);
  --muted: oklch(96.5% .002 285);
  --muted-foreground: oklch(55.6% .01 285);
  --accent: oklch(96.5% .002 285);
  --accent-foreground: oklch(20.5% .006 285);
  --destructive: oklch(57.7% .245 27.325);
  --destructive-foreground: oklch(98.5% 0 0);
  --border: oklch(92.2% .004 285);
  --input: oklch(92.2% .004 285);
  --ring: oklch(70.8% .01 285);
  --table-header-bg: oklch(82% .008 285);
  --table-header-border: oklch(65% .012 285);
  --table-header-text: oklch(25% .015 285);
  --table-row-border: oklch(85% .006 285);
  --table-row-base: oklch(95% .004 285);
  --table-row-stripe: oklch(88% .006 285);
  --table-row-hover: oklch(100% 0 0);
  --radius: .5rem;
  --sidebar-background: oklch(98.5% 0 0);
  --sidebar-foreground: oklch(14.5% .005 285);
  --sidebar-border: oklch(92.2% .004 285);
  --sidebar-accent: oklch(96.5% .002 285);
  --sidebar-accent-foreground: oklch(20.5% .006 285);
  --sidebar-primary: oklch(20.5% .006 285);
  --sidebar-primary-foreground: oklch(98.5% 0 0);
}

.jbkb-table-body > tr:not([data-group-header]) {
  border-bottom: 1px solid #e5e7eb;
  background-color: #fff !important;
}

.jbkb-table-body > tr:hover:not([data-group-header]) {
  background-color: #fafafa !important;
}

@property --tw-border-spacing-x {
  syntax: "<length>";
  inherits: false;
  initial-value: 0;
}

@property --tw-border-spacing-y {
  syntax: "<length>";
  inherits: false;
  initial-value: 0;
}

@property --tw-translate-x {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}

@property --tw-translate-y {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}

@property --tw-translate-z {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}

@property --tw-space-y-reverse {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}

@property --tw-space-x-reverse {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}

@property --tw-border-style {
  syntax: "*";
  inherits: false;
  initial-value: solid;
}

@property --tw-leading {
  syntax: "*";
  inherits: false
}

@property --tw-font-weight {
  syntax: "*";
  inherits: false
}

@property --tw-tracking {
  syntax: "*";
  inherits: false
}

@property --tw-ordinal {
  syntax: "*";
  inherits: false
}

@property --tw-slashed-zero {
  syntax: "*";
  inherits: false
}

@property --tw-numeric-figure {
  syntax: "*";
  inherits: false
}

@property --tw-numeric-spacing {
  syntax: "*";
  inherits: false
}

@property --tw-numeric-fraction {
  syntax: "*";
  inherits: false
}

@property --tw-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}

@property --tw-shadow-color {
  syntax: "*";
  inherits: false
}

@property --tw-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}

@property --tw-inset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}

@property --tw-inset-shadow-color {
  syntax: "*";
  inherits: false
}

@property --tw-inset-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}

@property --tw-ring-color {
  syntax: "*";
  inherits: false
}

@property --tw-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}

@property --tw-inset-ring-color {
  syntax: "*";
  inherits: false
}

@property --tw-inset-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}

@property --tw-ring-inset {
  syntax: "*";
  inherits: false
}

@property --tw-ring-offset-width {
  syntax: "<length>";
  inherits: false;
  initial-value: 0;
}

@property --tw-ring-offset-color {
  syntax: "*";
  inherits: false;
  initial-value: #fff;
}

@property --tw-ring-offset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}

@property --tw-duration {
  syntax: "*";
  inherits: false
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes pulse {
  50% {
    opacity: .5;
  }
}
