/*
 * Koboli — the dashboard's one stylesheet.
 *
 * The look is the Koboli design handoff (docs/koboli-handoff/, 2026-10-26):
 * a Sea header with a wave under it, paper behind white cards with a warm
 * line border, Fredoka for titles and big numbers, Inter for everything else,
 * IBM Plex Mono for the small uppercase labels. Every value below was read
 * from the inline styles of the handoff's .dc.html screens, and each block
 * names the file it came from. Where two screens disagree, the block says
 * which one won; where the handoff has nothing, it says so.
 *
 * Colour means status. Green = subscribed, delivered, fine. Sun = waiting or
 * worth a look. Stamp red = bounced, blocking. Grey = closed, off. Sea =
 * interactive, and in progress. Sun is never text (it fails contrast), so a
 * warning is a sun dot or a sun border beside ink text. The tones are
 * Koboli_UI::TONES.
 *
 * One recipe per part. Change a rule in place rather than adding a second
 * class for something already here; when parts share a look, they share a
 * rule; every colour, radius and size the handoff names is a token in :root.
 * tools/check-css.mjs fails on a class nothing draws, a class drawn with no
 * rule, two rules with the same declarations, a colour outside :root, a
 * style="" or a colour in the dashboard's markup, a class or custom
 * property without the ko- prefix, and a token used but never defined, or
 * defined but never used.
 */

@font-face { font-family: 'Fredoka'; font-style: normal; font-weight: 600; font-display: swap; src: url('fonts/fredoka-600-latin.woff2') format('woff2'); }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400 600; font-display: swap; src: url('fonts/inter-latin.woff2') format('woff2'); }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/plex-mono-400-latin.woff2') format('woff2'); }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500; font-display: swap; src: url('fonts/plex-mono-500-latin.woff2') format('woff2'); }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 600; font-display: swap; src: url('fonts/plex-mono-600-latin.woff2') format('woff2'); }

/* tokens/tokens.css, under Koboli's own prefix. */
:root {
	--ko-sea: #1E5F74;
	--ko-sea-dark: #163A4A;
	--ko-ink: #1B2A3A;
	--ko-sun: #F2B33D;
	--ko-stamp: #C8432F;
	--ko-paper: #F7F3EA;
	--ko-paper-deep: #E9E4D8;
	--ko-white: #FFFFFF;
	--ko-line: #E4DCCB;
	--ko-line-strong: #D9D1BF;
	--ko-muted: #5A6573;
	--ko-body: #3D4A59;
	--ko-grey: #8A94A0;
	--ko-track: #EFEAE0;
	--ko-green: #2F7D4F;
	--ko-green-tint: #DCEBE3;
	--ko-green-text: #1F5C3A;
	--ko-grey-tint: #E7E3DA;
	--ko-red-tint: #F6DED9;
	--ko-red-text: #8E2A1C;
	/* Not in tokens.css: Contacts.dc.html's destructive border and the fifth
	   avatar tint (also an Audiences card colour); wood and wood-dark (the
	   cubby wall) are the handoff's. */
	--ko-red-line: #E2B7AE;
	--ko-indigo: #3B4C8C;
	--ko-wood: #8A6A45;
	--ko-wood-dark: #5E4630;
	--ko-display: 'Fredoka', Arial, sans-serif;
	--ko-sans: 'Inter', system-ui, Helvetica, Arial, sans-serif;
	--ko-mono: 'IBM Plex Mono', ui-monospace, monospace;
	--ko-radius-sm: 10px;
	--ko-radius-md: 14px;
	--ko-radius-lg: 16px;
	--ko-radius-pill: 999px;
	--ko-touch: 44px;
	--ko-shadow-card: 0 2px 10px rgba(27, 42, 58, 0.08);
	/* The sign-in page (Login.dc.html): text on Sea, quieter text on Sea,
	   and the card lifted off it. */
	--ko-on-sea: #E4EEF1;
	--ko-on-sea-quiet: #C9D2DC;
	--ko-shadow-lift: 0 20px 50px rgba(0, 0, 0, 0.25);
	/* The page column and its side gutter (every app screen), and a panel's
	   padding (Contacts and Preview asides). The handoff's column is about
	   1240px; Dennis chose up to 1920px for the dashboard (2026-10-30), since
	   the smallest screen it is used on is 2560px wide. The sign-in page
	   keeps the mock's (Sign-in, below). */
	--ko-column: 1920px;
	--ko-gutter: 32px;
	--ko-panel-pad: 22px;
	/* A field's label to its control to its help (Preview aside). */
	--ko-field-gap: 6px;
	/* The preview frame: Desktop and Mobile (HANDOFF.md §3, Preview.dc.html). */
	--ko-frame: 600px;
	--ko-frame-mobile: 375px;
}

/* ── Base — every screen: paper, ink, Inter 14px at the font's own line
 *    height (no screen sets one); prose that wraps gets 1.5, as the mocks'
 *    help text does. Links sea, hover sea-dark. ──────────────────────── */
* { box-sizing: border-box; }
body.ko-app { background: var(--ko-paper); color: var(--ko-ink); font: 400 14px var(--ko-sans); -webkit-font-smoothing: antialiased; }
a { color: var(--ko-sea); text-decoration: none; }
a:hover, .ko-link:hover { color: var(--ko-sea-dark); }
button, input, select, textarea { font: inherit; color: inherit; }
html, body, h1, h2, h3, p { margin: 0; }
p, .ko-empty, .ko-checklist { line-height: 1.5; }
:focus-visible, .ko-segmented label:has(input:focus-visible), .ko-letter:has(.ko-letter__name:focus-visible) { outline: 2px solid var(--ko-sea); outline-offset: 2px; }
/* Audiences.dc.html's visually hidden search label. */
.ko-hide { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ── Header — Audiences.dc.html <header>: Sea, paper text; the page column,
 *    padding 14px 32px, wraps, gap 14. Mark 32px, gap 10, Fredoka 600 24px.
 *    Nav 14px, gap 4; each 10px 16px, a pill; the current one paper, ink,
 *    600. The mock's nav is 37px tall; Dennis chose HANDOFF.md §4's 44px
 *    touch target for the nav, the brand switcher and the person's
 *    initial (2026-10-26). The wave: 12px tall, full width, paper behind. ─────── */
.ko-header { background: var(--ko-sea); color: var(--ko-paper); }
.ko-header__in, .ko-page, .ko-signin__main { max-width: var(--ko-column); margin: 0 auto; }
.ko-header__in { padding: 14px var(--ko-gutter); display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px; }
.ko-header__group { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
/* The nav wraps inside its group, so the person's initial stays beside it on a phone. */
.ko-header__group:last-child { flex-wrap: nowrap; }
.ko-header :focus-visible { outline-color: var(--ko-paper); }
.ko-wordmark { display: flex; align-items: center; gap: 10px; font: 600 24px var(--ko-display); }
.ko-nav { display: flex; flex-wrap: wrap; gap: 4px; }
.ko-nav a, .ko-brand select { min-height: var(--ko-touch); padding: 10px 16px; border-radius: var(--ko-radius-pill); color: var(--ko-paper); }
.ko-nav a { display: inline-flex; align-items: center; }
.ko-nav a:hover { background: var(--ko-sea-dark); }
.ko-nav a[aria-current="page"] { color: var(--ko-ink); background: var(--ko-paper); font-weight: 600; }
.ko-wave { display: block; width: 100%; height: 12px; color: var(--ko-sea); background: var(--ko-paper); }

/* Not in the handoff, whose header has no brand or person in it. The brand
 * switcher is a nav link's pill on sea-dark, its arrow drawn in paper; a
 * field's select has the same arrow in ink (Fields, below). The
 * not-live tag is a warn pill; the person's initial, which opens Your
 * sign-in, is the avatar (below) in ink. */
form.ko-brand, .ko-select { position: relative; }
form.ko-brand::after, .ko-select::after { content: ""; position: absolute; top: 50%; right: 16px; width: 6px; height: 6px; margin-top: -5px; border-right: 1.6px solid var(--ko-paper); border-bottom: 1.6px solid var(--ko-paper); transform: rotate(45deg); pointer-events: none; }
.ko-brand select { border: 0; background: var(--ko-sea-dark); }
.ko-brand option { color: var(--ko-ink); background: var(--ko-white); }
.ko-account { width: var(--ko-touch); height: var(--ko-touch); flex: none; display: grid; place-items: center; }
.ko-account:hover .ko-avatar { background: var(--ko-sea-dark); }

/* ── People — Contacts.dc.html: the avatar is a 34px circle, the initial in
 *    Fredoka 600 14px, paper on one of five tints fixed per person (sea,
 *    stamp, indigo, green, wood); the header's own is ink. The name cell:
 *    avatar, then the name 600 over the email 12px muted, gap 12, at least
 *    40px tall. ───────────────────────────────────────────────────────────── */
.ko-avatar { width: 34px; height: 34px; flex: none; border-radius: 50%; display: grid; place-items: center; font: 600 14px var(--ko-display); color: var(--ko-paper); background: var(--ko-ink); }
.ko-avatar--sea { background: var(--ko-sea); }
.ko-avatar--stamp { background: var(--ko-stamp); }
.ko-avatar--indigo { background: var(--ko-indigo); }
.ko-avatar--green { background: var(--ko-green); }
.ko-avatar--wood { background: var(--ko-wood); }
.ko-person { display: flex; align-items: center; gap: 12px; min-height: 40px; color: var(--ko-ink); }

/* ── Page — Contacts.dc.html and Preview.dc.html <main>: the page column,
 *    padding 28px 32px 40px, gap 20 (Audiences has 32px and 24; two screens
 *    to one). Onboarding is the same page, narrower. ─────────────────────── */
.ko-page { padding: 28px var(--ko-gutter) 40px; display: flex; flex-direction: column; gap: 20px; }
.ko-page--narrow { max-width: 760px; }

/* ── The uppercase mono label — Plex Mono, uppercase, muted. 11px .14em
 *    for panel and section labels (Contacts, Preview asides); 12px for the
 *    kicker over a title (Audiences); .12em and 500 for table heads, .12em
 *    for stat labels (Contacts, Audiences). ─────────────────────────────── */
.ko-kicker, .ko-panel__head h2, .ko-panel h3, .ko-table th, .ko-stat > span, .ko-block__type, .ko-card__tag {
	font: 400 11px var(--ko-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ko-muted); }
.ko-kicker { font-size: 12px; }
.ko-table th, .ko-stat > span { letter-spacing: 0.12em; }

/* ── Page header — Audiences.dc.html: wraps, bottom-aligned, gap 16; title
 *    column gap 4; h1 Fredoka 600 38px (Compose has 36, Preview 34 — one
 *    size); the line under it 14px muted (Contacts); actions gap 10 — and
 *    inside a panel 8 (Contacts aside). A row of buttons in a panel sits at
 *    the panel's gap, without the aside's extra 4px (Dennis, 2026-10-27).
 *    Stat tiles: 4 columns, gap 12; number Fredoka 600 28px — green text
 *    when good, red text when bad. ──────────────────────────────────────── */
.ko-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 16px; }
.ko-head__title { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ko-head h1 { font: 600 38px var(--ko-display); overflow-wrap: anywhere; }
.ko-head__sub, .ko-signin__title div { font-size: 14px; color: var(--ko-muted); }
.ko-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.ko-panel .ko-acts { gap: 8px; }
.ko-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.ko-stat strong { font: 600 28px var(--ko-display); }
.ko-stat--good strong { color: var(--ko-green-text); }
.ko-stat--bad strong { color: var(--ko-red-text); }

/* ── Buttons — Audiences.dc.html. Primary: sun, ink, Fredoka 600 15px,
 *    11px 20px. Secondary: white, sea text, 1.5px sea border, Inter 600
 *    14px, 11px 18px. Both pills, at least the touch target tall, and no
 *    icons. Inside a panel (Contacts and Preview asides) the secondary is
 *    smaller, 13px, 10px 14px (Preview's 10px 12px: one size, Dennis
 *    2026-10-27), and the primary spans the panel, 16px, 12px 20px, 48px
 *    tall (Preview's Send). (Compose's primary is 16px / 12px 22px;
 *    Audiences and Contacts agree on 15px.) No hover is drawn; the
 *    secondary darkens to sea-dark like a link, the primary dims a little.
 *    Destructive: Contacts' "Close cubby", red text on a red-line border.
 *    A segmented choice's options are this pill too (below), and a file
 *    field's "Choose File" is the small secondary (Dennis). ──────────────── */
.ko-solid, .ko-outline, .ko-segmented label, .ko-field input::file-selector-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: var(--ko-touch); border-radius: var(--ko-radius-pill); white-space: nowrap; cursor: pointer; text-decoration: none; }
.ko-solid { padding: 11px 20px; border: 0; font: 600 15px var(--ko-display); color: var(--ko-ink); background: var(--ko-sun); }
.ko-solid:hover { filter: brightness(0.95); }
.ko-outline, .ko-field input::file-selector-button { padding: 11px 18px; border: 1.5px solid var(--ko-sea); font: 600 14px var(--ko-sans); color: var(--ko-sea); background: var(--ko-white); }
.ko-outline:hover, .ko-field input::file-selector-button:hover { color: var(--ko-sea-dark); border-color: var(--ko-sea-dark); }
.ko-panel .ko-outline, .ko-field input::file-selector-button { padding: 10px 14px; font-size: 13px; }
.ko-panel .ko-solid, .ko-signin__card .ko-solid, .ko-signin__card .ko-outline { width: 100%; min-height: 48px; padding: 12px 20px; font-size: 16px; }
.ko-outline--bad { color: var(--ko-red-text); border-color: var(--ko-red-line); }
.ko-outline--bad:hover { color: var(--ko-red-text); border-color: var(--ko-red-text); }
.ko-link { display: inline-flex; align-items: center; gap: 5px; padding: 0; border: 0; background: none; font-size: 13px; font-weight: 600; color: var(--ko-sea); white-space: nowrap; cursor: pointer; }
form.ko-inline { display: contents; }

/* ── Fields — Preview.dc.html aside: label 13px 600, gap 6; control 14px,
 *    10px 12px, line-strong border, radius sm, at least the touch target;
 *    help 12px muted. The search box is Contacts.dc.html's: the same control
 *    as a pill, 11px 16px, 300px. ──────────────────────────────────────── */
.ko-fields { display: flex; flex-wrap: wrap; gap: 16px 12px; }
/* A button beside a field sits at the bottom of the row, by the input; so
 * a row with a button has its help under the row (Koboli_UI::field_row()),
 * at the field's own gap and leading. */
.ko-fields > div { align-self: flex-end; }
.ko-fields + .ko-quiet { margin-top: var(--ko-field-gap); }
.ko-fields > .ko-field { flex: 1 1 180px; }
.ko-fields > .ko-field--sm { flex: 0 0 110px; }
.ko-field { display: flex; flex-direction: column; gap: var(--ko-field-gap); min-width: 0; font-size: 13px; font-weight: 600; }
.ko-field input, .ko-field select, .ko-field textarea, .ko-search input { width: 100%; min-height: var(--ko-touch); padding: 10px 12px; border: 1px solid var(--ko-line-strong); border-radius: var(--ko-radius-sm); background: var(--ko-white); font-size: 14px; font-weight: 400; color: var(--ko-ink); }
/* Selects draw no box of the browser's own, which Safari otherwise keeps
 * (short, grey, ignoring the padding), and take the brand switcher's arrow;
 * number fields have no spin buttons, which Safari shows on every one and
 * which look like a select's arrows. */
.ko-brand select, .ko-field select { appearance: none; padding-right: 36px; cursor: pointer; }
.ko-select { display: block; }
.ko-select::after { border-color: var(--ko-ink); }
.ko-field input[type=number] { appearance: textfield; }
.ko-field input::-webkit-inner-spin-button, .ko-field input::-webkit-outer-spin-button { appearance: none; margin: 0; }
.ko-field textarea { min-height: 88px; resize: vertical; }
.ko-field textarea.ko-code { min-height: 360px; white-space: pre; overflow-wrap: normal; overflow-x: auto; }
.ko-field textarea.ko-code, .ko-secret { font: 400 12px/1.5 var(--ko-mono); }
.ko-field input[type=color] { padding: 4px; }
/* A file field is its button and the file's name, 13px, with no box around
 * them (Dennis). */
.ko-field input[type=file] { padding: 0; border: 0; background: none; font-size: 13px; }
.ko-field input::file-selector-button { margin-right: 12px; }
/* Help and errors under a field, or under a field's row, the sign-in card's
 * notes and an empty state's line are 12px; the help is not bold. */
.ko-field .ko-quiet, .ko-field .ko-warnline, .ko-fields + .ko-quiet, .ko-signin__card > .ko-quiet, .ko-empty .ko-quiet { font-size: 12px; }
.ko-field .ko-quiet { font-weight: 400; }
.ko-field--error input, .ko-field--error select, .ko-field--error textarea { border-color: var(--ko-stamp); }
/* A field with an aside: the label and the link share a line, on its
 * baseline. The link is the touch target's height, reaching into the space
 * above and below (6px each at 44px), so the line stays 32px
 * (Login.dc.html's) and the label by its input. */
.ko-field__top { display: flex; justify-content: space-between; align-items: baseline; }
.ko-field__top .ko-link { min-height: var(--ko-touch); margin: calc((32px - var(--ko-touch)) / 2) 0; }
.ko-search { width: 300px; max-width: 100%; }
.ko-search input { padding: 11px 16px; border-radius: var(--ko-radius-pill); }

/* ── Surfaces — white on paper with the line border, their content in a
 *    column (a letter's in a row, below). Stat tile (Audiences): padding 16px 18px, radius 12 (the mock's
 *    own; no token), gap 4. Panel (Contacts and Preview asides): padding 22,
 *    radius md, gap 16; its title is the mono label; a section inside
 *    starts after a dashed line-strong rule (Contacts, above "MAIL
 *    HISTORY"). Card (Audiences): padding 20, radius lg, gap 14. Editor
 *    block — not in the handoff: a paper well inside its panel, radius sm,
 *    headed like a panel. ────────────────────────────────────────────────── */
.ko-stat, .ko-panel, .ko-card, .ko-block, .ko-letter { min-width: 0; border: 1px solid var(--ko-line); background: var(--ko-white); }
.ko-stat, .ko-panel, .ko-card, .ko-block { display: flex; flex-direction: column; }
.ko-stat { gap: 4px; padding: 16px 18px; border-radius: 12px; }
.ko-panel { gap: 16px; padding: var(--ko-panel-pad); border-radius: var(--ko-radius-md); overflow-x: auto; }
.ko-card { gap: 14px; padding: 20px; border-radius: var(--ko-radius-lg); }
.ko-block { gap: 12px; padding: 14px 16px; }
/* The paper inset: an editor block, the pre-send checks, a secret. */
.ko-block, .ko-blockers, .ko-secret { border-radius: var(--ko-radius-sm); background: var(--ko-paper); }
.ko-panel__head { display: flex; align-items: center; gap: 10px; }
.ko-panel__right { margin-left: auto; display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ko-muted); white-space: nowrap; }
.ko-panel h3, .ko-card__foot { border-top: 1px dashed var(--ko-line-strong); }
.ko-panel h3 { padding-top: 16px; }

/* ── Layouts — Contacts.dc.html: main and aside wrap side by side, gap 20,
 *    the wide one taking the room (flex 999 1 620px / 1 1 280px). Here the
 *    narrow one comes first and holds the editors: it starts at 356px and
 *    takes a quarter of the spare room, so it is about 416px in the full
 *    column and the two stay side by side down to a 1000px window (Dennis,
 *    2026-10-27: 356 + 20 + 560 + the gutters is 1000). Audiences cards:
 *    auto-fill from 270px, gap 16.
 *    A campaign being edited has three columns from a 1600px window
 *    (Dennis, 2026-10-30): its settings (Message, Who gets it, Save), the
 *    email's HTML or blocks, and the preview with Test and send. The
 *    settings keep the narrow column's 356px, so their fields stand one a
 *    row, and the preview the frame's width in its well; the HTML or blocks
 *    take the rest (Dennis: "give more to the html"). The editor form
 *    lays out as if it weren't there (ko-inline), so its two halves are
 *    columns of their own. From 1000px the settings sit over the email's
 *    content beside the preview; below that, one column. */
.ko-two { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 20px; }
.ko-two > :first-child { flex: 1 1 356px; }
.ko-two > :last-child { flex: 3 1 560px; }
.ko-two--compose { display: grid; grid-template: "settings" "content" "preview" / minmax(0, 1fr); }
.ko-two--compose > :last-child { grid-area: preview; }
.ko-two__settings { grid-area: settings; }
.ko-two__content { grid-area: content; }
@media (min-width: 1000px) {
	.ko-two--compose { grid-template: "settings preview" auto "content preview" 1fr / minmax(356px, 1fr) minmax(560px, 3fr); }
}
@media (min-width: 1600px) {
	.ko-two--compose { grid-template: "settings content preview" / 356px minmax(0, 1fr) calc(var(--ko-frame) + 32px); }
}
.ko-band { min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.ko-three { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; align-items: start; }
.ko-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: 16px; align-items: start; }

/* ── Tables — Contacts.dc.html: 14px. Head: the mono label, 14px 12px, a
 *    line rule under, on one line. Cells: 12px, track rules; the first
 *    column 18px both sides, the last 18px on the right; every column
 *    left-aligned; a second line 12px muted; numbers and dates mono 13px on
 *    one line, dates muted (LAST
 *    OPENED); the footer ("Showing N of M") 13px muted. In a panel the table
 *    runs to the card's edges, as the mock's table card does; one with a
 *    head is at least 640px wide and scrolls inside its card. ───────────── */
.ko-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.ko-panel > .ko-table { width: calc(100% + 2 * var(--ko-panel-pad)); margin: 0 calc(-1 * var(--ko-panel-pad)); }
.ko-panel > .ko-table:first-child { margin-top: calc(-1 * var(--ko-panel-pad)); }
.ko-panel > .ko-table:last-child { margin-bottom: calc(-1 * var(--ko-panel-pad)); }
.ko-table:has(> thead) { min-width: 640px; }
.ko-table th, .ko-table td { padding: 12px; text-align: left; vertical-align: middle; border-bottom: 1px solid var(--ko-track); }
.ko-table th { padding-top: 14px; padding-bottom: 14px; border-bottom-color: var(--ko-line); font-weight: 500; white-space: nowrap; }
.ko-table :is(th, td):first-child { padding-left: 18px; padding-right: 18px; }
.ko-table :is(th, td):last-child { padding-right: 18px; }
.ko-table > :last-child > tr:last-child > td { border-bottom: 0; }
.ko-table td strong, .ko-brand { font-weight: 600; }
/* The second line under a name, a step or a history line. */
.ko-sub { display: block; margin-top: 2px; font-size: 12px; color: var(--ko-muted); }
.ko-table td.ko-num, .ko-date { font: 400 13px var(--ko-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ko-date { color: var(--ko-muted); }

/* ── Status pills — Contacts.dc.html: a pill, 4px 10px, 12px 600, gap 6, a
 *    7px dot. Subscribed: green tint, green text, green dot. Closed: grey
 *    tint, body text, grey dot. Returned: red tint, red text, stamp dot.
 *    Compose.dc.html's "Held at the counter": paper, ink, a sun dot — and
 *    the same with a sea dot for in progress. (Compose's pills are 13px with
 *    a 9px dot; one size, the table's, Dennis.) Flashes are not in the
 *    handoff and take the pills' tints at radius sm; a warning flash is white
 *    with a sun border; a wrapped one keeps its icon by the first line
 *    (Dennis). ─────────────────────────────────────────────────────────── */
.ko-pill { flex: none; display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: var(--ko-radius-pill); font-size: 12px; font-weight: 600; white-space: nowrap; }
.ko-pill--good, .ko-flash { background: var(--ko-green-tint); color: var(--ko-green-text); }
.ko-pill--good, .ko-dotline--good { --ko-dot: var(--ko-green); }
.ko-pill--off { background: var(--ko-grey-tint); color: var(--ko-body); }
.ko-pill--off, .ko-dotline--off { --ko-dot: var(--ko-grey); }
.ko-pill--bad, .ko-flash--bad { background: var(--ko-red-tint); color: var(--ko-red-text); }
.ko-pill--bad, .ko-dotline--bad { --ko-dot: var(--ko-stamp); }
.ko-pill--warn, .ko-pill--info { background: var(--ko-paper); color: var(--ko-ink); }
.ko-pill--warn, .ko-dotline--warn { --ko-dot: var(--ko-sun); }
.ko-pill--info, .ko-dotline--info { --ko-dot: var(--ko-sea); }
.ko-flash { display: flex; align-items: flex-start; gap: 8px; padding: 12px 16px; border: 1.5px solid transparent; border-radius: var(--ko-radius-sm); font-size: 14px; font-weight: 600; }
.ko-flash svg { flex: none; margin-top: 1.5px; }
.ko-flash--warn { background: var(--ko-white); color: var(--ko-ink); border-color: var(--ko-sun); }

/* The dot, for every status dot: 8px (Preview's checks, Contacts' mail
 * history), 7px in a pill. Red unless the line says otherwise. */
.ko-pill::before, .ko-dotline::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--ko-dot, var(--ko-stamp)); }
.ko-pill::before { width: 7px; height: 7px; }

/* ── Dot lines — a line of text after an 8px dot, the dot 5px down.
 *    Pre-send checks (Preview.dc.html): gap 8; a paper box, radius sm, 12px
 *    14px, gap 6, 13px; green when fine. The mock shows no blocking line; a
 *    block is stamp red, and a line that only matters once live is grey.
 *    Mail history (Contacts.dc.html): gap 10; a list, gap 12, 13px, the date
 *    under each in mono 11px muted, 2px down. The setup checklist is the
 *    same list, numbered. ─────────────────────────────────────────────────── */
.ko-dotline { display: flex; align-items: flex-start; gap: 8px; }
.ko-dotline::before { margin-top: 5px; }
.ko-blockers { display: flex; flex-direction: column; gap: 6px; padding: 12px 14px; font-size: 13px; }
.ko-history, .ko-steps { margin: 0; display: flex; flex-direction: column; gap: 12px; font-size: 13px; }
.ko-history .ko-dotline, .ko-steps .ko-dotline, .ko-signin__points .ko-dotline { gap: 10px; }
.ko-steps { padding-left: 20px; }
.ko-dotline .ko-date { font-size: 11px; }

/* ── Cards — Audiences.dc.html's audience card (the surface above): the
 *    name Fredoka 600 21px, the line under it 13px muted (gap 4); the foot
 *    after the dashed rule, 12px above, 13px muted, ends apart. ────────── */
.ko-item { display: flex; align-items: center; gap: 12px; }
.ko-item__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.ko-item__name { font: 600 21px var(--ko-display); }
.ko-card__foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 12px; font-size: 13px; color: var(--ko-muted); }
.ko-grow { flex: 1; min-width: 0; }

/* ── Letters — Compose.dc.html's rows: the surface above, radius md,
 *    18px 20px, 10px apart, each the card's item (gap 12, the line 4px
 *    under the name); the name 600 16px ink, the line 13px muted; the pill,
 *    or once sent the small postmark, on the right, and under the words
 *    when they leave it no room (the mock at 420). The whole letter is the
 *    link (Dennis): the name's link stretches over it, above the tilted
 *    postmark, and the letter takes its focus ring; only the pill sits above
 *    the link, so its plain word still shows on hover (Dennis). A long name
 *    breaks rather than widen the page. ─────────────────────────────────── */
.ko-letters { display: flex; flex-direction: column; gap: 10px; }
.ko-letter { position: relative; flex-wrap: wrap; padding: 18px 20px; border-radius: var(--ko-radius-md); }
.ko-letter__name { font-size: 16px; font-weight: 600; color: var(--ko-ink); overflow-wrap: anywhere; }
.ko-letter__name::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.ko-letter__name:focus-visible { outline: 0; }
.ko-letter > .ko-pill { position: relative; z-index: 2; }

/* The on/off toggle — not in the handoff: a round button the size of the
 * touch target. Off: white, line-strong border, grey tick. On: green. */
.ko-check { width: var(--ko-touch); height: var(--ko-touch); flex: none; display: grid; place-items: center; padding: 0; border: 1.5px solid var(--ko-line-strong); border-radius: 50%; background: var(--ko-white); color: var(--ko-grey); cursor: pointer; }
.ko-check:hover { border-color: var(--ko-sea); color: var(--ko-sea); }
.ko-check--on { border-color: var(--ko-green); background: var(--ko-green); color: var(--ko-white); }
.ko-check--on:hover { border-color: var(--ko-green-text); background: var(--ko-green-text); color: var(--ko-white); }

/* ── Facts — Contacts.dc.html aside: 14px rows, gap 8, the label muted and
 *    the value at the right end. ─────────────────────────────────────────── */
.ko-kv { margin: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 16px; font-size: 14px; }
.ko-kv dt { color: var(--ko-muted); }
.ko-kv dd { margin: 0; text-align: right; overflow-wrap: anywhere; }

/* ── Preview — Preview.dc.html. The well: paper-deep, radius lg, 28px 16px,
 *    its content centred in a column, gap 14. The envelope lines and the
 *    frame share the frame's width: --ko-frame, or --ko-frame-mobile. The
 *    envelope is the facts list at 13px, gap 4px 8px, the label 54px wide
 *    and muted, the value 600 and left. The frame is the iframe itself:
 *    white, radius sm, the card shadow; the email in it is the real one, as
 *    tall as the email (assets/preview.js; 900px and scrolling without it).
 *    The device switch (page header): a white pill, line-strong border,
 *    padding 4, gap 4; each choice 14px, 9px 18px, the button pill; an ink
 *    fill, paper text and 600 when chosen, 500 otherwise. The mock's choices
 *    are 40px tall buttons; here they are 44px radios (Dennis, 2026-10-27).
 *    ────────────────────────────────────────────────────────────────────── */
.ko-well { min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 28px 16px; border-radius: var(--ko-radius-lg); background: var(--ko-paper-deep); }
.ko-well > * { width: 100%; max-width: var(--ko-frame); }
.ko-page:has(.ko-segmented input[value="mobile"]:checked) .ko-well > * { max-width: var(--ko-frame-mobile); }
.ko-kv--envelope { grid-template-columns: 54px minmax(0, 1fr); gap: 4px 8px; font-size: 13px; }
.ko-kv--envelope dd { text-align: left; font-weight: 600; }
.ko-preview { display: block; height: 900px; border: 0; border-radius: var(--ko-radius-sm); background: var(--ko-white); box-shadow: var(--ko-shadow-card); }
.ko-segmented { display: inline-flex; gap: 4px; padding: 4px; border: 1px solid var(--ko-line-strong); border-radius: var(--ko-radius-pill); background: var(--ko-white); }
.ko-segmented label { padding: 9px 18px; font-size: 14px; font-weight: 500; }
.ko-segmented label:has(input:checked) { background: var(--ko-ink); color: var(--ko-paper); font-weight: 600; }

/* ── Postmark — HANDOFF §6.1 and Login.dc.html's sent state: a circle with
 *    a 3px stamp ring round the mock's own 92px content box (98px across),
 *    Plex Mono 500 10px .06em uppercase, its first line 14px 600, tilted
 *    -12°. On a letter (Compose.dc.html): a 70px box and a 2.5px ring
 *    (browsers draw it 2px, so 74px across, as the mock renders), its first
 *    line 12px, tilted −14° to −8° by the campaign's id (Koboli_UI::TILTS).
 *    Koboli_UI::postmark(). ─────────────────────────────────────────────── */
.ko-postmark { width: 92px; height: 92px; box-sizing: content-box; flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 3px solid var(--ko-stamp); border-radius: 50%; color: var(--ko-stamp); font: 500 10px var(--ko-mono); letter-spacing: 0.06em; text-transform: uppercase; transform: rotate(-12deg); }
.ko-postmark b { font-size: 14px; font-weight: 600; }
.ko-postmark--small { width: 70px; height: 70px; border-width: 2.5px; }
.ko-postmark--small b { font-size: 12px; }
.ko-postmark--14 { transform: rotate(-14deg); }
.ko-postmark--13 { transform: rotate(-13deg); }
.ko-postmark--12 { transform: rotate(-12deg); }
.ko-postmark--11 { transform: rotate(-11deg); }
.ko-postmark--10 { transform: rotate(-10deg); }
.ko-postmark--9 { transform: rotate(-9deg); }
.ko-postmark--8 { transform: rotate(-8deg); }

/* ── Postbox — HANDOFF §6.6: 168 × 210 (the image's own size, in
 *    postbox()) with its letter at 64, 20 (Login.dc.html); the letter drops into the slot every 3s while the box
 *    wiggles (Postbox.dc.html's keyframes); both stop under reduced motion.
 *    Koboli_UI::postbox(). ──────────────────────────────────────────────── */
.ko-postbox { position: relative; flex: none; }
.ko-postbox__letter { position: absolute; left: 64px; top: 20px; animation: ko-drop 3s ease-in infinite; }
.ko-postbox__box { display: block; transform-origin: 50% 96%; animation: ko-wiggle 3s ease-in-out infinite; }
@keyframes ko-drop { 0% { transform: translateY(-46px); opacity: 0; } 15% { opacity: 1; } 55% { transform: translateY(0); opacity: 1; } 62%, 100% { transform: translateY(6px); opacity: 0; } }
@keyframes ko-wiggle { 0%, 55%, 100% { transform: rotate(0); } 60% { transform: rotate(-2.5deg); } 66% { transform: rotate(2deg); } 72% { transform: rotate(0); } }
@media (prefers-reduced-motion: reduce) {
	.ko-postbox__letter, .ko-postbox__box { animation: none; }
}

/* ── Sign-in — Login.dc.html (docs/koboli-handoff/sign-in): Sea all the
 *    way down, paper text, at least the window's height, the shore at its
 *    foot. The browser's own font smoothing, as the mock (Dennis): the
 *    dashboard's thinner antialiasing makes paper on Sea too light.
 *    Header: the app's, without its wave; 22px above and below, gap 16; the mark
 *    34px, the wordmark 26px; the label at its end Plex Mono 12px .16em in
 *    paper.
 *    The page column is the mock's 1240px, not the dashboard's wider one.
 *    Main: the page column, padding 24px 32px 120px, wrapping, centred, gap
 *    48. The pitch: flex 1 1 440px, wrapping, bottom-aligned, gap 36. Its
 *    words: flex 1 1 300px, a column, gap 18 — the kicker 13px in paper
 *    (Dennis: readable; the mock's sun is 3.83:1), the title Fredoka 600
 *    64px/0.98 at -.01em, the line 18px/1.55 at most
 *    460px in soft Sea text, the points 15px, gap 10, 6px above, each dot
 *    10px from its words and centred on the first line (Dennis: by the
 *    first line when a point wraps). Beside the words, the postbox (above).
 *    The card: flex 0 1 420px, paper, ink, radius 20 (the mock's own; no
 *    token), padding 36, lifted, a column, gap 20. Its title Fredoka 600 30px
 *    over 14px muted, gap 6 (the page header's subtitle); its fields a size
 *    up from the dashboard's, 15px, 12px 14px, radius 12 (the mock's own,
 *    as the stat tile's), 48px tall; its primary the panel primary, 13px 20px, 17px;
 *    "Sign in with a passkey" under it (the mock has no such button: D14,
 *    Q24) an outline at the panel primary's full width, 12px 20px, 16px;
 *    its note 12px muted, as a field's help; its links underlined, not bold,
 *    14px, 10px either side, and "Forgot it?" at the link's own 13px on the
 *    password's line; both the touch target's 44px tall (Dennis), where the
 *    mock's are 37px and 32px — "Forgot it?" without moving the fields.
 *    The sent state: centred, gap 16, 12px above and below; the postmark
 *    (above); its title 28px; its words 14px body. The shore: two waves,
 *    sea-dark behind ink, 90px. The foot: 12px quiet, 14px from the
 *    bottom. ───────────────────────────────────────────────────────────── */
.ko-signin { --ko-column: 1240px; position: relative; overflow: hidden; min-height: 100vh; display: flex; flex-direction: column; background: var(--ko-sea); color: var(--ko-paper); -webkit-font-smoothing: auto; }
.ko-signin .ko-header__in { padding-top: 22px; padding-bottom: 22px; gap: 16px; }
.ko-signin .ko-wordmark { font-size: 26px; }
.ko-signin .ko-wordmark img { width: 34px; height: 34px; }
.ko-signin .ko-kicker { letter-spacing: 0.16em; color: inherit; }
.ko-signin__main { flex: 1; width: 100%; padding: 24px var(--ko-gutter) 120px; display: flex; flex-wrap: wrap; align-items: center; gap: 48px; }
.ko-signin__pitch { flex: 1 1 440px; min-width: 0; display: flex; flex-wrap: wrap; align-items: flex-end; gap: 36px; }
.ko-signin__words { flex: 1 1 300px; min-width: 0; display: flex; flex-direction: column; gap: 18px; }
.ko-signin__words .ko-kicker { font-size: 13px; }
.ko-signin h1 { font: 600 64px/0.98 var(--ko-display); letter-spacing: -0.01em; }
.ko-signin__words p { max-width: 460px; font-size: 18px; line-height: 1.55; color: var(--ko-on-sea); }
.ko-signin__points { display: flex; flex-direction: column; gap: 10px; padding-top: 6px; font-size: 15px; }
/* The shared 5px is Contacts.dc.html's (on 13px text it sits 1px under the
 * first line's centre). Login's points are centred on their 19px line, so
 * 5.5px here: one-line points as the mock draws them, wrapped ones by the
 * first line (Dennis). */
.ko-signin__points .ko-dotline::before { margin-top: 5.5px; }
.ko-signin__card { flex: 0 1 420px; min-width: 0; display: flex; flex-direction: column; gap: 20px; padding: 36px; border-radius: 20px; background: var(--ko-paper); color: var(--ko-ink); box-shadow: var(--ko-shadow-lift); }
.ko-signin__title { display: flex; flex-direction: column; gap: 6px; }
.ko-signin__title h2, .ko-signin__sent h2 { font: 600 30px var(--ko-display); }
.ko-signin__sent h2 { font-size: 28px; }
.ko-signin__card .ko-field input { min-height: 48px; padding: 12px 14px; border-radius: 12px; font-size: 15px; }
.ko-signin__card .ko-solid { padding-top: 13px; padding-bottom: 13px; font-size: 17px; }
.ko-signin__card .ko-link { font-weight: 400; text-decoration: underline; }
.ko-signin__card > .ko-link, .ko-signin__sent > .ko-link { align-self: center; min-height: var(--ko-touch); padding: 0 10px; font-size: 14px; }
.ko-signin__sent { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 12px 0; text-align: center; }
.ko-signin__sent p { font-size: 14px; color: var(--ko-body); }
.ko-signin__shore { position: absolute; left: 0; bottom: 0; display: block; width: 100%; height: 90px; }
.ko-signin__far { fill: var(--ko-sea-dark); }
.ko-signin__near { fill: var(--ko-ink); }
.ko-signin__foot { position: absolute; left: 0; right: 0; bottom: 14px; text-align: center; font-size: 12px; color: var(--ko-on-sea-quiet); }

/* ── Small parts. Secrets: mono 12px on paper. */
.ko-quiet { font-size: 13px; color: var(--ko-muted); }
/* A line that blocks something: a field's error, a missing step. */
.ko-warnline { font-size: 13px; font-weight: 600; color: var(--ko-red-text); }
.ko-secret { padding: 10px 12px; border: 1px solid var(--ko-line); white-space: pre-wrap; word-break: break-all; }
.ko-checklist { display: flex; align-items: flex-start; gap: 10px; font-size: 14px; cursor: pointer; }
.ko-checklist input { flex: none; width: 18px; height: 18px; margin: 2px 0 0; accent-color: var(--ko-sea); }

/* ── Empty states — Ideas.dc.html's "Sorting-cubby empty states": the wood
 *    cubby wall (4 × 3, gap 4, the mock's 132px content box with 6px
 *    padding, so 144 across; radius 4; each cubby 26px tall, wood-dark,
 *    radius 2), one cubby holding a paper letter 18 × 12, radius 1 — a
 *    different one per screen — 22px from the words, a card item: the title
 *    Fredoka 600 17px over the line, 12px muted, 4px apart. The tile's paper
 *    well is the empty state's own paper (Dennis): the page itself, a well
 *    inside a panel. Its outline is Preview.dc.html's image placeholder,
 *    1.5px dashed line-strong at the panel's radius. The wall leads, so the
 *    item runs right to left. The words take their own width, up to 360px a
 *    line, so the pair stays centred; where the row can't give them 160px
 *    (or their own width, if less) the wall goes above them (Dennis): every
 *    empty state below 416px wide, and Brands' panel below 446px.
 *    Below .ko-item, so its gap wins. Koboli_UI::empty_state(). ─────────── */
.ko-empty { flex-direction: row-reverse; flex-wrap: wrap-reverse; justify-content: center; gap: 22px; padding: 28px; border: 1.5px dashed var(--ko-line-strong); border-radius: var(--ko-radius-md); background: var(--ko-paper); }
.ko-empty .ko-item__text { flex: 1 1 160px; max-width: max-content; }
.ko-empty .ko-quiet { max-width: 360px; }
.ko-empty__title { font: 600 17px var(--ko-display); }
.ko-cubbies { flex: none; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px; width: 132px; box-sizing: content-box; padding: 6px; border-radius: 4px; background: var(--ko-wood); }
.ko-cubbies > span { height: 26px; display: grid; place-items: center; border-radius: 2px; background: var(--ko-wood-dark); }
.ko-cubbies span span { width: 18px; height: 12px; border-radius: 1px; background: var(--ko-paper); }

/* ── Narrow screens — the handoff only says "fluid". A 16px gutter (the
 *    sign-in page's too, Dennis), and two stat tiles a row. ───────────── */
@media (max-width: 720px) {
	:root { --ko-gutter: 16px; }
	.ko-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
