/* The form controls every browser page on this listener shares.
   
   Here rather than in each page's own stylesheet because there is exactly one
   right answer per control and four pages that need it. /overview shipped its
   own <select> and drew the browser's chevron hard against the border; the
   correct one already existed on /preferences, in internal/agents' stylesheet,
   where nothing else could reach it.
   
   Served as /appearance.css, which every page already links, so
   a new page gets these without remembering to ask. That also decides what may
   go in this file: rules a page can OVERRIDE, and none it has to know about. */

:root {

    /* How much room the chevron needs on the right of a select. A token
       because a page that sets its own padding has to leave that room, and
       the alternative is the same magic number written in four stylesheets
       and corrected in one. */
    --select-chevron-gap: 34px;

    /* The chevron itself, pointing down and — on a trigger whose list is
       open — up, as the period and account chevrons do. Tokens, so a native
       <select> and a button drawn as one (.field-select, below) share one
       drawing. A data: URI cannot read a custom property — the colour is
       inside the image — so each palette ships its own pair. The muted grey
       in both: at 12x8 the arrow is a hairline, and text-weight ink reads as
       a smudge on the dark ground and as heavier than the label on the light. */
    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2364748b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
    --select-chevron-open: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2364748b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 6.5 6 1.5 11 6.5'/%3E%3C/svg%3E");
}

[data-theme="dark"] {
    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2398a3b8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
    --select-chevron-open: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2398a3b8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 6.5 6 1.5 11 6.5'/%3E%3C/svg%3E");
}

/* The chevron, drawn by us.
   
   The browser's own is painted against the BORDER edge and ignores
   padding-right — measured on /preferences' own selects: 10px and 32px
   screenshot identically — so the only way to move it off the outline is to
   turn the native one off and supply one.
   
   :not([multiple]) is not a filter, it is SPECIFICITY. A bare `select` here
   loses to any page rule of equal weight further down the cascade, and the two
   properties it would lose are the two that matter: a page's `background`
   shorthand resets background-image, and its `padding` shorthand resets
   padding-right. Either leaves a select that has had its native arrow removed
   and has no arrow at all — drawn, not thrown. At (0,1,1) this outranks the
   element-level rules pages actually write. A multiple select has no chevron
   to draw anyway, so the selector is honest about what it does.
   
   background-image and not the background shorthand, for the mirror of the
   same reason: a page owns its control's surface colour, and this file must
   not decide it. */
select:not([multiple]) {
    appearance: none;
    -webkit-appearance: none;
    padding-right: var(--select-chevron-gap);
    background-image: var(--select-chevron);
    background-repeat: no-repeat;
    background-position: right 13px center;
}


/* ---- pages with no script ---------------------------------------------

   The palette above is painted by appearance.js, which sets every --wb-*
   role on <html> and stamps data-theme. The sign-in pages (/login, and
   /oauth/login's consent flow) carry no script at all, so without this block
   the shared controls there would draw with their light fallbacks on a dark
   machine. This is the same palette, written out, following the machine's
   prefers-color-scheme — the one thing a page with no script can read.

   :root:not([data-theme]) is what keeps it off every other page: appearance.js
   stamps data-theme synchronously in <head>, before the first paint, so on a
   scripted page this never matches, not even for a frame.

   A copy of appearance.js's catalog, which is the drift this file exists to
   prevent — so TestTheNoScriptPaletteIsTheCatalog compares every value here
   against appearance.js and the [data-theme="dark"] blocks above. */
:root:not([data-theme]) {
    color-scheme: light;
    --wb-text: #222d49;
    --wb-panel: #ffffff;
    --wb-backdrop: #ffffff;
    --wb-surface: #f6f7f8;
    --wb-accent: #ff66b3;
    --wb-accent-bg: #f7f8fc;
    --wb-on-accent: #ffffff;
    --wb-title: #8c7cd3;
    --wb-muted: #64748b;
    --wb-shadow: #c0c8d3;
    --wb-bevel-hi: #ffffff;
    --wb-error: #df1160;
    --wb-status-bg: #f1f5f9;
    --wb-status-bd: #c0c8d3;
    --wb-widget-bd: #ecedf2;
    --wb-quote-bg: #fdfdfd;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) {
        color-scheme: dark;
        --wb-text: #e6e9f0;
        --wb-panel: #1a1e28;
        --wb-backdrop: #14171f;
        --wb-surface: #232833;
        --wb-accent: #ff66b3;
        --wb-accent-bg: #252334;
        --wb-on-accent: #ffffff;
        --wb-title: #a79ae0;
        --wb-muted: #98a3b8;
        --wb-shadow: #3a4254;
        --wb-bevel-hi: #414a5e;
        --wb-error: #ff5f8a;
        --wb-status-bg: #1c202a;
        --wb-status-bd: #3a4254;
        --wb-widget-bd: #3a4254;
        --wb-quote-bg: #191d26;
        --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2398a3b8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
        --select-chevron-open: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2398a3b8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 6.5 6 1.5 11 6.5'/%3E%3C/svg%3E");
        --btn-bd: #3a4254;
        --btn-hover: #2a303d;
        --btn-ring: rgba(255, 102, 179, .45);
        --field-focus-bd: #6b7589;
        --field-focus-ring: rgba(152, 163, 184, .18);
    }
}

/* ---- buttons ---------------------------------------------------------

   The one button every browser page on this listener draws, opted into with
   class="button" on a <button> or an <a>. It lived in internal/agents'
   stylesheet until 2026-09-25, where /overview copied it and /admin drew a
   pink capsule of its own three times over.

   Modelled on shadcn's outline button (2026-09-23): a fixed height with the
   label centred, weight 500, a light border with a faint shadow, a grey fill on
   hover rather than a pink border, a 1px press, and a soft ring for keyboard
   focus. Icons (.btn-icon) sit in the flex gap rather than carrying a margin,
   so one can go before or after the words.

   A class and not a bare `button` selector: the rail (internal/nav) and chat's
   composer style their own buttons, and a bare rule leaks a border and padding
   into both. :where() keeps the base at zero weight, so a page's own class
   rule — .pill, .ctx-btn — still beats it, which is the "rules a
   page can override" contract at the top of this file. */
:root {
    --btn-bg: var(--wb-panel, #fff);
    --btn-bd: #dde2ea;
    --btn-hover: #eef1f5;
    --btn-ring: rgba(255, 102, 179, .35);
    --btn-radius: 10px;

    /* A text box with the cursor in it: the border a few steps darker than
       --btn-bd, grey rather than the accent (2026-09-28) — pink around every
       box being typed in read as an alert. */
    --field-focus-bd: #94a3b8;
    --field-focus-ring: rgba(100, 116, 139, .15);
}

/* The dark page is #14171f and the panel #1a1e28, so a button on the page is a
   step lighter than it; the hover is a second step, not the same colour as
   the card it may sit in. */
[data-theme="dark"] {
    --btn-bd: #3a4254;
    --btn-hover: #2a303d;
    --btn-ring: rgba(255, 102, 179, .45);
    --field-focus-bd: #6b7589;
    --field-focus-ring: rgba(152, 163, 184, .18);
}

:where(.button) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;

    /* An inline-flex <a> and <button> otherwise sit on different baselines,
       and Open report rode a few pixels above Details beside it. */
    vertical-align: middle;
    height: 36px;
    padding: 0 14px;
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;

    /* The text colour, not inherit: /overview's toolbar sits inside a muted
       label, and an inherited grey made Add tile read as disabled. */
    color: var(--wb-text, #222d49);
    text-decoration: none;
    background-color: var(--btn-bg);
    border: 1px solid var(--btn-bd);
    border-radius: var(--btn-radius);
    box-shadow: 0 1px 2px rgba(20, 25, 40, .05);
    cursor: pointer;
    transition: background-color .15s, border-color .15s, box-shadow .15s, color .15s;
}
:where(.button):hover { background-color: var(--btn-hover); }
:where(.button):active { transform: translateY(1px); }

/* The ring replaces the outline: it follows the radius, and it is drawn for
   the keyboard only, so a mouse click leaves no ring behind. */
:where(.button):focus-visible {
    outline: none;
    border-color: var(--wb-accent, #ff66b3);
    box-shadow: 0 0 0 3px var(--btn-ring);
}

/* pointer-events, not a :hover override per variant: a disabled button then
   neither fills nor presses, whatever class it carries. */
:where(.button):disabled { opacity: .5; pointer-events: none; box-shadow: none; }

:where(.button).small { height: 32px; padding: 0 10px; font-size: 13px; gap: 5px; }

/* The one filled button a view has: Create agent, Save. Hover darkens the
   accent a step instead of greying it, which would read as disabled. */
:where(.button).primary {
    background-color: var(--wb-accent, #ff66b3);
    border-color: var(--wb-accent, #ff66b3);
    color: var(--wb-on-accent, #fff);
    font-weight: 600;
}
:where(.button).primary:hover { background-color: color-mix(in srgb, var(--wb-accent, #ff66b3) 90%, #000); }

/* Filled red: the OK of a question whose answer destroys something. */
:where(.button).danger {
    background-color: var(--wb-error, #df1160);
    border-color: var(--wb-error, #df1160);
    color: #fff;
    font-weight: 600;
}
:where(.button).danger:hover { background-color: color-mix(in srgb, var(--wb-error, #df1160) 88%, #000); }

/* No box until hovered: a dialog's close and a row's delete. Delete turns
   red on hover, the one warning it gives before it asks. */
:where(.button).quiet {
    border-color: transparent;
    background-color: transparent;
    box-shadow: none;
    color: var(--wb-muted, #64748b);
}
:where(.button).quiet:hover { background-color: var(--btn-hover); color: var(--wb-error, #df1160); }

/* 16px marks, 14px in a small button, as Lucide is drawn. flex: none keeps a
   long label from squeezing the icon. */
/* An <svg> straight inside a button is drawn as a .btn-icon even without the
   class: chat builds its icon buttons' marks in script, with no class on
   them. A page that fills its mark (a ⋯ of three dots) says so itself. */
.btn-icon,
:where(.button) > svg {
    width: 16px;
    height: 16px;
    flex: none;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.small .btn-icon { width: 14px; height: 14px; }

/* A button whose label is drawn rather than written: square, the mark alone. */
:where(.button).icon-only { width: 36px; padding: 0; }
:where(.button).icon-only.small { width: 32px; }
/* An icon-only button's mark is all it has, so a small one keeps it 16px. */
:where(.button).icon-only.small .btn-icon,
:where(.button).icon-only.small > svg { width: 16px; height: 16px; }

/* A ghost: no box until pointed at, the muted ink until then. For the small
   actions that sit beside content rather than ask to be pressed — copy, rate,
   pin under an answer, a ⋯, a dialog's ✕. The grey fill it takes on hover
   stays while it is pressed (a rated answer) or while its menu is open, so
   the screen still says so once the pointer has gone. Not .quiet, whose hover
   turns the ink red: that one is for Delete. */
:where(.button).ghost {
    border-color: transparent;
    background-color: transparent;
    box-shadow: none;
    color: var(--wb-muted, #64748b);
}
:where(.button).ghost:hover,
:where(.button).ghost:is([aria-pressed="true"], [aria-expanded="true"], .active) {
    background-color: var(--btn-hover);
    color: var(--wb-text, #222d49);
}

/* A bell: an alert that is on or off (common.js bellToggle, on /preferences
   and /events). A button with a bell and a word, so it reads as a control.
   Off is the plain bordered button, "Set" in the page's own ink — never
   muted grey, which is how a disabled control looks. On is a soft pink chip:
   pink text in medium weight on a pale pink tint, a border one step darker
   than the tint so it keeps a crisp edge on a white row, and the bell still
   an outline — bold text and a filled glyph at this size read as heavy and
   loud. The pink is the accent's hue (the Find and New buttons), a notch
   softer. Both states keep the small button's own padding and size. It
   rings once when turned on, by a class set on the press, not on
   [aria-pressed] — a list redrawn with its bells on would otherwise ring
   every one of them. */
:where(.button).bell[aria-pressed="true"] {
    color: #ef5a9f;
    background-color: #ffeef6;
    border-color: #ffc5df;
    font-weight: 500;
}
:where(.button).bell[aria-pressed="true"]:hover {
    background-color: #ffe2ef;
}
[data-theme="dark"] :where(.button).bell[aria-pressed="true"] {
    color: #ff82c0;
    background-color: #36132a;
    border-color: #6a2653;
}
[data-theme="dark"] :where(.button).bell[aria-pressed="true"]:hover {
    background-color: #431834;
}
:where(.button).bell .btn-icon { transform-origin: 50% 12%; }
:where(.button).bell.ringing .btn-icon { animation: bell-ring .5s ease-out; }
@keyframes bell-ring {
    0% { transform: rotate(0); }
    20% { transform: rotate(16deg); }
    40% { transform: rotate(-12deg); }
    60% { transform: rotate(8deg); }
    80% { transform: rotate(-4deg); }
    100% { transform: rotate(0); }
}
@media (prefers-reduced-motion: reduce) {
    :where(.button).bell.ringing .btn-icon { animation: none; }
}

/* Round: the composer's + and Send, which sit inside a rounded box of their
   own and would read as a corner of it if they were square. */
:where(.button).round { border-radius: 999px; }

/* An opened Details turns its chevron over, so the button says it is open. */
button[aria-expanded="true"] .btn-icon.chevron { transform: rotate(180deg); }
.btn-icon.chevron { transition: transform .15s; }

@media (prefers-reduced-motion: reduce) {
    :where(.button) { transition: none; }
    :where(.button):active { transform: none; }
}

/* ---- menus -----------------------------------------------------------

   The popup every dropdown on this listener draws: the rail's account and
   group-by menus, chat's app picker, + menu and ⋯ menu, /overview's select
   lists and dashboard menus, /agents' Actions menus and pickers. Until
   2026-09-27 that was six stylesheets' worth of panel and row, each a little
   different — three borders, four row heights, three ways to mark the chosen
   row. cmd/components draws them side by side.

   Markup: a .menu holding .menu-item rows (<button> or <a>), plus, when
   wanted, .menu-heading, .menu-sep, .menu-empty, a .menu-filter input and a
   .menu-list that scrolls under it. Scripts show and hide it with `hidden`
   and nothing else; everything the eye sees is here.

   Plain classes, not :where() like the button. A row is usually a <button>,
   and a page's element rules (/agents styles every input, /login every
   button) outweigh a zero-weight base — the row would draw as a form control.
   A page still overrides with a compound (.menu.app-picker-menu), which is how every
   page positions its own. */

/* The panel. It hangs DOWN from its trigger, left edges aligned; .up and
   .end turn it the other way. The trigger's wrapper is the positioned box.
   The border is not decoration: on the dark palette the shadow disappears
   into the page, and a borderless panel reads as part of whatever it covers. */
.menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 30;
    display: flex;
    flex-direction: column;
    gap: 2px;
    box-sizing: border-box;
    min-width: 10rem;
    max-height: min(60vh, 22rem);
    overflow-y: auto;
    padding: 6px;
    text-align: left;
    background: var(--wb-panel, #fff);
    border: 1px solid var(--wb-widget-bd, #ecedf2);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(20, 25, 40, .14);
    scrollbar-width: thin;
    scrollbar-color: var(--wb-shadow, #c0c8d3) transparent;
}
.menu.up { top: auto; bottom: calc(100% + 6px); }
.menu.end { left: auto; right: 0; }

/* It fades in and out, sliding 4px away from its trigger and growing from
   97%. Three pieces make that work with no script: @starting-style is the
   first frame after display:none lifts, the [hidden] rule is the last frame
   before it lands, and allow-discrete holds display back until the fade is
   over. A transition outranks even a page's `[hidden] { display: none
   !important }`, so this works on the pages that have one. */
.menu {
    transform-origin: top left;
    transition: opacity 150ms ease, transform 150ms ease, display 150ms allow-discrete;
}
.menu.end { transform-origin: top right; }
.menu.up { transform-origin: bottom left; }
.menu.up.end { transform-origin: bottom right; }

.menu[hidden] {
    display: none;
    opacity: 0;
    transform: translateY(-4px) scale(0.97);
}
.menu.up[hidden] { transform: translateY(4px) scale(0.97); }

@starting-style {
    .menu { opacity: 0; transform: translateY(-4px) scale(0.97); }
    .menu.up { transform: translateY(4px) scale(0.97); }
}

/* One row. The hover tint is --wb-accent-bg: faint on the white panel, but
   the one grey that still reads on the dark one, where --wb-status-bg is
   within a shade of the panel. Keyboard focus draws the same fill as the
   mouse, not an outline — the panel scrolls, and a scrolling box clips an
   outline drawn outside its row. */
.menu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    height: auto;
    box-sizing: border-box;
    margin: 0;
    padding: 7px 9px;
    font: inherit;
    font-family: var(--ui-font, inherit);
    font-size: 0.85rem;
    font-weight: 500;
    line-height: 1.35;
    color: var(--wb-text, #222d49);
    text-align: left;
    text-decoration: none;
    background: transparent;
    border: 0;
    border-radius: 6px;
    box-shadow: none;
    cursor: pointer;
}
.menu-item:hover,
.menu-item:focus-visible {
    background: var(--wb-accent-bg, #f7f8fc);
    outline: none;
}
.menu-item:active { transform: none; }

/* An author display: beats the UA's [hidden], so a hidden row would draw. */
.menu-item[hidden] { display: none; }

/* The chosen row, however the page marks it: bold, and nothing else. A tint
   would read as the hover, and the row under the pointer and the row that
   is chosen would be the same colour. */
.menu-item:is(.chosen, [aria-current="page"], [aria-selected="true"], [aria-checked="true"]) {
    font-weight: 600;
}

/* Delete and its kind warn on hover, the only warning before the confirm. */
.menu-item.danger:hover { color: var(--wb-error, #df1160); }

/* Offered but not available here; the row's title says why. */
.menu-item:is(:disabled, [aria-disabled="true"]) {
    color: var(--wb-muted, #64748b);
    opacity: 0.6;
    cursor: default;
    background: transparent;
}

/* A leading icon, in the muted ink so the words lead. */
.menu-item > svg,
.menu-item > .btn-icon {
    flex: none;
    width: 16px;
    height: 16px;
    color: var(--wb-muted, #64748b);
}

/* A check in front of the label that holds its room on every row, so the
   labels line up, and shows on the chosen one only. For lists where bold
   alone is too quiet: a select's options, the dashboard switcher. */
.menu-item > .menu-check {
    width: 13px;
    height: 13px;
    color: var(--wb-accent, #ff66b3);
    visibility: hidden;
}
.menu-item:is(.chosen, [aria-current="page"], [aria-selected="true"], [aria-checked="true"]) > .menu-check {
    visibility: visible;
}

/* Muted words inside a row: who shared it, an id's tail. Inline after the
   label; a row that wants it on the right grows its label (flex: 1). */
.menu-hint {
    font-size: 0.75rem;
    font-weight: 400;
    color: var(--wb-muted, #64748b);
}

/* A group's name: text, not a row, so not on the tab order. */
.menu-heading {
    padding: 8px 9px 3px;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--wb-muted, #64748b);
}

.menu-sep {
    flex: none;
    height: 0;
    margin: 4px 2px;
    border-top: 1px solid var(--wb-widget-bd, #ecedf2);
}

.menu-empty {
    padding: 7px 9px;
    font-size: 0.8rem;
    color: var(--wb-muted, #64748b);
}

/* The filter box at the top of a long list, and the list under it. The list
   scrolls and the filter stays put: min-height 0 is what lets the list
   shrink inside the panel instead of pushing it past its max-height. */
.menu-filter {
    flex: none;
    width: 100%;
    box-sizing: border-box;
    margin: 0 0 4px;
    padding: 6px 9px;
    font: inherit;
    font-size: 0.8rem;
    color: var(--wb-text, #222d49);
    background: var(--wb-panel, #fff);
    border: 1px solid var(--wb-widget-bd, #ecedf2);
    border-radius: 6px;
}
.menu-filter::placeholder { color: var(--wb-muted, #64748b); }
.menu-filter:focus { outline: none; border-color: var(--field-focus-bd, #94a3b8); }

.menu-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

@media (prefers-reduced-motion: reduce) {
    .menu { transition: none; }
}

/* ---- text fields -----------------------------------------------------

   The box every page types into: class="field" on an <input>, a <textarea>
   or a <select>. Until 2026-09-27 that was four families — /agents' global
   input rule and /preferences' variant of it, chat's three boxes, /overview's
   two, /admin's search — at three heights, three borders and three radii,
   and cmd/components drew them side by side.

   It is the button's box on purpose: the same 36px (32px .small), the same
   --btn-bd border and --btn-radius, so a field and the button beside it line
   up and read as one family. Focus draws a darker grey border with a soft
   ring around it and keeps the white fill — a focused control is never grey, since
   grey reads as disabled, the opposite of "type here".

   A plain class, like .menu and unlike the button's :where(): /login styles
   every bare input, and a zero-weight base would lose to it. Not a bare
   `input` selector either — checkboxes, radios, the composer's textarea and
   the inline dashboard rename are all inputs and none of them is this box.

   background-color, never the shorthand: on a <select> the shared chevron
   above is a background-image, and the shorthand would wipe it. */
.field {
    box-sizing: border-box;
    width: 100%;
    height: 36px;
    margin: 0;
    padding: 0 12px;
    font: inherit;
    font-size: 14px;
    line-height: 1.4;
    color: var(--wb-text, #222d49);
    background-color: var(--wb-panel, #fff);
    border: 1px solid var(--btn-bd, #dde2ea);
    border-radius: var(--btn-radius, 10px);
    box-shadow: 0 1px 2px rgba(20, 25, 40, .05);
    outline: none;
    transition: border-color .15s, box-shadow .15s, background-color .15s;
}
.field::placeholder {
    color: var(--wb-muted, #64748b);
    opacity: 1;
}
.field:focus {
    border-color: var(--field-focus-bd, #94a3b8);
    box-shadow: 0 0 0 3px var(--field-focus-ring, rgba(100, 116, 139, .15));
}
.field:disabled {
    opacity: .6;
    cursor: not-allowed;
}

/* A paragraph box: no fixed height, a line of padding, and it grows down.
   How tall it starts is the page's call, from what it holds. */
textarea.field {
    height: auto;
    min-height: 88px;
    padding: 8px 12px;
    line-height: 1.45;
    resize: vertical;
}

.field.small {
    height: 32px;
    padding: 0 10px;
    font-size: 13px;
}

/* The toolbar size: a chart widget's bar (internal/widgets), where a row of
   pickers sits above a chart in a chat bubble or a dashboard tile and every
   pixel of height is taken from the chart. The same field, one step smaller;
   a select keeps its chevron and its room for it. */
.field.xsmall {
    height: 26px;
    padding: 0 8px;
    font-size: 12px;
    border-radius: 8px;
}
.field.field-select.xsmall {
    padding-right: 24px;
    background-position: right 8px center;
}

/* A select. Two ways to get one, drawn alike: a native <select class="field">,
   and a <button class="field field-select"> that opens a .menu — what a page
   uses when the list needs more than an <option> can hold (a filter box, a
   check mark, a pill). The button takes the field's box, the select's chevron
   (flipped while its list is open) and ellipsis rather than a second line.
   Both answer the pointer with the grey fill a .button uses. Neither draws
   the accent ring on a click, open or not: a select is something to open,
   not a place to type, and the open list already says where you are. The
   ring stays for keyboard focus (:focus-visible). A focused select keeps its
   white fill.

   .field.field-select, two classes: .field's padding shorthand comes later in
   this file and would otherwise take back the chevron's room. */
.field.field-select {
    display: block;
    padding-right: var(--select-chevron-gap);
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    background-image: var(--select-chevron);
    background-repeat: no-repeat;
    background-position: right 13px center;
    cursor: pointer;
}
.field.field-select[aria-expanded="true"] { background-image: var(--select-chevron-open); }

select.field:hover:not(:disabled):not(:focus),
.field.field-select:hover:not(:disabled):not(:focus) {
    background-color: var(--btn-hover);
}
select.field:focus:not(:focus-visible),
.field.field-select:focus:not(:focus-visible) {
    border-color: var(--btn-bd, #dde2ea);
    box-shadow: 0 1px 2px rgba(20, 25, 40, .05);
}

/* A field with a mark inside its left edge — the search boxes. The wrapper
   is the positioned box; the input keeps its own border and focus ring, so
   the mark and the box read as one control. */
.field-icon {
    position: relative;
    display: block;
}
.field-icon > svg {
    position: absolute;
    left: 11px;
    top: 50%;
    width: 16px;
    height: 16px;
    transform: translateY(-50%);
    color: var(--wb-muted, #64748b);
    pointer-events: none;
}
.field-icon > .field { padding-left: 34px; }

@media (prefers-reduced-motion: reduce) {
    .field { transition: none; }
}

/* ---- dialogs ------------------------------------------------------------

   A modal: class="dialog" on a <dialog> opened with showModal(), holding a
   .dialog-head (the title and a ✕), a .dialog-body, and a .dialog-actions row
   at its foot. Until 2026-09-27 the three pages drew three: /agents' with a
   darker border and 10px corners, chat's with its own scrim and 14px,
   /dashboard's with a shadow and 12px, each with its own padding and title
   size.

   /dashboard's box wins, because it is the one that reads as lifted: a light
   border plus a real shadow. The scrim is darker in the dark palette and
   pure black, since rgba(20,25,40,.38) over white is a clear dim and over
   #14171f is very nearly nothing — the same value doing opposite amounts of
   work, as /agents found. Width is 620px by default and the page's to change
   (#tile-dialog is narrow, chat's prompt library wide). */
.dialog {
    box-sizing: border-box;
    width: min(620px, 94vw);
    padding: 0;
    color: var(--wb-text, #222d49);
    background: var(--wb-panel, #fff);
    border: 1px solid var(--wb-widget-bd, #ecedf2);
    border-radius: 12px;
    box-shadow: 0 20px 50px rgba(20, 25, 40, .25);
}

/* Opening fades the box in and grows it from 96%; closing runs it back.
   @starting-style gives the first frame to animate from, and allow-discrete
   keeps the box in the top layer until its fade-out ends — without it a
   close() drops it at once. A browser without either opens it still, as
   before. The dim behind is not animated. */
.dialog {
    opacity: 0;
    transform: scale(.96) translateY(4px);
    transition: opacity .18s ease, transform .18s cubic-bezier(.2, .8, .2, 1),
                overlay .18s allow-discrete, display .18s allow-discrete;
}
.dialog[open] { opacity: 1; transform: none; }
@starting-style {
    .dialog[open] { opacity: 0; transform: scale(.96) translateY(4px); }
}
@media (prefers-reduced-motion: reduce) {
    .dialog { transition: none; }
}

.dialog::backdrop { background: rgba(20, 25, 40, .38); }
[data-theme="dark"] .dialog::backdrop { background: rgba(0, 0, 0, .58); }

/* The title, and its ✕ pushed to the far end. Any heading level: a page
   picks the one its outline needs, and they all look like this here. */
.dialog-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 18px 22px 0;
}
.dialog-head > :is(h1, h2, h3, h4) {
    margin: 0;
    font-size: 17px;
    font-weight: 600;
}
.dialog-head > .button:last-child { margin-left: auto; }

/* The fields, a column with one gap between them. A <form> may be the body
   itself: method="dialog" closes on submit. */
/* minmax(0, 1fr), not the implicit auto column: a grid column never shrinks
   below its widest unbreakable content, so one long file name in the agent
   form held the column at full width even after the dialog's own vertical
   scrollbar took 15px of it — and the dialog grew a horizontal scrollbar. */
.dialog-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
    padding: 18px 22px 22px;
}

/* Cancel, then the one filled action, at the right. */
.dialog-actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
}

/* Confirm.ask and Confirm.input (confirm.js): a question and two buttons,
   narrower than a form. */
.confirm-dialog { width: min(420px, 94vw); }
.confirm-dialog .dialog-body { gap: 20px; padding-top: 22px; }

/* confirm.js hides the parts a question does not use with the hidden
   attribute, which .dialog-head's display: flex would otherwise beat — an
   empty head left an 18px band above every untitled question. */
.confirm-dialog [hidden] { display: none; }
.confirm-message {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
    overflow-wrap: anywhere;
}

/* ---- tables -------------------------------------------------------------

   A data table: class="table" on a <table>. Small uppercase muted headers,
   one hairline between rows and none around the block — a table sits inside a
   card or a page section, and is not a card of its own. Until 2026-09-27
   /agents and /admin styled every bare <table> with element rules, two ways:
   different header sizes, paddings and row lines for the same kind of list.

   A class, not an element rule: chat's reply tables are markdown from the
   model, read as prose, and the chart widgets' tables are drawn inside a
   chart — both are compact on purpose and are not this.

   `tr.clickable` is a row that opens something, `.dense` a long list where
   row height decides how much fits on screen (/preferences' apps), `.num` a
   right-aligned figure. */
.table {
    width: 100%;
    border-collapse: collapse;
}
.table th {
    padding: 8px 10px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .05em;
    text-align: left;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--wb-muted, #64748b);
    border-bottom: 1px solid var(--wb-widget-bd, #ecedf2);
}
.table td {
    padding: 10px;
    vertical-align: top;
    border-bottom: 1px solid var(--wb-widget-bd, #ecedf2);
}
.table tbody tr:last-child td { border-bottom: none; }

.table tr.clickable { cursor: pointer; }
.table tr.clickable:hover td { background: var(--wb-accent-bg, #f7f8fc); }

.table.dense td {
    padding: 6px 10px;
    vertical-align: middle;
}

.table .num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* ---- pills, chips and tones ----------------------------------------------

   A capsule holding one word: class="pill" for a state (a run's status), or
   class="chip" for a category (which engine ran) — the same shape, two names,
   because a row can carry one of each and a reader must not read the second as
   another status. Plain, it is the neutral grey: "nothing to say".

   Its colour is a tone, named by HUE rather than by meaning: tone-green,
   tone-pink, tone-violet, tone-yellow, tone-orange, tone-blue, tone-magenta.
   Which status wears which is the page's to decide, in its script, beside the
   word it prints; what a tone MEANS is kept the same everywhere by habit and
   by this list: green is done or good, pink failed or bad, violet in
   progress, yellow waiting or worth a look, orange look at this now, blue and
   magenta are categories and never a state. Until 2026-09-27 /agents, /admin
   and /dashboard each wrote the same hex pairs out, and two blues had drifted
   a shade apart.

   A tone is a class of its own, not a .pill modifier, so /dashboard's KPI
   change badge — a different shape on purpose — wears the same green and pink.

   Each has a light pair and a dark pair, and the dark ones invert rather than
   dim: a pale tint with a saturated word on it becomes a bright block on a
   dark card, the loudest thing on a page whose job is a list, so at night the
   tint goes dark and the word goes light. Measured, the dark pairs read
   BETTER (6.8:1 to 9.4:1) than the light (1.6:1 to 5.1:1) — a light hue on a
   dark tint has room to be both bright and saturated.

   The light words are the fully saturated steps, chosen with that cost
   known: 1.6:1 (yellow) to 5.1:1 (violet) against their own tint, where a
   paragraph would need 4.5:1. Two things carry them and neither is optional:
   the 600 weight, and never letting the colour say anything alone — the word
   is in the pill, and a failure spells out its reason. Darker steps that
   measure 2.0-2.5:1, if a reader ever reports one as unreadable: #0fc763 for
   the green, #dfa900 for the yellow. No border, deliberately: an outlined
   capsule reads as a control you can press, and these are labels. */
.pill,
.chip {
    display: inline-block;
    padding: 3px 11px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    color: var(--wb-muted, #64748b);
    background: var(--wb-status-bg, #f1f5f9);
    border-radius: 999px;
}

.tone-green   { background: #ddfbe9; color: #16d96e; }
.tone-pink    { background: #fdeaf1; color: #f43f77; }
.tone-violet  { background: #eae4fe; color: #6b2ff5; }
.tone-yellow  { background: #fdf6d3; color: #f0c000; }
.tone-orange  { background: #ffe3cc; color: #ff7a1a; }
.tone-blue    { background: #dce8ff; color: #2f6fed; }
.tone-magenta { background: #fae8ff; color: #c026d3; }

[data-theme="dark"] .tone-green   { background: #10301f; color: #4ade80; }
[data-theme="dark"] .tone-pink    { background: #331624; color: #ff7d9e; }
[data-theme="dark"] .tone-violet  { background: #221c3a; color: #b49bff; }
[data-theme="dark"] .tone-yellow  { background: #2e2611; color: #f2c94c; }
[data-theme="dark"] .tone-orange  { background: #402008; color: #ffab5e; }
[data-theme="dark"] .tone-blue    { background: #14264a; color: #6ea8ff; }
[data-theme="dark"] .tone-magenta { background: #4a044e; color: #e879f9; }

/* ---- checkboxes, switches, segmented choices ---------------------------

   Three ways a page asks for a choice, each defined once. Until 2026-09-27
   /agents drew its checkboxes and its segmented pill group, /overview its
   switch and its app list, each in its own stylesheet.

   A checkbox is drawn here, not the browser's own tinted with accent-color:
   Chrome picks the tick's colour itself from the accent's lightness, and on
   our light pink it picked a dark tick. Unticked is the field's box (same
   border, white fill), ticked is the accent with a white tick, the way
   .button.primary puts white on pink. class="checkbox" on the <input>, not a
   bare input[type=checkbox] rule — the switch below is a checkbox too, and is
   not this box. */
.checkbox {
    flex: none;
    box-sizing: border-box;
    width: 16px;
    height: 16px;
    margin: 0;
    appearance: none;
    background-color: var(--wb-panel, #fff);
    background-position: center;
    background-repeat: no-repeat;
    background-size: 12px 12px;
    border: 1px solid var(--btn-bd, #dde2ea);
    border-radius: 4px;
    cursor: pointer;
    transition: background-color .12s, border-color .12s;
}
.checkbox:hover:not(:checked):not(:disabled) { border-color: color-mix(in srgb, var(--btn-bd, #dde2ea) 60%, var(--wb-text, #222d49)); }
.checkbox:checked {
    background-color: var(--wb-accent, #ff66b3);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
    border-color: var(--wb-accent, #ff66b3);
}
.checkbox:focus-visible {
    outline: 2px solid var(--wb-accent, #ff66b3);
    outline-offset: 2px;
}
.checkbox:disabled { opacity: .5; cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) {
    .checkbox { transition: none; }
}

/* A checkbox and its words in one row: <label class="check"> around both, or
   a .check holding the input and a <label for>. The words are body text, not
   a field's label: normal weight, no gap under them. */
.check {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
}
.check > label {
    display: inline;
    margin: 0;
    font-weight: 400;
    cursor: pointer;
}
.check:has(.checkbox:disabled) {
    color: var(--wb-muted, #64748b);
    cursor: not-allowed;
}
.check:has(.checkbox:disabled) > label { cursor: not-allowed; }

/* A switch, for a choice that acts the moment it is flipped (it saves),
   where a checkbox reads as part of a form still to be submitted.
   <label class="switch"><input type="checkbox" role="switch"> words</label>.
   Off is the widget-border grey, on the accent. */
.switch {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
    color: var(--wb-muted, #64748b);
    cursor: pointer;
}
.switch > input {
    position: relative;
    flex: none;
    width: 2.1rem;
    height: 1.2rem;
    margin: 0;
    appearance: none;
    -webkit-appearance: none;
    background: var(--wb-widget-bd, #ecedf2);
    border-radius: 999px;
    cursor: pointer;
    transition: background 120ms ease;
}
.switch > input::before {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: calc(1.2rem - 4px);
    height: calc(1.2rem - 4px);
    background: var(--wb-panel, #fff);
    border-radius: 50%;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
    transition: transform 120ms ease;
}
.switch > input:checked { background: var(--wb-accent, #ff66b3); }
.switch > input:checked::before { transform: translateX(0.9rem); }
.switch > input:focus-visible {
    outline: 2px solid var(--wb-accent, #ff66b3);
    outline-offset: 2px;
}
.switch > input:disabled {
    opacity: 0.5;
    cursor: default;
}

/* One of a few, drawn as a pill of options: radios, because it IS a radio
   group — one choice, arrow-keyable, announced as a group. The inputs are
   hidden but kept, so keyboard focus and the checked state still exist to
   style against: opacity and position, not display:none, which would take
   them off the tab order.

   position:relative is load-bearing. The radios are position:absolute, and
   with nothing positioned above them their containing block is the page:
   three 1px transparent radios once made a document 81px taller than its
   window, a second scrollbar beside the content column's own.

   The chosen option is a filled panel in the page's ink, not the accent
   pink: pink means "unread" and "still working" elsewhere, and a setting
   left where the reader put it is neither. Focus is drawn on the LABEL,
   since the input it belongs to is the invisible one. */
.segmented {
    position: relative;
    display: inline-flex;
    gap: 2px;
    margin: 0;
    padding: 3px;
    border: 1px solid var(--btn-bd, #dde2ea);
    border-radius: 999px;
    background: var(--wb-status-bg, #f1f5f9);
}
.segmented > input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}
.segmented > label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    padding: 6px 14px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 500;
    color: var(--wb-muted, #64748b);
    cursor: pointer;
}
.segmented svg { width: 15px; height: 15px; flex: none; }
.segmented > label:hover { color: var(--wb-text, #222d49); }
.segmented > input:checked + label {
    background: var(--wb-panel, #fff);
    color: var(--wb-text, #222d49);
    box-shadow: 0 1px 2px rgba(20, 25, 40, .12);
}
/* In the dark palette the panel colour is within a shade of the track
   (#1a1e28 on #1c202a), and the chosen option could not be told from the
   others at all. The hover grey is one clear step up from the track. */
[data-theme="dark"] .segmented > input:checked + label {
    background: var(--btn-hover);
}
.segmented > input:focus-visible + label {
    outline: 2px solid var(--wb-accent, #ff66b3);
    outline-offset: 2px;
}
.segmented > input:disabled + label {
    opacity: .45;
    cursor: not-allowed;
}
.segmented > input:disabled + label:hover { color: var(--wb-muted, #64748b); }

@media (prefers-reduced-motion: reduce) {
    .switch > input,
    .switch > input::before { transition: none; }
}

/* ---- the dropdown over a native select (selectmenu.js) ----------------

   SelectMenu.attach draws a .field-select button and a .menu in front of a
   <select>, and keeps the select in the page, never drawn, as the value
   every handler reads. /dashboard's since 2026-09-27, shared since
   2026-09-30. A page sets the button's width, nothing else. */

.smenu-native {
    display: none;
}

/* The wrapper is what the open list is positioned against. */
.smenu {
    position: relative;
    display: block;
}

/* Never narrower than its button, and allowed to be wider: a long option
   is not cut off because the button was sized for a short one. Rows do not
   wrap. */
.menu.smenu-menu {
    top: calc(100% + 4px);
    min-width: 100%;
    width: max-content;
    max-height: 320px;
}

.smenu-menu .menu-item {
    white-space: nowrap;
}
