/* Design tokens — locked in by /plan-design-review (plan §13). Every new
   component MUST reference these; no hardcoded hex values outside this
   block. If you find yourself picking a new color, add it here first. */
:root {
    --bg: #f3f4f6;
    --surface: #ffffff;
    --border: #e5e7eb;
    --border-soft: #f3f4f6;
    --border-hover: #d1d5db;

    --ink: #1f2937;
    --ink-strong: #111827;
    --ink-muted: #6b7280;
    --ink-faint: #9ca3af;
    --ink-subtle: #4b5563;
    --ink-dim: #374151;

    --accent: #2563eb;
    --accent-dark: #1d4ed8;
    --accent-soft: #eff6ff;

    --work-bg: #dbeafe;
    --work-ink: #1e40af;
    --private-bg: #dcfce7;
    --private-ink: #166534;

    --pri-now-bg: #fee2e2;
    --pri-now-ink: #991b1b;
    --pri-high-bg: #ffedd5;
    --pri-high-ink: #9a3412;
    --pri-med-bg: #e5e7eb;
    --pri-med-ink: #374151;
    --pri-low-bg: #f3f4f6;
    --pri-low-ink: #6b7280;

    /* Waiting-on badge escalation. fresh <3d, warn 3-7d, stale >7d. */
    --wait-fresh-bg: #dcfce7;
    --wait-fresh-ink: #166534;
    --wait-warn-bg: #fef3c7;
    --wait-warn-ink: #92400e;
    --wait-stale-bg: #fee2e2;
    --wait-stale-ink: #b91c1c;
    --wait-gray: #6b7280;

    /* Billable-hours accent (Phase 5.8). Same amber family as the
       wait-warn because both communicate "attention". */
    --billable-bg: #fef3c7;
    --billable-border: #fde68a;
    --billable-ink: #92400e;
    --billable-ink-strong: #78350f;

    /* Overdue/urgent chip — reuses the danger family. */
    --overdue-bg: #fee2e2;
    --overdue-ink: #b91c1c;

    /* Today chip — same as wait-warn, softer "upcoming" signal. */
    --today-bg: #fef3c7;
    --today-ink: #92400e;

    /* State dots on todo groups. */
    --state-open: #3b82f6;
    --state-progress: #8b5cf6;
    --state-waiting: #f59e0b;
    --state-done: #10b981;
    --state-cancel: #9ca3af;

    --radius: 0.75rem;
    --radius-sm: 0.5rem;
    --radius-pill: 999px;
    --shadow-sm: 0 1px 3px rgba(0,0,0,0.04);
    --shadow-md: 0 4px 12px rgba(0,0,0,0.2);
    --shadow-fab: 0 4px 12px rgba(37,99,235,0.4);
    --focus-ring: 0 0 0 3px rgba(37,99,235,0.35);

    --font-head: Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

/* ───────────────────────────────────────────────────────────────────────
   Dark mode tokens (Phase 8). Applied when the <html> element has
   data-theme="dark" set. Theme bootstrap script in <head> picks the
   value from localStorage so the first paint is correct — no flash
   of wrong theme on load. Every component MUST use the var(--*) names
   above and skip hardcoded hex.
   ───────────────────────────────────────────────────────────────────────*/
[data-theme="dark"] {
    --bg: #0f172a;
    --surface: #1f2937;
    --border: #374151;
    --border-soft: #1e293b;
    --border-hover: #4b5563;

    --ink: #e5e7eb;
    --ink-strong: #f9fafb;
    --ink-muted: #9ca3af;
    --ink-faint: #6b7280;
    --ink-subtle: #d1d5db;
    --ink-dim: #9ca3af;

    --accent: #60a5fa;
    --accent-dark: #3b82f6;
    --accent-soft: #1e3a8a;

    --work-bg: #1e3a8a;
    --work-ink: #bfdbfe;
    --private-bg: #14532d;
    --private-ink: #bbf7d0;

    --pri-now-bg: #7f1d1d;
    --pri-now-ink: #fecaca;
    --pri-high-bg: #7c2d12;
    --pri-high-ink: #fed7aa;
    --pri-med-bg: #374151;
    --pri-med-ink: #e5e7eb;
    --pri-low-bg: #1f2937;
    --pri-low-ink: #9ca3af;

    --wait-fresh-bg: #14532d;
    --wait-fresh-ink: #86efac;
    --wait-warn-bg: #78350f;
    --wait-warn-ink: #fde68a;
    --wait-stale-bg: #7f1d1d;
    --wait-stale-ink: #fecaca;

    --billable-bg: #78350f;
    --billable-border: #b45309;
    --billable-ink: #fde68a;
    --billable-ink-strong: #fef3c7;

    --overdue-bg: #7f1d1d;
    --overdue-ink: #fecaca;
    --today-bg: #78350f;
    --today-ink: #fde68a;

    --state-open: #60a5fa;
    --state-progress: #a78bfa;
    --state-waiting: #fbbf24;
    --state-done: #34d399;
    --state-cancel: #9ca3af;

    --shadow-sm: 0 1px 3px rgba(0,0,0,0.3);
    --shadow-md: 0 4px 12px rgba(0,0,0,0.5);
    --shadow-fab: 0 4px 12px rgba(96,165,250,0.4);
    --focus-ring: 0 0 0 3px rgba(96,165,250,0.45);
}

/* Body-level theme coupling. Keeps body bg + text in sync with tokens so
   the page doesn't have a bright-white border when the rest is dark. */
html { background: var(--bg); color-scheme: light dark; }
[data-theme="dark"] { color-scheme: dark; }
body { background: var(--bg); color: var(--ink); }

/* Phase 8 a11y: make keyboard focus universally visible. :focus-visible
   only lights up on keyboard navigation, so pointer users don't see
   halos everywhere. Overrides Bootstrap's default outlines with our
   token-driven ring. */
:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: inherit;
}
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[role="button"]:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

/* V1.23: trim the article's px-4 (Bootstrap = 1.5rem each side) on
   mobile so cards/lists use more of the viewport. Below the sidebar
   breakpoint (641px) the page is the only column anyway, so the extra
   gutter was wasted whitespace. Selector is .content.px-4 with !important
   because px-4 is a Bootstrap utility carrying its own !important. */
@media (max-width: 640px) {
    .content.px-4 {
        padding-left: 0.5rem !important;
        padding-right: 0.5rem !important;
    }
}

/* Account pages (admin Accounts / Edit / Register / ChangePassword /
   ResetPassword / ForgotPassword) live in VSITodo.Server and render
   SSR — they can't ship per-page .razor.css. Match .todos-page exactly:
   only padding-bottom, no max-width, no margin-auto. The article
   wrapper above (<article class="content px-4">) carries the side
   gutter, and the page content fills the column the way Todos does. */
.accounts-page,
.edit-page,
.account-form-page {
    padding-bottom: 4rem;
}

/* Accounts list table — fills the page column. */
.accounts-page .table {
    width: 100%;
}

/* ===== Usage panel (Phase 19) on /Account/Edit/{id} =====
   Four cards on a 2-col grid at desktop width, collapsing to 1 col on
   mobile. Each card is a dl with the label tone-muted on the left and
   the figure heavier on the right. Read-only; nothing here is
   editable, so the muted card chrome differentiates it from the
   action sections (Roles, Reset password, Delete) below it. */
.usage-panel .usage-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    margin-top: 0.5rem;
}

/* ===== Admin usage dashboard (/admin/usage) ===== */
.usage-dashboard .stat-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 0.75rem;
    margin: 1rem 0;
}

.usage-dashboard .stat-tile {
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    padding: 0.75rem 1rem;
    display: flex;
    flex-direction: column;
}

.usage-dashboard .stat-tile-warn {
    background: #fef9c3;
    border-color: #facc15;
}

.usage-dashboard .stat-tile-cost {
    background: #ecfdf5;
    border-color: #6ee7b7;
}

.usage-dashboard .stat-num {
    font-size: 1.5rem;
    font-weight: 700;
    color: #0f172a;
    line-height: 1.1;
}

.usage-dashboard .stat-label {
    font-size: 0.75rem;
    color: #64748b;
    margin-top: 0.25rem;
}

.usage-panel .usage-card {
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    padding: 0.875rem 1rem;
}

.usage-panel .usage-card h3 {
    margin: 0 0 0.5rem 0;
    letter-spacing: 0.04em;
}

.usage-panel .usage-card dl {
    display: grid;
    grid-template-columns: minmax(8.5rem, max-content) 1fr;
    gap: 0.25rem 0.75rem;
    margin: 0;
    font-size: 0.9rem;
}

.usage-panel .usage-card dt {
    color: #64748b;
    font-weight: 500;
}

.usage-panel .usage-card dd {
    margin: 0;
    color: #0f172a;
    word-break: break-word;
}

@media (max-width: 720px) {
    .usage-panel .usage-grid {
        grid-template-columns: 1fr;
    }
    .usage-panel .usage-card dl {
        grid-template-columns: 1fr;
        gap: 0.1rem 0;
    }
    .usage-panel .usage-card dt {
        margin-top: 0.4rem;
    }
}

html[data-theme="dark"] .usage-panel .usage-card {
    background: #1f2937;
    border-color: #374151;
}
html[data-theme="dark"] .usage-panel .usage-card dt { color: #94a3b8; }
html[data-theme="dark"] .usage-panel .usage-card dd { color: #e5e7eb; }

/* Password input with the eye-toggle button. The button absolute-
   positions inside the form-floating wrapper at the right edge.
   Form-floating's label already sits at the right z-index so we
   only need to make space for the button on the input itself. */
.pw-field-wrap {
    position: relative;
}
.pw-field-wrap > .pw-input {
    padding-right: 2.75rem; /* room for the eye button */
}
.pw-eye-btn {
    position: absolute;
    top: 50%;
    right: 0.5rem;
    transform: translateY(-50%);
    width: 2rem;
    height: 2rem;
    border: 0;
    background: transparent;
    color: #475569;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    z-index: 5;
}
.pw-eye-btn:hover {
    background: rgba(15, 23, 42, 0.05);
    color: #0f172a;
}
.pw-eye-btn:focus-visible {
    outline: 2px solid #2563eb;
    outline-offset: 2px;
}
/* Hide the alternate icon depending on visibility state. The script
   below toggles `data-visible` on the wrapper. */
.pw-eye-btn .pw-eye-on { display: inline; }
.pw-eye-btn .pw-eye-off { display: none; }
.pw-field-wrap[data-visible="true"] .pw-eye-btn .pw-eye-on { display: none; }
.pw-field-wrap[data-visible="true"] .pw-eye-btn .pw-eye-off { display: inline; }

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* WebAssembly boot indicator. Shown on every fresh HTML response until a
   WASM-rendered component signals ready via window.vsiTodo.markWasmReady().
   The 400ms fade-in delay means users with warm caches never see it. */
.app-boot-pill {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 2000;
    background: var(--ink-strong, #111827);
    color: #fff;
    padding: 0.5rem 0.875rem;
    border-radius: 999px;
    font-size: 0.875rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    opacity: 0;
    pointer-events: none;
    animation: app-boot-fade-in 220ms ease-out 400ms forwards;
    transition: opacity 220ms ease-out;
    box-shadow: 0 6px 20px rgba(0,0,0,0.18);
}
html.wasm-ready .app-boot-pill {
    opacity: 0 !important;
    animation: none;
    visibility: hidden;
}
.app-boot-pill-spinner {
    width: 16px;
    height: 16px;
    border: 2px solid rgba(255,255,255,0.25);
    border-top-color: #fff;
    border-radius: 50%;
    animation: app-boot-spin 800ms linear infinite;
}
@keyframes app-boot-fade-in { to { opacity: 0.95; } }
@keyframes app-boot-spin { to { transform: rotate(360deg); } }

/* Full-screen boot splash. Server-rendered in App.razor so it paints with
   the first byte of HTML — the user sees the brand + spinner immediately,
   even while the WebAssembly runtime is still downloading on a slow link.
   Only shown on app routes (html.app-route); /account pages stay usable and
   use the corner pill instead. Fades out the instant WASM signals ready. */
.app-splash {
    display: none;
}
html.app-route .app-splash {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 3000;
    align-items: center;
    justify-content: center;
    background: linear-gradient(160deg, #2563eb 0%, #1e40af 100%);
    color: #fff;
    opacity: 1;
    /* fade-out tuned to feel instant once WASM is ready */
    transition: opacity 280ms ease-out, visibility 0s linear 280ms;
}
/* On app routes the full splash replaces the corner pill. */
html.app-route .app-boot-pill {
    display: none;
}
/* WASM booted — fade the splash away and let pointer events through. */
html.wasm-ready .app-splash {
    opacity: 0 !important;
    visibility: hidden;
    pointer-events: none;
}
.app-splash-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.85rem;
    padding: 2rem;
    text-align: center;
}
.app-splash-logo {
    width: 96px;
    height: 96px;
    border-radius: 22px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.25);
    /* subtle entrance so it doesn't read as a frozen image */
    animation: app-splash-pop 320ms ease-out both;
}
.app-splash-name {
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: 0.01em;
}
.app-splash-text {
    font-size: 0.9rem;
    opacity: 0.85;
}
.app-splash-spinner {
    width: 30px;
    height: 30px;
    border: 3px solid rgba(255,255,255,0.3);
    border-top-color: #fff;
    border-radius: 50%;
    animation: app-boot-spin 800ms linear infinite;
}
@keyframes app-splash-pop {
    from { transform: scale(0.9); opacity: 0; }
    to   { transform: scale(1);   opacity: 1; }
}
/* Dark theme — match the app's dark surface instead of bright blue. */
html[data-theme="dark"].app-route .app-splash {
    background: linear-gradient(160deg, #0f172a 0%, #1e293b 100%);
}
/* Respect reduced-motion: keep the spinner subtle, drop the pop. */
@media (prefers-reduced-motion: reduce) {
    .app-splash-logo { animation: none; }
    .app-splash-spinner { animation-duration: 1400ms; }
}

/* Shared inline loading indicator (LoadingSpinner.razor). Used on every
   data-loading view so a slow fetch shows visible motion instead of a blank
   or static screen. Sits above the skeleton placeholders on list pages and
   stands alone on pages without skeletons. */
.app-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    padding: 1.5rem 1rem;
    color: var(--text-muted, #6b7280);
    font-size: 0.95rem;
}
.app-loading-spinner {
    width: 22px;
    height: 22px;
    border: 3px solid rgba(37, 99, 235, 0.25);
    border-top-color: #2563eb;
    border-radius: 50%;
    animation: app-boot-spin 800ms linear infinite;
    flex: 0 0 auto;
}
.app-loading-text { font-weight: 500; }
@media (prefers-reduced-motion: reduce) {
    .app-loading-spinner { animation-duration: 1400ms; }
}

/* Inline per-row action buttons (Done / Tomorrow / Next week).
   Shared by /todos cards, /clients/{id} todo rows, and /week rows.
   Lives outside scoped CSS so the same look applies everywhere. */
.todo-actions-inline {
    display: flex;
    gap: 0.35rem;
    padding-top: 0.5rem;
    border-top: 1px dashed #e5e7eb;
    flex-wrap: nowrap;
}

.action-inline {
    flex: 1 1 auto;
    min-height: 34px;
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    border: 1px solid #d1d5db;
    background: #fff;
    color: #374151;
    font-weight: 600;
    font-size: 0.8125rem;
    cursor: pointer;
    transition: background-color 120ms, border-color 120ms, color 120ms;
}

.action-inline:hover {
    border-color: #9ca3af;
    background: #f9fafb;
}

.action-inline.action-done {
    background: #2563eb;
    color: #fff;
    border-color: #2563eb;
}

.action-inline.action-done:hover {
    background: #1d4ed8;
    border-color: #1d4ed8;
}

.action-inline.action-restore {
    background: #eff6ff;
    color: #2563eb;
    border-color: #2563eb;
}

.action-inline.action-restore:hover {
    background: #dbeafe;
}

/* /week's row-item is a CSS grid; the action row needs to span the full
   row instead of squeezing into the last column. */
.row-item .todo-actions-inline.row-actions {
    grid-column: 1 / -1;
    margin-top: 0.35rem;
}

/* ============================================================
   Development feature (VSIDirigent orchestrator)
   ============================================================ */
/* No max-width: the jobs grid is the widest thing in the app and the opdracht
   column is the one you actually read, so it gets every pixel the window has.
   Capping the page at 1200px left a third of a wide monitor empty while the
   opdracht was clipped after a handful of words. */
.development-page {
    padding: 1rem;
    width: 100%;
}

.development-header h1,
.development-detail-header h1 {
    margin: 0;
}

.development-create {
    background: var(--bs-body-bg, #fff);
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 8px;
    padding: 1rem;
    margin: 1rem 0 1.5rem;
    max-width: 720px;
}

.development-field {
    margin-bottom: 0.75rem;
}

.development-field label {
    display: block;
    font-weight: 600;
    margin-bottom: 0.25rem;
}

.development-actions {
    display: flex;
    align-items: center;
}

.development-subtitle {
    margin-top: 1rem;
}

/* Status badge — shared by the grid and the detail header. */
.dev-status {
    display: inline-block;
    padding: 0.15rem 0.55rem;
    border-radius: 12px;
    font-size: 0.8rem;
    font-weight: 600;
    color: #fff;
    white-space: nowrap;
}

.dev-status-queued { background: #6c757d; }
.dev-status-validating { background: #0dcaf0; color: #053; }
.dev-status-running { background: #0d6efd; }
.dev-status-completed { background: #198754; }
.dev-status-failed { background: #dc3545; }
.dev-status-cancelled { background: #495057; }
.dev-status-rolledback { background: #6f42c1; }
.dev-status-rejected { background: #fd7e14; }
/* Reviewed = done + acknowledged. Calm violet, success-like (never red). */
.dev-status-reviewed { background: #7048e8; }
/* Paused = parked on purpose. Amber, and the hex is copied verbatim from
   VSIDirigent's .dg-badge--paused so the same job reads the same colour in
   both apps. It pulses (see below) because a parked task must be
   unmistakable — it is the one state where nothing will ever happen unless
   you act. */
.dev-status-paused { background: #d98e04; }

/* Running is the only live state: pulse it so the overview shows at a glance
   that something is actually happening, instead of a static blue chip that
   looks the same as a stalled job. Brightness + halo, no size change — a
   growing chip would reflow the row on every beat. */
.dev-status-running {
    animation: dev-status-pulse 1.4s ease-in-out infinite;
}

@keyframes dev-status-pulse {
    0%, 100% {
        background: #0d6efd;
        box-shadow: 0 0 0 0 rgba(13, 110, 253, 0.55);
    }

    50% {
        background: #4d94ff;
        box-shadow: 0 0 0 4px rgba(13, 110, 253, 0);
    }
}

/* Paused pulses too, but slower and in amber, so a parked task stands apart
   from both the calm grey "Queued" and the busy blue "Running" at a glance.
   Timing and keyframes match VSIDirigent's badge. */
.dev-status-paused {
    animation: dev-status-paused-pulse 1.9s ease-in-out infinite;
}

@keyframes dev-status-paused-pulse {
    0%, 100% {
        opacity: 1;
        box-shadow: 0 0 0 0 rgba(217, 142, 4, 0.65);
    }

    50% {
        opacity: 0.7;
        box-shadow: 0 0 0 5px rgba(217, 142, 4, 0);
    }
}

/* Users who asked the OS for less motion get the plain chip. */
@media (prefers-reduced-motion: reduce) {
    .dev-status-running {
        animation: none;
    }

    /* Keep the amber — only the motion goes. */
    .dev-status-paused {
        animation: none;
    }
}

/* ── VSI Dirigent health ─────────────────────────────────────────────────
   Warning surface for "the orchestrator is not actually running". Kept out of
   the .razor files per project convention. */

/* The ⚠ next to a status chip whose job has stopped moving. Deliberately NOT
   animated: the running chip already pulses, and a pulsing warning next to a
   pulsing status reads as decoration instead of a problem. */
.dev-status-stale {
    display: inline-block;
    margin-left: 0.35rem;
    color: #b02a37;
    font-size: 0.95rem;
    line-height: 1;
    cursor: help;
    vertical-align: middle;
}

/* On the detail page the marker carries its own text, so it becomes a chip. */
.dev-status-stale--inline {
    padding: 0.15rem 0.55rem;
    border-radius: 12px;
    background: #f8d7da;
    border: 1px solid #f1aeb5;
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
}

.dirigent-health {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    margin: 0 0 1rem;
    padding: 0.85rem 1rem;
    border-radius: 10px;
    border: 1px solid transparent;
    background: #fff3cd;
    border-color: #ffe69c;
    color: #664d03;
}

/* Offline / disabled: nothing is being picked up at all — the loud one. */
.dirigent-health--offline {
    background: #f8d7da;
    border-color: #f1aeb5;
    color: #58151c;
}

/* Online but with frozen job rows: something is off, but the host is there. */
.dirigent-health--stale {
    background: #fff3cd;
    border-color: #ffe69c;
    color: #664d03;
}

/* Planned states — informative, not alarming. */
.dirigent-health--stopped,
.dirigent-health--paused,
.dirigent-health--unknown {
    background: #e7f1ff;
    border-color: #b6d4fe;
    color: #052c65;
}

.dirigent-health__icon {
    font-size: 1.35rem;
    line-height: 1.2;
    flex: 0 0 auto;
}

.dirigent-health__body {
    flex: 1 1 auto;
    min-width: 0;
}

.dirigent-health__message {
    margin: 0;
    font-weight: 600;
}

.dirigent-health__hint,
.dirigent-health__meta,
.dirigent-health__result {
    margin: 0.35rem 0 0;
    font-size: 0.85rem;
}

.dirigent-health__meta {
    opacity: 0.8;
}

.dirigent-health__result {
    font-weight: 600;
}

.dirigent-health__jobs {
    margin: 0.4rem 0 0;
    padding-left: 1.1rem;
    font-size: 0.85rem;
}

.dirigent-health__jobs li {
    margin-bottom: 0.15rem;
}

.dirigent-health__job-app {
    margin-left: 0.4rem;
    font-weight: 600;
}

.dirigent-health__job-quiet {
    margin-left: 0.4rem;
    opacity: 0.85;
}

.dirigent-health__actions {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    flex: 0 0 auto;
}

/* 48px minimum touch target — this banner is read on a phone between
   meetings, which is exactly when you need to know the agent stopped. */
.dirigent-health__btn {
    min-height: 48px;
    padding: 0.4rem 0.85rem;
    border-radius: 8px;
    border: 1px solid currentColor;
    background: transparent;
    color: inherit;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}

.dirigent-health__btn:disabled {
    opacity: 0.55;
    cursor: default;
}

.dirigent-health__btn--danger {
    background: #b02a37;
    border-color: #b02a37;
    color: #fff;
}

/* On a phone the action column would squeeze the text to a few characters. */
@media (max-width: 700px) {
    .dirigent-health {
        flex-wrap: wrap;
    }

    .dirigent-health__actions {
        flex-direction: row;
        flex-wrap: wrap;
        width: 100%;
    }

    .dirigent-health__btn {
        flex: 1 1 auto;
    }
}

/* Priority chip — High stands out, Low is muted, Normal is plain. */
.dev-prio {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: 10px;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.dev-prio-high {
    background: #fff3bf;
    color: #b8860b;
    border: 1px solid #ffe066;
}

.dev-prio-normal {
    background: #f1f3f5;
    color: #868e96;
}

.dev-prio-low {
    background: #e9ecef;
    color: #adb5bd;
}

/* ---- Job detail ---- */
.development-detail-top {
    margin-bottom: 0.75rem;
}

.development-detail-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.development-job-id {
    color: #6c757d;
    font-weight: 400;
}

.development-status-message {
    margin: 0.5rem 0;
    font-style: italic;
}

.development-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1.25rem;
    color: #6c757d;
    font-size: 0.9rem;
    margin: 0.5rem 0 1rem;
}

.development-detail-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 1rem;
}

.development-section {
    margin-bottom: 1.5rem;
}

.development-section h2 {
    font-size: 1.1rem;
    margin-bottom: 0.5rem;
}

.development-command,
.development-summary {
    background: #f6f8fa;
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 6px;
    padding: 0.75rem 1rem;
    word-break: break-word;
    margin: 0;
}

/* The command box is a <pre>, which keeps the typed line breaks but does NOT
   wrap by default — a long line ran off the right edge. pre-wrap keeps the
   original breaks AND wraps, so the box grows to exactly the height of its
   text (auto height) instead of scrolling sideways. Not applied to
   .development-summary: that one is rendered Markdown in a <div>, where
   pre-wrap would turn source whitespace into visible blank lines.
   The box grows with its text up to 20rem and scrolls vertically beyond
   that, so a very long command can't push the rest of the page off screen. */
.development-command {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    height: auto;
    max-height: 20rem;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* Rendered Markdown (MarkdownView). Shared look for agent results and any
   other Markdown surface. Tuned to read as a document, not a code dump. */
.markdown-body {
    line-height: 1.55;
    overflow-wrap: anywhere;
}

.markdown-body > :first-child {
    margin-top: 0;
}

.markdown-body > :last-child {
    margin-bottom: 0;
}

.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
    margin: 1.1em 0 0.4em;
    line-height: 1.25;
    font-weight: 600;
}

.markdown-body h2 { font-size: 1.35rem; }
.markdown-body h3 { font-size: 1.15rem; }
.markdown-body h4 { font-size: 1.02rem; }
.markdown-body h5,
.markdown-body h6 { font-size: 0.95rem; }

.markdown-body p {
    margin: 0 0 0.6em;
}

.markdown-body ul,
.markdown-body ol {
    margin: 0 0 0.6em;
    padding-left: 1.5rem;
}

.markdown-body li {
    margin: 0.15em 0;
}

.markdown-body li > ul,
.markdown-body li > ol {
    margin: 0.15em 0;
}

.markdown-body code {
    background: rgba(27, 31, 35, 0.06);
    border-radius: 4px;
    padding: 0.12em 0.35em;
    font-size: 0.88em;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

.markdown-body pre.markdown-code {
    background: #1e1e1e;
    color: #e6e6e6;
    border-radius: 6px;
    padding: 0.75rem 1rem;
    overflow-x: auto;
    margin: 0 0 0.7em;
}

.markdown-body pre.markdown-code code {
    background: transparent;
    padding: 0;
    color: inherit;
    font-size: 0.85rem;
    white-space: pre;
}

.markdown-body blockquote.markdown-quote {
    margin: 0 0 0.7em;
    padding: 0.3em 0.9em;
    border-left: 3px solid #b6c2cf;
    color: #57606a;
    background: rgba(0, 0, 0, 0.02);
}

.markdown-body table.markdown-table {
    border-collapse: collapse;
    margin: 0 0 0.7em;
    width: 100%;
    font-size: 0.92rem;
}

.markdown-body table.markdown-table th,
.markdown-body table.markdown-table td {
    border: 1px solid rgba(0, 0, 0, 0.15);
    padding: 0.35em 0.6em;
    text-align: left;
    vertical-align: top;
}

.markdown-body table.markdown-table th {
    background: rgba(0, 0, 0, 0.04);
    font-weight: 600;
}

.markdown-body hr {
    border: 0;
    border-top: 1px solid rgba(0, 0, 0, 0.12);
    margin: 1em 0;
}

.markdown-body a {
    color: #0969da;
    text-decoration: underline;
}

/* Documents generated during a job, listed under the result. Touch-friendly
   rows (48px min height) because this is read on the phone between meetings. */
.dev-files {
    margin-top: 1rem;
}

.dev-files-title {
    font-weight: 600;
    margin-bottom: 0.5rem;
}

.dev-files-msg {
    margin-top: 1rem;
}

.dev-files-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.dev-files-item {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    min-height: 48px;
    padding: 0.5rem 0;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.dev-files-badge {
    flex: 0 0 auto;
    border-radius: 999px;
    padding: 0.15rem 0.6rem;
    font-size: 0.75rem;
    font-weight: 600;
    color: #fff;
}

.dev-files-badge--added {
    background: #198754;
}

.dev-files-badge--modified {
    background: #0d6efd;
}

.dev-files-badge--deleted {
    background: #dc3545;
}

.dev-files-badge--renamed {
    background: #fd7e14;
}

.dev-files-name {
    flex: 1 1 12rem;
    word-break: break-all;
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.85rem;
}

.dev-files-size {
    flex: 0 0 auto;
    font-size: 0.75rem;
    color: #6c757d;
}

.dev-files-action {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 0.75rem;
}

.development-live {
    color: #dc3545;
    font-size: 0.8rem;
    margin-left: 0.5rem;
    animation: dev-pulse 1.5s ease-in-out infinite;
}

@keyframes dev-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; }
}

/* Live activity log — fixed height, scrolls, newest at bottom. */
.development-log {
    background: #1e1e1e;
    color: #e6e6e6;
    border-radius: 6px;
    padding: 0.5rem 0.75rem;
    height: 420px;
    overflow-y: auto;
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.85rem;
    line-height: 1.5;
}

.development-log-empty {
    color: #888;
    font-style: italic;
}

.development-log-line {
    display: flex;
    gap: 0.5rem;
    align-items: baseline;
    padding: 0.1rem 0;
}

.development-log-time {
    color: #888;
    flex: 0 0 auto;
}

.development-log-level {
    flex: 0 0 5.5rem;
    font-weight: 600;
    text-transform: uppercase;
    font-size: 0.72rem;
}

.development-log-msg {
    flex: 1 1 auto;
    white-space: pre-wrap;
    word-break: break-word;
}

.dev-log-info { color: #9cdcfe; }
.dev-log-agent { color: #d4d4d4; }
.dev-log-tooluse { color: #c586c0; }
.dev-log-status { color: #4ec9b0; }
.dev-log-warning { color: #dcdcaa; }
.dev-log-error { color: #f48771; }

/* Status/info/warning/error/tool lines must be unmistakable against the wall of
   agent output and code that fills most of the log: a colored left rail, a
   tinted row background, a filled level chip and a bolder message. Plain agent
   output keeps the bare row so the contrast stays meaningful. */
.development-log-line.dev-line-status,
.development-log-line.dev-line-info,
.development-log-line.dev-line-warning,
.development-log-line.dev-line-error,
.development-log-line.dev-line-tooluse {
    border-left: 3px solid transparent;
    border-radius: 4px;
    padding: 0.25rem 0.45rem;
    margin: 0.25rem 0;
}

    .development-log-line.dev-line-status .development-log-level,
    .development-log-line.dev-line-info .development-log-level,
    .development-log-line.dev-line-warning .development-log-level,
    .development-log-line.dev-line-error .development-log-level,
    .development-log-line.dev-line-tooluse .development-log-level {
        flex: 0 0 auto;
        min-width: 5.5rem;
        text-align: center;
        border-radius: 999px;
        padding: 0.05rem 0.45rem;
        font-weight: 700;
        letter-spacing: 0.04em;
    }

    .development-log-line.dev-line-status .development-log-msg,
    .development-log-line.dev-line-warning .development-log-msg,
    .development-log-line.dev-line-error .development-log-msg {
        font-weight: 600;
    }

.dev-line-status {
    border-left-color: #4ec9b0;
    background: rgba(78, 201, 176, 0.14);
}

    .dev-line-status .development-log-level {
        background: #4ec9b0;
        color: #05302b;
    }

    .dev-line-status .development-log-msg { color: #8fe6d4; }

.dev-line-info {
    border-left-color: #9cdcfe;
    background: rgba(156, 220, 254, 0.10);
}

    .dev-line-info .development-log-level {
        background: #9cdcfe;
        color: #0b2b3d;
    }

.dev-line-warning {
    border-left-color: #dcdcaa;
    background: rgba(220, 220, 170, 0.14);
}

    .dev-line-warning .development-log-level {
        background: #dcdcaa;
        color: #3b3a1a;
    }

    .dev-line-warning .development-log-msg { color: #ececc0; }

.dev-line-error {
    border-left-color: #f48771;
    background: rgba(244, 135, 113, 0.16);
}

    .dev-line-error .development-log-level {
        background: #f48771;
        color: #3d1109;
    }

    .dev-line-error .development-log-msg { color: #ffb3a2; }

.dev-line-tooluse {
    border-left-color: #c586c0;
    background: rgba(197, 134, 192, 0.10);
}

    .dev-line-tooluse .development-log-level {
        background: #c586c0;
        color: #33113a;
    }

/* ---- Dictation row under the Opdracht box in the new-job sheet ---- */
.dev-cmd-mic {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-top: 0.45rem;
}

.dev-cmd-mic-btn {
    flex: 0 0 auto;
    min-height: 48px; /* thumb target */
    padding: 0.5rem 1rem;
    border-radius: 999px;
    border: 1px solid #0d6efd;
    background: #fff;
    color: #0d6efd;
    font-weight: 600;
    font-size: 0.9rem;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    cursor: pointer;
}

.dev-cmd-mic-btn:hover {
    background: #eef4ff;
}

.dev-cmd-mic-btn:disabled {
    opacity: 0.6;
    cursor: default;
}

/* Recording is the one state that must be unmissable — you're talking to it. */
.dev-cmd-mic-btn.recording {
    background: #dc3545;
    border-color: #dc3545;
    color: #fff;
    animation: dev-pulse 1.2s ease-in-out infinite;
}

.dev-cmd-mic-hint {
    flex: 1 1 12rem;
    font-size: 0.78rem;
    color: #6c757d;
    line-height: 1.3;
}

/* ---- Mic dictation on the create form ---- */
.development-cmd-row {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
}

.development-cmd-row textarea {
    flex: 1 1 auto;
}

.dev-mic-btn {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: 1px solid #0d6efd;
    background: #fff;
    color: #0d6efd;
    font-size: 1.25rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.dev-mic-btn:disabled {
    opacity: 0.6;
    cursor: default;
}

.dev-mic-btn.recording {
    background: #dc3545;
    border-color: #dc3545;
    color: #fff;
    animation: dev-pulse 1.2s ease-in-out infinite;
}

.dev-mic-spinner {
    width: 18px;
    height: 18px;
    border: 2px solid rgba(13, 110, 253, 0.3);
    border-top-color: #0d6efd;
    border-radius: 50%;
    animation: dev-spin 0.8s linear infinite;
}

@keyframes dev-spin {
    to { transform: rotate(360deg); }
}

.dev-mic-status {
    margin-top: 0.35rem;
    font-size: 0.85rem;
    color: #6c757d;
}

.dev-mic-status.recording {
    color: #dc3545;
    font-weight: 600;
}

/* ---- Compact detail header + Ask status ---- */
.development-compact-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-bottom: 0.5rem;
}

.development-compact-header h1 {
    margin: 0;
    font-size: 1.5rem;
}

.development-init {
    margin: 0.5rem 0 0.75rem;
}

.development-askstatus {
    background: #eef5ff;
    border: 1px solid #cfe2ff;
    border-radius: 6px;
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.75rem;
    font-size: 0.9rem;
}

.development-log-init {
    margin-bottom: 0.5rem;
}

/* ---- Custom tab strip (mobile-friendly) ---- */
.dev-tabs {
    display: flex;
    gap: 0.25rem;
    border-bottom: 2px solid #dee2e6;
    margin-bottom: 1rem;
}

.dev-tab {
    flex: 1 1 0;
    min-height: 48px;
    border: none;
    background: transparent;
    color: #495057;
    font-weight: 600;
    padding: 0.5rem 0.75rem;
    cursor: pointer;
    border-bottom: 3px solid transparent;
    margin-bottom: -2px;
}

.dev-tab.active {
    color: #0d6efd;
    border-bottom-color: #0d6efd;
}

.dev-tab .development-live {
    margin-left: 0.25rem;
}

/* ---- Mobile job cards (hidden on desktop) ---- */
.development-cards-mobile {
    display: none;
}

.development-card {
    display: block;
    text-decoration: none;
    color: inherit;
    border: 1px solid rgba(0, 0, 0, 0.12);
    border-radius: 8px;
    padding: 0.75rem;
    margin-bottom: 0.6rem;
    background: #fff;
}

.development-card-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.development-card-app {
    font-weight: 700;
}

/* Job (task) number — shown in the grid and on the mobile card so a job can
   be referenced by its number (the #N used in safety commits and follow-ups). */
.dev-job-nr {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: #868e96;
    /* Also used as an <a> in the grid (a second way into the job detail);
       it should still read as a job number, not as a blue link. */
    text-decoration: none;
}

a.dev-job-nr:hover {
    color: #0d6efd;
}

.development-card-cmd {
    margin-top: 0.35rem;
    color: #333;
    word-break: break-word;
}

.development-card-status {
    margin-top: 0.35rem;
    font-style: italic;
    color: #6c757d;
    font-size: 0.9rem;
}

.development-card-meta {
    display: flex;
    justify-content: space-between;
    margin-top: 0.5rem;
    color: #6c757d;
    font-size: 0.85rem;
}

/* On narrow screens swap the wide grid for the stacked cards. */
@media (max-width: 700px) {
    .development-grid-desktop {
        display: none;
    }
    .development-cards-mobile {
        display: block;
    }
    .development-page {
        padding: 0.75rem;
    }
    .development-create {
        padding: 0.75rem;
    }
    .development-log {
        height: 320px;
        font-size: 0.8rem;
    }
}

/* ---- Job filter bar (search + status pills) ----
   Sits above BOTH the desktop grid and the mobile cards, because it filters
   the data behind them rather than the grid's own filter row. Wraps to two
   rows on a phone: search on top, pills underneath, horizontally scrollable
   so six pills never squash into unreadable stubs. */
.dev-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

.dev-filter-search {
    flex: 1 1 260px;
    max-width: 420px;
    min-height: 44px;
}

.dev-filter-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.dev-filter-pills .dev-pill-sm {
    min-height: 38px;
    padding: 0.25rem 0.75rem;
    font-size: 0.85rem;
    border: 1px solid #0d6efd;
    border-radius: 999px;
}

.dev-filter-count {
    display: inline-block;
    margin-left: 0.3rem;
    padding: 0 0.35rem;
    border-radius: 999px;
    background: rgba(13, 110, 253, 0.12);
    font-size: 0.75rem;
    font-weight: 700;
}

.dev-pill-sm.active .dev-filter-count {
    background: rgba(255, 255, 255, 0.25);
}

.dev-filter-empty {
    margin: 0.5rem 0 0.75rem;
}

@media (max-width: 700px) {
    .dev-filter-search {
        flex: 1 1 100%;
        max-width: none;
    }
    .dev-filter-pills {
        flex-wrap: nowrap;
        overflow-x: auto;
        width: 100%;
        padding-bottom: 0.2rem;
    }
    .dev-filter-pills .dev-pill-sm {
        flex: 0 0 auto;
    }
}

/* ---- Model pills + commit checkbox ---- */
.dev-model-pills {
    display: inline-flex;
    border: 1px solid #0d6efd;
    border-radius: 999px;
    overflow: hidden;
}

.dev-pill {
    min-height: 40px;
    padding: 0.35rem 1.1rem;
    border: none;
    background: #fff;
    color: #0d6efd;
    font-weight: 600;
    cursor: pointer;
}

.dev-pill.active {
    background: #0d6efd;
    color: #fff;
}

.dev-check {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 500;
    min-height: 44px;
    cursor: pointer;
}

.dev-check input {
    width: 20px;
    height: 20px;
}

/* Small grey hint under a per-job toggle. Indented to align with the
   checkbox label text and given bottom spacing so stacked toggles read
   as separate rows. */
.dev-check-hint {
    margin: 0.15rem 0 0.75rem 1.75rem;
    font-size: 0.8125rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* ---- Attachments ---- */
.development-attach-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.dev-file-hidden {
    display: none;
}

.dev-upload-btn {
    margin: 0;
    cursor: pointer;
}

.dev-attach-hint {
    color: #6c757d;
    font-size: 0.85rem;
}

.development-attach-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.6rem;
}

.dev-attach-item {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.dev-attach-thumb {
    width: 72px;
    height: 72px;
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
}

.dev-attach-chip {
    display: inline-block;
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 0.4rem 0.6rem;
    background: #f1f3f5;
    border: 1px solid rgba(0, 0, 0, 0.12);
    border-radius: 6px;
    font-size: 0.85rem;
}

.dev-attach-remove {
    position: absolute;
    top: -8px;
    right: -8px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: none;
    background: #dc3545;
    color: #fff;
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ---- Dense meta grid (job detail) ---- */
.development-meta-grid {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.15rem 0.75rem;
    margin: 0.5rem 0 1rem;
    font-size: 0.9rem;
}

.development-meta-grid dt {
    color: #6c757d;
    font-weight: 600;
}

.development-meta-grid dd {
    margin: 0;
    word-break: break-word;
}

.development-meta-msg {
    margin-left: 0.4rem;
    font-style: italic;
    color: #6c757d;
}

.development-cmd-label {
    font-weight: 600;
    margin-bottom: 0.25rem;
}

/* Keep both tab panels mounted; just hide the inactive one so the live
   log keeps streaming/scrolling while Summary is shown. */
.dev-hidden {
    display: none;
}
/* ─── Development: mobile-first slide-in sheet ──────────────────────────────
   The create/edit form lives in a bottom sheet instead of permanently on the
   page: on a phone the jobs list is what you came for, and the form only
   appears when you ask for it. The sheet is capped at 92vh with its own
   scroll body and a sticky footer, so the confirm button is always reachable
   with a thumb no matter how long the form gets. */
.dev-sheet-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(17, 24, 39, 0.5);
    z-index: 80;
}

.dev-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 81;
    display: flex;
    flex-direction: column;
    max-height: 92vh;
    background: #fff;
    border-radius: 1rem 1rem 0 0;
    box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.25);
    animation: dev-sheet-up 180ms ease-out;
}

@keyframes dev-sheet-up {
    from { transform: translateY(100%); }
    to { transform: translateY(0); }
}

/* On a wide screen the bottom sheet reads as a centred dialog instead. */
@media (min-width: 720px) {
    .dev-sheet {
        left: 50%;
        right: auto;
        bottom: auto;
        top: 50%;
        transform: translate(-50%, -50%);
        width: 640px;
        max-height: 88vh;
        border-radius: 0.75rem;
        animation: none;
    }
}

.dev-sheet-grip {
    width: 40px;
    height: 4px;
    border-radius: 999px;
    background: #d1d5db;
    margin: 0.5rem auto 0;
}

.dev-sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 1rem;
    border-bottom: 1px solid #e5e7eb;
}

.dev-sheet-head h2 {
    font-size: 1.1rem;
    margin: 0;
}

.dev-sheet-close {
    border: none;
    background: transparent;
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
    color: #6b7280;
    min-width: 44px;
    min-height: 44px;
}

.dev-sheet-body {
    overflow-y: auto;
    padding: 0.75rem 1rem;
    flex: 1 1 auto;
}

.dev-sheet-foot {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
    padding: 0.75rem 1rem;
    border-top: 1px solid #e5e7eb;
    background: #fff;
    border-radius: 0 0 1rem 1rem;
}

/* The AI confirmation banner. Green when the model filled the form in;
   amber when AI was off and you're looking at your raw dictation. */
.dev-ai-banner {
    margin: 0.75rem 1rem 0;
    padding: 0.6rem 0.75rem;
    border-radius: 0.5rem;
    background: #dcfce7;
    border: 1px solid #bbf7d0;
    color: #166534;
    font-size: 0.85rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.dev-ai-banner.raw {
    background: #fef3c7;
    border-color: #fde68a;
    color: #92400e;
}

/* Follow-up source banner in the create sheet — a blue "info" strip that keeps
   the originating job in view while the new command is written. */
.dev-followup-banner {
    margin: 0.75rem 1rem 0;
    padding: 0.6rem 0.75rem;
    border-radius: 0.5rem;
    background: #dbeafe;
    border: 1px solid #bfdbfe;
    color: #1e40af;
    font-size: 0.85rem;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.dev-followup-cmd {
    font-style: italic;
    opacity: 0.9;
}

.dev-followup-link {
    align-self: flex-start;
    font-weight: 600;
    color: #1d4ed8;
}

.dev-transcript summary {
    cursor: pointer;
    font-weight: 600;
}

.dev-transcript p {
    margin: 0.35rem 0 0;
    font-style: italic;
}

/* Header action buttons — the mic is the primary way in on a phone. */
.development-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.dev-head-actions {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

/* On a phone the two FABs leave the header and dock bottom-right — where a
   floating action button belongs and where the thumb already is. The plus sits
   lowest (the primary action), the mic just above it. */
@media (max-width: 700px) {
    .dev-head-actions {
        position: fixed;
        right: 1rem;
        bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
        flex-direction: column-reverse;
        gap: 0.75rem;
        z-index: 70;
    }

    .dev-head-actions .dev-fab {
        min-width: 56px;
        min-height: 56px;
        box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
    }

    /* Room to scroll the last card clear of the FABs. */
    .development-cards-mobile {
        padding-bottom: 7rem;
    }
}

.dev-fab {
    min-width: 48px;
    min-height: 48px;
    border-radius: 50%;
    border: none;
    font-size: 1.3rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.dev-fab-mic {
    background: #ede9fe;
    color: #5b21b6;
    border: 1px solid #ddd6fe;
}

.dev-fab-mic.recording {
    background: #fee2e2;
    color: #991b1b;
    border-color: #fca5a5;
}

.dev-fab-new {
    background: #2563eb;
    color: #fff;
}

.dev-fab:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* Known-application chips — one tap instead of hunting through the dropdown.
   Deliberately smaller than the model/priority pills: there can be a dozen of
   them and they are a shortcut, not the control itself (the dropdown right
   below is), so they trade the 48px target for fitting several per row on a
   phone. */
.dev-app-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-bottom: 0.4rem;
}

.dev-app-chips .dev-pill-sm {
    min-height: 34px;
    padding: 0.2rem 0.6rem;
    font-size: 0.8rem;
    border: 1px solid #0d6efd;
    border-radius: 999px;
}

/* Quick commands, collapsed by default. */
.dev-collapse-toggle {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: #f9fafb;
    border: 1px solid #e5e7eb;
    border-radius: 0.5rem;
    padding: 0.6rem 0.75rem;
    font-weight: 600;
    font-size: 0.9rem;
    cursor: pointer;
    min-height: 48px;
}

.dev-collapse-hint {
    font-weight: 400;
    font-size: 0.8rem;
    color: #6b7280;
}

.dev-quick-list {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-top: 0.4rem;
}

.dev-quick-btn {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.15rem;
    text-align: left;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 0.5rem;
    padding: 0.6rem 0.75rem;
    cursor: pointer;
    min-height: 48px;
}

.dev-quick-btn:hover {
    background: #f3f4f6;
}

.dev-quick-label {
    font-weight: 600;
    font-size: 0.9rem;
}

.dev-quick-cmd {
    font-size: 0.78rem;
    color: #6b7280;
}

/* Destructive zone at the foot of the job-detail page — deliberately quiet
   (outline button, no btn-lg) so rolling the repo back never competes with the
   everyday actions at the top. */
.dev-danger-zone {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 2rem;
    padding-top: 0.75rem;
    border-top: 1px solid #e5e7eb;
}

.dev-danger-hint {
    color: #6b7280;
    font-size: 0.85rem;
}

/* Schakelaars boven de agent-output log ("Details tonen" + automatisch
   scrollen). Wrapt op een telefoon, waar twee labels naast elkaar niet passen. */
.dev-log-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1.25rem;
}

/* Auto-scroll switch above the agent-output log. */
.dev-log-scroll {
    margin-bottom: 0.4rem;
    font-size: 0.85rem;
}

.dev-reviewed-note {
    color: #166534;
    font-weight: 600;
    font-size: 0.85rem;
}

html[data-theme="dark"] .dev-sheet,
html[data-theme="dark"] .dev-sheet-foot {
    background: #1f2937;
    color: #e5e7eb;
}

html[data-theme="dark"] .dev-quick-btn,
html[data-theme="dark"] .dev-collapse-toggle {
    background: #111827;
    border-color: #374151;
    color: #e5e7eb;
}

/* Filter pills use the same white background as the model pills, which is
   opaque in dark mode too — only the count badge needs toning down so it
   doesn't glow against it. */
html[data-theme="dark"] .dev-filter-count {
    background: rgba(13, 110, 253, 0.18);
}

/* Dictation playback on the job-detail page. Full width so the scrubber is
   usable with a thumb. */
.dev-voice-player {
    width: 100%;
    max-width: 480px;
    margin-top: 0.25rem;
}

/* Quick note button in the todo card's inline action row (between Next week
   and Done). Neutral/violet so it doesn't compete with the green Done. */
.action-note {
    color: #5b21b6;
    border-color: #ddd6fe;
}

.action-note:hover {
    background: #ede9fe;
}

/* Quick-note modal — reuses the .vc-* shell of the voice-capture modal. */
.qn-subject {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    color: #6b7280;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.qn-text {
    width: 100%;
    font-size: 1rem;
}

.qn-actions {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-top: 0.5rem;
}

html[data-theme="dark"] .action-note {
    color: #c4b5fd;
    border-color: #4c1d95;
}

/* Quick-note modal. Its own shell rather than reusing VoiceCaptureModal's
   .vc-* classes: those live in that component's SCOPED stylesheet, so they
   never reach this component and the dialog rendered inline in the page. */
.qn-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(17, 24, 39, 0.5);
    z-index: 90;
}

.qn-modal {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    z-index: 91;
    width: min(560px, calc(100vw - 2rem));
    max-height: 88vh;
    overflow-y: auto;
    background: #fff;
    border-radius: 0.75rem;
    padding: 1rem;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
}

/* On a phone it becomes a bottom sheet — thumb-reachable, no reaching up. */
/* Stays vertically centred on a phone too. It used to drop to a bottom sheet,
   which parked the Save button right on top of the card's action row — after a
   transcription you were reaching for "Notitie opslaan" in the same spot as
   Done/Next week. Centred keeps it clear of everything underneath. */
@media (max-width: 640px) {
    .qn-modal {
        width: calc(100vw - 1.5rem);
        max-height: 80vh;
    }
}

.qn-title {
    font-size: 1.2rem;
    margin: 0 0 0.25rem;
}

.qn-body {
    margin-bottom: 0.75rem;
}

/* Sticky footer: a long transcription grows the body past the modal's
   max-height, and a footer that scrolls with it puts "Notitie opslaan" out of
   reach exactly when you need it. */
.qn-buttons {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
    align-items: center;
    position: sticky;
    bottom: 0;
    background: #fff;
    padding-top: 0.6rem;
    margin-top: 0.25rem;
    border-top: 1px solid #e5e7eb;
}

html[data-theme="dark"] .qn-buttons {
    background: #1f2937;
    border-top-color: #374151;
}

.qn-error {
    color: #b91c1c;
    font-size: 0.85rem;
    margin: 0.5rem 0 0;
}

html[data-theme="dark"] .qn-modal {
    background: #1f2937;
    color: #e5e7eb;
}

/* ── Job colour flags (shared mechanism with VSIDirigent) ──────────────── */
.dev-flag {
    position: relative;
    display: inline-flex;
    align-items: center;
}
.dev-flag__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    padding: 0.15rem;
    cursor: pointer;
    border-radius: 4px;
    line-height: 0;
}
.dev-flag__btn:hover { background: rgba(0, 0, 0, 0.06); }
.dev-flag__svg { display: block; }

/* Popover of swatches, anchored under the flag button. */
.dev-flag__pop {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 50;
    margin-top: 0.25rem;
    display: inline-flex;
    gap: 0.2rem;
    padding: 0.3rem;
    background: #fff;
    border: 1px solid #d1d5db;
    border-radius: 8px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
}
.dev-flag__swatch {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    background: none;
    border: 1px solid transparent;
    border-radius: 4px;
    cursor: pointer;
    line-height: 0;
}
.dev-flag__swatch:hover { background: rgba(0, 0, 0, 0.06); }
.dev-flag__swatch--active { border-color: #0d6efd; background: rgba(0, 0, 0, 0.05); }

html[data-theme="dark"] .dev-flag__pop {
    background: #1f2937;
    border-color: #374151;
}
html[data-theme="dark"] .dev-flag__btn:hover,
html[data-theme="dark"] .dev-flag__swatch:hover { background: rgba(255, 255, 255, 0.08); }

/* ── Development grid: one line per row ───────────────────────────────────
   Kendo grid cells already clip with an ellipsis (overflow:hidden +
   text-overflow:ellipsis in the theme), but they do NOT set white-space, so
   long text still WRAPS and only then clips. On a tablet — where the columns
   are squeezed — "Opdracht" and "Laatste status" broke over four or five
   lines, blowing up the row height so a screenful showed only a handful of
   jobs. Forcing nowrap makes every row exactly one line high; the full text
   is still reachable via the cell's title tooltip and the detail page. */
.development-grid-desktop .k-table-td,
.development-grid-desktop .k-table-th {
    white-space: nowrap;
}

/* The ellipsis lives on the CELL, so an inline child would keep its own
   intrinsic width and overflow out of it — the child has to clip too. The
   flag picker is excluded: it is a positioning context for its popover and
   clipping it would cut the colour swatches off. */
.development-grid-desktop .k-table-td > span:not(.dev-flag) {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
}

/* ----- Opdracht cell -----
   A plain <a> filling the whole cell, so tapping the text opens the job on a
   touch device too (no JavaScript involved). The command wraps over AT MOST
   two lines: enough to see what a job does, bounded enough that the row height
   stays predictable — which is the whole point of the nowrap rule above, kept
   deliberately for this one cell by clamping instead of clipping. */
.dev-jobdesc {
    display: block;
    width: 100%;
    color: inherit;
    text-decoration: none;
    line-height: 1.3;
}

.dev-jobdesc:hover .dev-jobdesc__text {
    color: #0d6efd;
}

.dev-jobdesc__text {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    white-space: normal;
    word-break: break-word;
}

/* Secondary line, only rendered on a narrow screen: carries the values of the
   columns hidden there (model, prioriteit, duur, laatste status). */
.dev-jobdesc__meta {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.72rem;
    color: #6c757d;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Rows carry a tap highlight so a touch actually feels acknowledged, and cells
   centre their content now that the opdracht cell can be two lines tall. */
.development-grid-desktop .k-table-td {
    vertical-align: middle;
}

.development-grid-desktop .k-table-row {
    -webkit-tap-highlight-color: rgba(13, 110, 253, 0.15);
}

/* Tablet: trim the vertical padding and type size as well, so the rows are
   genuinely compact and roughly twice as many jobs fit on screen. Column
   visibility/width at this size is handled in Development.razor via
   TelerikMediaQuery — CSS cannot hide a column of a table-layout:fixed grid
   without breaking its colgroup alignment. Breakpoint matches the
   TelerikMediaQuery there (1250px: iPad landscape is 1180, portrait 820). */
@media (max-width: 1250px) {
    .development-grid-narrow .k-table-td {
        padding-block: 0.25rem;
        padding-inline: 0.4rem;
        font-size: 0.85rem;
    }

    .development-grid-narrow .k-table-th {
        padding-block: 0.35rem;
        padding-inline: 0.4rem;
        font-size: 0.8rem;
    }

    /* The Open link is the tallest thing in a row; shrink it to match. */
    .development-grid-narrow .k-table-td .btn-sm {
        padding: 0.1rem 0.4rem;
        font-size: 0.8rem;
        line-height: 1.35;
    }

    .development-grid-narrow .dev-status,
    .development-grid-narrow .dev-prio {
        font-size: 0.75rem;
        padding: 0.1rem 0.35rem;
    }
}

/* When a flag popover is open inside the Telerik grid, stop the cell/row from
   clipping it so the swatches are fully visible. Scoped with :has() to ONLY
   the cell/row that has an open popover. */
.development-grid-desktop .k-table-td:has(.dev-flag--open) { overflow: visible; }
.development-grid-desktop .k-table-row:has(.dev-flag--open) { position: relative; z-index: 5; }

/* The flag toggle pill in the filter bar. */
.dev-pill-flag { white-space: nowrap; }

/* Mobile card: the flag overlaid top-right, OUTSIDE the navigating anchor. */
.development-card-wrap { position: relative; }
/* Holds the two controls that must sit OUTSIDE the card's <a> (a <button>
   inside an anchor still fires the native navigation on tap): the pause
   toggle and the flag picker, in that order. */
.development-card-flag {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 3;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

/* ── Pause / unpause toggle (JobPauseButton) ──────────────────────────────
   In the grid it stays 32px so it does not push the row height past the
   two-line opdracht cell (the flag next to it is ~21px); on the mobile card
   it grows to a proper touch target. */
.dev-pause { display: inline-flex; align-items: center; }

.dev-pause-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 8px;
    background: #fff;
    color: #d98e04;
    cursor: pointer;
}

.dev-pause-btn:hover:not(:disabled) {
    border-color: #d98e04;
    background: #fff8e6;
}

/* Resuming is the "go" action, so it reads blue like the other primaries. */
.dev-pause-btn--resume { color: #0d6efd; }

.dev-pause-btn--resume:hover:not(:disabled) {
    border-color: #0d6efd;
    background: #eff5ff;
}

.dev-pause-btn:disabled {
    opacity: 0.5;
    cursor: default;
}

.dev-pause__svg { display: block; }

/* Mobile card: a real touch target — this is the phone gesture the whole
   feature exists for ("not tonight after all", from the couch). */
.development-card-flag .dev-pause-btn {
    width: 44px;
    height: 44px;
}

/* The verdict line under the filter bar. Neutral by default (a confirmation),
   red only when the server refused the action. */
.dev-pause-msg {
    margin: 0.35rem 0 0.5rem;
    font-size: 0.9rem;
    color: #495057;
}

.dev-pause-msg--error { color: #dc3545; }

/* Job-detail Prev/Next group + position badge, and the detail flag. */
.dev-nav-group {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}
.dev-nav-pos {
    font-size: 0.85rem;
    color: #6b7280;
    min-width: 3.5rem;
    text-align: center;
}
.dev-detail-flag { display: inline-flex; align-items: center; }

/* ───────────────────────── Deployment page (/deployment) ─────────────────
   Owner-only page that installs the newest published zip of a web
   application on the test web server. Mobile-first: one card per
   application, one big touch target per card. */
.deployment-page {
    padding-bottom: 2rem;
}

/* Live status of the deployment being followed. Three states: busy (upload /
   agent working), ok, error. */
.deploy-banner {
    border: 1px solid #d1d5db;
    border-left-width: 4px;
    border-radius: 8px;
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    background: #f9fafb;
}
.deploy-banner--busy { border-left-color: #2563eb; }
.deploy-banner--ok    { border-left-color: #16a34a; background: #f0fdf4; }
.deploy-banner--error { border-left-color: #dc2626; background: #fef2f2; }

.deploy-banner-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    justify-content: space-between;
}
.deploy-banner-msg { margin-top: 0.25rem; }

.deploy-status-badge {
    font-size: 0.8rem;
    font-weight: 600;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: #e5e7eb;
    white-space: nowrap;
}

/* The agent's full install log — collapsed by default, it can be long. */
.deploy-agent-log { margin-top: 0.5rem; }
.deploy-agent-log pre {
    max-height: 18rem;
    overflow: auto;
    font-size: 0.8rem;
    white-space: pre-wrap;
    word-break: break-word;
    background: #111827;
    color: #e5e7eb;
    padding: 0.75rem;
    border-radius: 6px;
}

.deploy-cards {
    display: grid;
    gap: 1rem;
    grid-template-columns: 1fr;
}
@media (min-width: 992px) {
    .deploy-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.deploy-card {
    border: 1px solid #e5e7eb;
    border-radius: 10px;
    padding: 1rem;
    background: #fff;
}
.deploy-card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    justify-content: space-between;
}
.deploy-card-head h2 {
    font-size: 1.15rem;
    margin: 0;
}

/* Facts grid: label left, value right, wrapping to two lines on a phone. */
.deploy-facts {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.15rem 0.75rem;
    margin: 0.75rem 0;
    font-size: 0.9rem;
}
.deploy-facts dt {
    font-weight: 600;
    color: #4b5563;
}
.deploy-facts dd {
    margin: 0;
    overflow-wrap: anywhere;
}
.deploy-facts dd .section-hint {
    display: block;
    font-size: 0.8rem;
}

.deploy-blocker {
    font-size: 0.9rem;
    margin-bottom: 0.5rem;
}

/* 48px minimum touch target, full width on a phone. */
.deploy-button {
    width: 100%;
    min-height: 48px;
}
