:root {
  /* === Tokens (Atlas Web overhaul 2026-09-28, SPEC 4) =====================
     The Atlas OS brand tokens for the app (app-*), light here and dark in
     the block below. Every colour in this file comes from a custom property;
     tests/test_web_css_lint.py rejects a hex literal anywhere else. The
     names above "Aliases" are the vocabulary components use; the aliases
     keep older rules working until they are rewritten. */
  color-scheme: light;

  /* Surfaces and ink. --surface is where a person types (inputs, cards on a
     tinted pane); --bright is the lifted hover shade. Both used to be
     `var(--surface)` -- a self-reference, which made every rule reading them
     invalid in the light theme (#179, #993). */
  --bg: #e9eef4;
  --bg-elevated: #f7f9fc;
  --surface: #ffffff;
  --bright: #f7f9fc;
  --ink: #0B0D12;
  --muted: #5b6b7c;
  /* --line is for decorative dividers only. Anything a person operates --
     an input, a select, a switch track, a chip -- is outlined in
     --control-line, which holds 3:1 against every surface (WCAG 1.4.11). */
  --line: #c8d2de;
  --line-soft: color-mix(in srgb, var(--line) 55%, transparent);
  --control-line: #6b7b8d;

  /* Accent. --accent fills primary actions; text in the accent colour uses
     --accent-text (the dark theme needs the brighter shade to reach 4.5:1
     on its raised surfaces); --accent-ink is the label on an accent fill;
     --accent-bright is for glows and bars, never for text on light. */
  --accent: #B32B5E;
  --accent-bright: #F2547D;
  --accent-text: var(--accent);
  --accent-ink: #fff5f8;
  --accent-tint: color-mix(in srgb, var(--accent) 12%, transparent);
  /* Accent-coloured text on an accent tint (a badge, a chip): one step
     further from the tint than --accent-text, so it holds 4.5:1 there. */
  --accent-on-tint: #9c2454;
  /* --signal is for fills, bars and icons; in light it reaches only 3.5:1
     as small text, so text in the signal hue uses --signal-text. */
  --signal: #0284c7;
  --signal-text: #075985;

  /* States. Pills put the colour on a 12% tint of itself; each one holds
     4.5:1 there over --bg and --bg-elevated in both themes
     (tests/test_web_contrast.py). Light is one step darker than the brand
     sheet for exactly that reason (#994). --ok keeps its teal: the "no teal"
     rule is about atmosphere, focus and accents, not about "done". */
  --ok: #115e59;
  --fail: #9a3412;
  --danger: #9a3412;
  --danger-ink: #ffffff;
  --run: #075985;
  --queue: #475569;
  --ask: #854d0e;  /* waiting on the person -- not done (--ok), not wrong (--fail) */
  /* Quiet states (hidden, a label chip): --muted is right for body text but
     drops to 4.0:1 on its own 12% tint, so a pill or chip uses --idle. */
  --idle: #4f5d6d;

  /* Machine output: logs and terminals stay dark in both themes. */
  --log-bg: #101820;
  --log-ink: #d5e0ea;
  --log-muted: #9fb0c3;
  --code-bg: #101612;
  --code-ink: #d7e0d9;
  --code-line: #1c2834;

  /* Full-screen embeds (3D view, map, diagram) and the picture lightbox sit
     on their own dark chrome in both themes. */
  --embed-bg: #0b0f14;
  --embed-ink: #e8eef5;
  --embed-muted: #9fb0c3;
  --embed-raised: #1d2733;
  --embed-raised-2: #111821;
  --embed-action: #2d6cdf;
  --embed-link: #7cc4ff;
  --on-dark: #ffffff;
  --scrim-dark: rgba(15, 23, 32, 0.55);
  --scrim-darker: rgba(15, 23, 32, 0.8);

  /* Brand spectrum: logo, rare highlights. */
  --brand-ink: #0B0D12;
  --spectrum-1: #FFC24A;
  --spectrum-2: #FF7A59;
  --spectrum-3: #F2547D;
  --spectrum-4: #A855F7;
  --violet: #7e22ce;
  --green: #22c55e;

  /* Atmosphere: a magenta glow top left, amber top right (#994). */
  --atmosphere-top: #f3f7fb;
  --atmosphere-bottom: #dfe8f1;
  --atmosphere-accent: rgba(242, 84, 125, 0.16);
  --atmosphere-warm: rgba(255, 194, 74, 0.14);
  --grid-line: rgba(15, 23, 32, 0.045);
  --orb-a: rgba(242, 84, 125, 0.22);
  --orb-b: rgba(255, 194, 74, 0.26);
  --overlay-scrim: rgba(15, 23, 32, 0.35);
  --dialog-scrim: rgba(15, 23, 32, 0.42);
  --focus-ring: color-mix(in srgb, var(--accent) 24%, transparent);
  --mask-solid: #000;

  /* Type: Outfit for headings, Source Sans 3 for reading, JetBrains Mono for
     machine output (run numbers, durations, Full-view status labels), never
     for sentences. Self-hosted in web/fonts/ (#994). */
  --font-display: "Outfit", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body: "Source Sans 3", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --fs-setup-title: clamp(1.6rem, 3vw, 2rem);
  --fs-empty-title: clamp(1.35rem, 2.4vw, 1.7rem);
  --fs-stat: clamp(1.45rem, 2.4vw, 1.85rem);
  --fs-pane-title: 1.05rem;
  --fs-section-title: 0.95rem;
  --fs-group-label: 0.74rem;
  --fs-body: 1rem;
  --fs-small: 0.875rem;       /* the floor for text somebody reads */
  --fs-control: 1rem;         /* inputs, selects, textareas: never smaller */
  --fs-status: 0.68rem;       /* Full view: mono uppercase pill */
  --fs-status-simple: 0.8125rem; /* Simple view: a word in sentence case */
  --lh-body: 1.5;

  /* Shape, depth, motion. */
  --radius: 10px;
  --radius-sm: 7px;
  --shadow-soft: 0 1px 0 rgba(15, 23, 32, 0.04), 0 12px 32px rgba(15, 23, 32, 0.06);
  --shadow-app-soft: var(--shadow-soft);
  --shadow-dialog: var(--shadow-soft), 0 24px 64px rgba(15, 23, 32, 0.14);
  --shadow-primary: 0 1px 0 rgba(255, 255, 255, 0.18) inset, 0 8px 20px rgba(179, 43, 94, 0.22);
  --shadow-fab: 0 1px 0 rgba(255, 255, 255, 0.18) inset, 0 10px 26px rgba(179, 43, 94, 0.34);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-fast: 150ms;
  --dur: 250ms;

  /* The app shell (part 2 builds it): sidebar, rail, phone bars, layers.
     --shell-inset is how far the content column starts from the left edge;
     the atmosphere paints from there, so the glows sit under the content
     and never under the opaque sidebar. */
  /* In rem, so the shell grows with the text-size preference: 248px, 88px,
     56px and 64px at the browser's default size, half as much again at
     «Maximal» -- a fixed-width sidebar wrapped every label at 150%. */
  --sidebar-w: 15.5rem;
  --sidebar-rail-w: 5.5rem;
  --shell-inset: 0px;
  --topbar-h: 3.5rem;
  --tabbar-h: 4rem;
  --target-min: 44px;
  --row-min: 56px;
  --z-shell: 40;
  --z-fab: 50;
  --z-voice: 60;
  --z-embed: 70;
  --z-toast: 75;
  --z-overlay: 80;

  /* Aliases: older names still read by rules in this file. */
  --text: var(--ink);
  --fg: var(--ink);
  --text-muted: var(--muted);
  --border: var(--line);
  --warn: var(--ask);
  --mono: var(--font-mono);
  --accent-hot: var(--accent-bright);
  --shadow: var(--shadow-soft);
}

html[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0B0D12;
  --bg-elevated: #151A22;
  --surface: #1A222C;
  --bright: #243040;
  --ink: #E8EEF4;
  --muted: #9AA8B8;
  --line: #2C3644;
  --line-soft: color-mix(in srgb, var(--line) 70%, transparent);
  --control-line: #71829a;
  --accent: #E0457A;
  --accent-bright: #F2547D;
  --accent-text: var(--accent-bright);
  /* Brand ink on an accent fill: #fff5f8 reaches only 3.7:1 here. */
  --accent-ink: #0B0D12;
  --accent-on-tint: #ff85a8;
  --signal: #38bdf8;
  --signal-text: var(--signal);
  --ok: #2dd4bf;
  --fail: #fb923c;
  --danger: #fb923c;
  --danger-ink: #0B0D12;
  --violet: #c084fc;
  --run: #38bdf8;
  --queue: #94a3b8;
  --ask: #fbbf24;  /* waiting on the person -- not done (--ok), not wrong (--fail) */
  --idle: var(--muted);
  --log-bg: #070A0E;
  --log-ink: #d5e0ea;
  --atmosphere-top: #10141B;
  --atmosphere-bottom: #07090C;
  --atmosphere-accent: rgba(242, 84, 125, 0.14);
  --atmosphere-warm: rgba(255, 194, 74, 0.08);
  --grid-line: rgba(232, 238, 244, 0.045);
  --orb-a: rgba(242, 84, 125, 0.16);
  --orb-b: rgba(255, 194, 74, 0.10);
  --overlay-scrim: rgba(0, 0, 0, 0.55);
  --dialog-scrim: rgba(0, 0, 0, 0.6);
  --focus-ring: color-mix(in srgb, var(--accent-bright) 30%, transparent);
  --shadow-soft: 0 1px 0 rgba(0, 0, 0, 0.35), 0 16px 40px rgba(0, 0, 0, 0.45);
  --shadow-dialog: var(--shadow-soft), 0 24px 64px rgba(0, 0, 0, 0.5);
  --shadow-primary: 0 1px 0 rgba(255, 255, 255, 0.14) inset, 0 8px 20px rgba(0, 0, 0, 0.35);
  --shadow-fab: 0 1px 0 rgba(255, 255, 255, 0.14) inset, 0 10px 26px rgba(0, 0, 0, 0.45);
}

/* === Fonts (#994) ========================================================
   Self-hosted, so opening Atlas asks no third party for anything. Each
   family is one variable font per subset (the weight axis covers every
   weight this file uses); unicode-range means a page in German or English
   downloads only the Latin file. Licence: fonts/OFL.txt. */
@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/outfit-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/outfit-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url("fonts/source-sans-3-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url("fonts/source-sans-3-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/jetbrains-mono-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/jetbrains-mono-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  height: 100%;
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.5;
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

/* The view keeps its scale on a phone (see the viewport meta in index.html).
   text-size-adjust: turning the phone sideways no longer inflates the text.
   manipulation: pan still works, double-tap no longer zooms -- and taps stop
   waiting ~300ms to find out whether a second one follows. */
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* === Global state on <html> (Atlas Web overhaul 2026-09-28) ==============
   No px size on html or body: 100% is whatever the browser is set to, so a
   reader who raised the browser's own default keeps it, and every rem in
   this file follows the "Textgrösse" preference below.

   The utility classes fail closed. Each one hides its element until the
   attribute proves otherwise -- a page whose /api/auth/me has not answered
   yet shows no admin control, and one whose mode is unknown is Simple.
   Written once, here, with !important, so no component rule can undo them.
   `.hidden` stays the JS visibility toggle; these are the static gates. */
html { font-size: 100%; }
body { font-size: 1rem; }
html[data-text-size="large"] { font-size: 112.5%; }
html[data-text-size="larger"] { font-size: 125%; }
html[data-text-size="largest"] { font-size: 150%; }

html:not([data-mode="full"]) .only-full { display: none !important; }
html[data-mode="full"] .only-simple { display: none !important; }
html:not([data-role="admin"]) .only-admin { display: none !important; }
html:not([data-hosted="0"]) .not-hosted { display: none !important; }

* {
  touch-action: manipulation;
}

body {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Painted from --shell-inset, so once the sidebar exists (part 2 of the
   overhaul sets the inset on desktop) the glows sit under the content
   column and never behind the opaque sidebar. */
.atmosphere {
  position: fixed;
  inset: 0 0 0 var(--shell-inset);
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background:
    radial-gradient(ellipse 90% 55% at 12% -5%, var(--atmosphere-accent), transparent 55%),
    radial-gradient(ellipse 70% 45% at 95% 0%, var(--atmosphere-warm), transparent 50%),
    linear-gradient(168deg, var(--atmosphere-top) 0%, var(--bg) 45%, var(--atmosphere-bottom) 100%);
}

.atmosphere .orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(40px);
  opacity: 0.7;
  animation: drift 18s var(--ease) infinite alternate;
}

.orb-a {
  width: 46vw;
  height: 46vw;
  max-width: 560px;
  max-height: 560px;
  top: -14%;
  left: -10%;
  background: radial-gradient(circle, var(--orb-a), transparent 68%);
}

.orb-b {
  width: 38vw;
  height: 38vw;
  max-width: 480px;
  max-height: 480px;
  top: -8%;
  right: -12%;
  background: radial-gradient(circle, var(--orb-b), transparent 68%);
  animation-delay: -6s;
  animation-duration: 22s;
}

.atmosphere .grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 44px 44px;
  mask-image: radial-gradient(ellipse 85% 65% at 50% -5%, var(--mask-solid) 15%, transparent 72%);
  opacity: 0.85;
}

@keyframes drift {
  from { transform: translate(0, 0) scale(1); }
  to { transform: translate(3%, 4%) scale(1.08); }
}

.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-shrink: 0;
  padding: 1.1rem 1.6rem;
  border-bottom: 1px solid var(--line);
  /* Opaque: the shell is a surface of its own, not a pane of frosted glass
     over the atmosphere (the sidebar in part 2 inherits this). */
  background: var(--bg-elevated);
  animation: rise 0.55s var(--ease) both;
}

.top-actions {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex-shrink: 0;
}

/* --- Call Atlas (voice) -------------------------------------------------
 * The dot is the whole status indicator: dim when idle, pulsing while
 * connecting, solid accent on a live call. A call is the one action in this
 * UI with an invisible side effect -- an open microphone -- so its state has
 * to be readable at a glance without reading the label.
 */
.voice-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}

.voice-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.35;
  flex-shrink: 0;
}

.voice-btn[data-state="connecting"] .voice-dot {
  opacity: 1;
  animation: voice-pulse 1s ease-in-out infinite;
}

.voice-btn.live {
  border-color: color-mix(in srgb, var(--accent-bright) 65%, var(--line));
  color: var(--accent-bright);
}

.voice-btn.live .voice-dot {
  opacity: 1;
  background: var(--accent-bright);
  animation: voice-pulse 2s ease-in-out infinite;
}

/* Not a dead control (AtlasOS#914): with no key this button is the way to go
   and add one, so it must not feel disabled. `not-allowed` would say the
   opposite of what a click now does, and 0.55 left the one visible
   explanation hard to read. A shade quieter than ready-to-call, no more. */
.voice-btn[data-state="unconfigured"] {
  opacity: 0.8;
}

.voice-btn[data-state="error"] {
  color: var(--danger);
}

.voice-btn[data-state="error"] .voice-dot {
  opacity: 1;
  background: var(--danger);
}

@keyframes voice-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(0.6); opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
  .voice-btn .voice-dot { animation: none !important; }
}

/* Dictation: the microphone next to the task and reply fields
   (dictation.js). Icon-only while idle; while recording it turns the
   danger colour and shows the elapsed time, so an open microphone is
   never mistaken for an idle one. */
.dictate-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  min-height: var(--target-min);
  min-width: var(--target-min);
  padding-inline: 0.6rem;
}

.dictate-btn.hidden {
  display: none;
}

.dictate-icon {
  flex-shrink: 0;
}

.dictate-label:empty {
  display: none;
}

.dictate-label {
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-small);
}

/* .btn in front: the microphone is a .btn.ghost, and `.btn.ghost
   { background: transparent }` further down outranked the tint on source
   order, so a live microphone lost it (the border alone said «recording»). */
.btn.dictate-btn[data-state="recording"] {
  color: var(--danger);
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
}

.dictate-btn[data-state="recording"] .dictate-icon {
  animation: voice-pulse 1.4s ease-in-out infinite;
}

.dictate-btn[data-state="transcribing"],
.dictate-btn[data-state="starting"] {
  cursor: progress;
}

@media (prefers-reduced-motion: reduce) {
  .dictate-btn .dictate-icon { animation: none !important; }
}

.home-foot-tools {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

/* In the reply row it sits between the field and Send. `.send-row .btn`
   gives every button there Send's width; the microphone only needs a
   square. */
.send-row .btn.dictate-btn {
  min-width: var(--target-min);
}

/* The call panel: the visible half of a call. Floats under the header so
 * it survives every view change -- the whole point is that the caller can
 * move around Atlas Web (or have Atlas move them) while the call runs. */
.voice-panel {
  position: fixed;
  top: 4.6rem;
  right: 1.2rem;
  z-index: 60;
  width: min(22rem, calc(100vw - 2rem));
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  color: var(--ink);
  box-shadow: var(--shadow-soft);
  animation: rise 0.3s var(--ease) both;
}

.voice-panel[data-state="live"] {
  border-color: color-mix(in srgb, var(--accent-bright) 55%, var(--line));
}

.voice-panel[data-state="error"] {
  border-color: color-mix(in srgb, var(--danger) 55%, var(--line));
}

.voice-panel-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
}

.voice-panel-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--muted);
  flex-shrink: 0;
}

.voice-panel[data-state="connecting"] .voice-panel-dot {
  background: var(--accent-bright);
  animation: voice-pulse 1s ease-in-out infinite;
}

.voice-panel[data-state="live"] .voice-panel-dot {
  background: var(--accent-bright);
  animation: voice-pulse 2s ease-in-out infinite;
}

.voice-panel.is-muted .voice-panel-dot {
  background: var(--muted);
  animation: none;
}

.voice-panel[data-state="error"] .voice-panel-dot {
  background: var(--danger);
}

.voice-panel-status {
  font-weight: 600;
  flex: 1;
  min-width: 0;
}

.voice-panel-timer,
.voice-panel-cost {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.voice-panel-cost {
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  /* "~USD 0.42" has a space the old "~$0.42" did not; without this the pill
     breaks across two lines on a narrow panel. */
  white-space: nowrap;
}

.voice-transcript {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 11rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.88rem;
  line-height: 1.35;
}

.voice-line .voice-who {
  font-weight: 600;
  color: var(--muted);
  margin-right: 0.25rem;
}

.voice-line.is-atlas .voice-who {
  color: var(--accent-text);
}

.voice-line.is-partial .voice-said {
  color: var(--muted);
}

.voice-panel-notice {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--muted);
}

.voice-panel-notice[data-tone="warn"] {
  color: var(--ask);
}

.voice-panel-notice[data-tone="danger"] {
  color: var(--danger);
}

.voice-panel-actions {
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
}

.voice-panel[data-state="idle"] .voice-panel-actions,
.voice-panel[data-state="error"] .voice-panel-actions,
.voice-panel[data-state="ending"] .voice-panel-actions {
  display: none;
}

.voice-panel[data-state="connecting"] #btn-voice-mute {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .voice-panel,
  .voice-panel .voice-panel-dot { animation: none !important; }
}

.restart-overlay {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--bg) 55%, var(--overlay-scrim));
  backdrop-filter: blur(8px);
}

.restart-overlay.hidden,
.restart-overlay[hidden] {
  display: none;
}

.restart-card {
  padding: 1.35rem 1.6rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-soft);
  max-width: 22rem;
  text-align: center;
  animation: rise 0.4s var(--ease) both;
}

.restart-title {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
}

.restart-detail {
  margin: 0;
  color: var(--muted);
  font-size: 0.92rem;
}

/* An update is queued (ClaudeOS#732). A flush strip under the header, not a
   card: a card with its own margins sat misaligned against the header's
   padding and took ~60px from a layout sized to the viewport, which cut off
   the bottom of the Tasks pane. Same horizontal padding as .top so the text
   lines up with the brand above it; same tone as the blocking setup notice. */
.update-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  flex-shrink: 0;
  gap: 0.2rem 0.5rem;
  margin: 0;
  padding: 0.45rem 1.6rem;
  border-bottom: 1px solid color-mix(in srgb, var(--ask) 38%, var(--line));
  background: color-mix(in srgb, var(--ask) 12%, var(--bg-elevated));
  font-size: var(--fs-small);
  line-height: 1.35;
}

@media (max-width: 820px) {
  .update-banner {
    padding: 0.4rem 1rem;
  }
}

.update-banner.hidden {
  display: none;
}

.update-banner strong {
  font-family: var(--font-display);
}

.brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.28rem;
  min-width: 0;
}

/* The real lockup, not a plate plus the word "Atlas" set in the UI font.
   Height-driven: the artwork is 507x66, so width follows from the ratio. */
.brand-lockup {
  height: 1.85rem;
  width: auto;
  display: block;
  color: var(--ink);
  flex-shrink: 0;
}





.tagline {
  margin: 0;
  padding-left: 0.14rem;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}

.layout {
  display: grid;
  grid-template-columns: minmax(280px, 360px) 1fr;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  animation: rise 0.7s var(--ease) 0.08s both;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.runs-pane, .detail-pane {
  min-height: 0;
  height: 100%;
}

.runs-pane {
  border-right: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg-elevated) 55%, transparent);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.pane-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
  padding: 1rem 1.15rem 0.55rem;
}

.pane-head-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.pane-head h2, .detail-head h2 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-pane-title);
  font-weight: 700;
  letter-spacing: -0.02em;
}

.run-list {
  list-style: none;
  margin: 0;
  padding: 0.35rem 0.65rem 1rem;
  overflow: auto;
  flex: 1;
}

/* Only genuinely new rows (see app.js createRunItem) get the entrance
   animation -- it used to be on every <li>, which replayed for the first
   five rows on every 5s poll because the whole list was rebuilt from
   scratch each time. */
.run-list li.new-item {
  animation: rise 0.45s var(--ease) both;
}

.run-list li + li {
  margin-top: 0.3rem;
}

.run-list li button {
  width: 100%;
  text-align: left;
  border: 1px solid transparent;
  background: transparent;
  padding: 0.8rem 0.85rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: inherit;
  font: inherit;
  transition: background 0.18s ease, border-color 0.18s ease, transform 0.18s var(--ease);
}

.run-list li button:hover {
  background: color-mix(in srgb, var(--bg-elevated) 90%, var(--bright));
  border-color: var(--line-soft);
}

.run-list li button.active {
  background: var(--bg-elevated);
  border-color: color-mix(in srgb, var(--accent-bright) 45%, var(--line));
  box-shadow: inset 3px 0 0 var(--accent-bright);
}

.run-list li button:active {
  transform: scale(0.995);
}

.run-list .rid {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--muted);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

/* When the task was started, at the right-hand end of the id line.
   Recurring routines all produce the same one-line-clamped title, so before
   this the running number was the only thing telling two rows apart and
   "heute Morgen oder letzte Woche?" could not be answered without opening
   every one of them (ClaudeOS#918). */
.run-list .rtime {
  flex: none;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.run-list .rtask {
  margin: 0.28rem 0 0.4rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.run-list .rmeta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 0.55rem;
  align-items: center;
  font-size: 0.75rem;
  color: var(--muted);
}

/* A stalled row's one-click resume (ClaudeOS#722). It sits over the row
   button's status line rather than inside it -- a button cannot hold one --
   so it has to out-rank `.run-list li button` and its :hover above. */
.run-list li {
  position: relative;
}

.run-list li.can-continue .rmeta {
  padding-right: 6.5rem;
}

.run-list li button.run-continue {
  position: absolute;
  right: 0.85rem;
  bottom: 0.8rem;
  width: auto;
  padding: 0.18rem 0.6rem;
  border: 1px solid color-mix(in srgb, var(--run) 55%, transparent);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--run) 12%, transparent);
  color: var(--run);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.run-list li button.run-continue:hover {
  background: color-mix(in srgb, var(--run) 22%, transparent);
  border-color: var(--run);
}

.run-list li button.run-continue:disabled {
  opacity: 0.6;
  cursor: progress;
}

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.18rem 0.5rem;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--fs-status);
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  background: color-mix(in srgb, currentColor 12%, transparent);
  color: var(--queue);
}

/* The status dot carries the state on its own. Every state gets its *own*
 * motion, so the shape of the movement is readable before the label is: a
 * turning ring means work is happening, a stutter means it broke, stillness
 * means it is over. One blinking dot for all of them said only "something".
 *
 * Transform, opacity and box-shadow only -- a list with a dozen live rows
 * stays on the compositor and costs no layout.
 */
.status-pill::before {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.status-pill.completed { color: var(--ok); }
.status-pill.failed, .status-pill.cancelled, .status-pill.stalled { color: var(--fail); }
.status-pill.running, .status-pill.starting {
  color: var(--run);
}

/* Running -- a ring turning on its own axis. Atlas is working and nothing is
 * waiting on anybody. */
.status-pill.running::before {
  width: 0.55rem;
  height: 0.55rem;
  background: transparent;
  border: 1.5px solid currentColor;
  border-top-color: transparent;
  animation: status-spin 0.9s linear infinite;
}

/* Starting -- a dot throwing a ring outward: spinning up, not yet turning. */
.status-pill.starting::before {
  animation: status-ripple 1.6s ease-out infinite;
}

/* Queued -- parked in line. A slow shuffle forward, deliberately low-energy. */
.status-pill.queued, .status-pill.waiting_for_approval { color: var(--queue); }
.status-pill.queued::before {
  animation: status-shuffle 2.4s ease-in-out infinite;
}

/* Waiting for approval -- a patient triple-blink, then a long hold. It is
 * asking, but it can wait. */
.status-pill.waiting_for_approval::before {
  animation: status-await 2.6s ease-in-out infinite;
}

/* Needs input -- a knock on the door. Two quick taps, pause, again: the one
 * motion in here that is trying to get a person's attention. */
.status-pill.needs_input { color: var(--ask); }
.status-pill.needs_input::before { animation: status-knock 1.9s ease-in-out infinite; }

/* Stalled -- the backend process died mid-run without a terminal status.
 * A broken signal: hard, uneven jumps and a dropout, no easing anywhere. It
 * is meant to look wrong, because it is the one non-terminal state that
 * needs a person to clean it up. */
.status-pill.stalled::before {
  animation: status-glitch 1.7s steps(1, end) infinite;
}

/* Terminal states hold perfectly still. Next to everything above, stillness
 * is itself the signal. */
.status-pill.completed::before,
.status-pill.failed::before,
.status-pill.cancelled::before { animation: none; }

.status-pill.unmerged { color: var(--fail); }
.status-pill.unmerged::before { animation: none; }
.status-pill.merged { color: var(--ok); }
.status-pill.merged::before { animation: none; }

@keyframes status-spin {
  to { transform: rotate(1turn); }
}

@keyframes status-ripple {
  0% {
    transform: scale(0.8);
    box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 60%, transparent);
  }
  70% {
    transform: scale(1);
    box-shadow: 0 0 0 0.3rem color-mix(in srgb, currentColor 0%, transparent);
  }
  100% {
    transform: scale(0.8);
    box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 0%, transparent);
  }
}

@keyframes status-shuffle {
  0%, 100% { transform: translateX(-0.09rem); opacity: 0.42; }
  50%      { transform: translateX(0.09rem);  opacity: 0.92; }
}

@keyframes status-await {
  0%, 12%, 24%, 36% { opacity: 1; }
  6%, 18%           { opacity: 0.15; }
  70%               { opacity: 0.48; }
  100%              { opacity: 1; }
}

@keyframes status-knock {
  0%, 40%, 100% { transform: translateY(0); }
  8%            { transform: translateY(-0.17rem); }
  16%           { transform: translateY(0); }
  24%           { transform: translateY(-0.17rem); }
  32%           { transform: translateY(0); }
}

@keyframes status-glitch {
  0%        { transform: translate(0, 0) scaleY(1);              opacity: 1; }
  8%        { transform: translate(0.06rem, 0) scaleY(0.3);      opacity: 0.3; }
  14%       { transform: translate(-0.05rem, 0.03rem) scaleY(1); opacity: 1; }
  20%       { transform: translate(0, 0) scaleY(1);              opacity: 0.12; }
  26%       { transform: translate(0.05rem, -0.03rem) scaleY(0.6); opacity: 1; }
  32%       { transform: translate(-0.04rem, 0) scaleY(1);       opacity: 0.4; }
  38%, 74%  { transform: translate(0, 0) scaleY(1);              opacity: 1; }
  80%       { transform: translate(-0.06rem, 0) scaleY(0.35);    opacity: 0.18; }
  86%, 100% { transform: translate(0, 0) scaleY(1);              opacity: 1; }
}

/* Reduced motion: the colour, the label and (for running) the ring shape
 * already carry the state, so the dot stops moving -- except for the states
 * that are still live, which keep a plain fade, because stillness is what
 * this file uses to mean "over" and they are not over. */
@media (prefers-reduced-motion: reduce) {
  .status-pill::before,
  .ask-badge::before { animation: none !important; }
  .status-pill.running::before,
  .status-pill.starting::before,
  .status-pill.stalled::before,
  .status-pill.waiting_for_approval::before,
  .status-pill.needs_input::before {
    animation: status-fade 2s ease-in-out infinite !important;
  }
}

@keyframes status-fade {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.4; }
}

.detail-pane {
  /* No padding-top on purpose. The pinned .detail-head below supplies its own,
     because a sticky element is clamped to its CONTAINING BLOCK (#detail), and
     that block's content box starts below whatever padding this scroller has.
     With 1.25rem here the bar could never reach the top of the scrollport, so a
     20px strip of the conversation was painted above it at every scroll offset
     (ClaudeOS#918). The negative margin only moves the static position; it does
     not move the clamp. */
  padding: 0 1.5rem 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  overflow: auto;
}

.detail {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  flex: 1;
  animation: rise 0.4s var(--ease) both;
}

.detail-empty {
  margin: auto;
  text-align: center;
  color: var(--muted);
  display: grid;
  gap: 0.85rem;
  justify-items: center;
  width: min(100%, 36rem);
  padding: 0.5rem 0 1rem;
}

.empty-mark {
  width: 3.4rem;
  height: 3.4rem;
  border-radius: 14px;
  background: var(--brand-ink);
  box-shadow: inset 0 0 0 1px rgba(242, 84, 125, 0.28), 0 10px 28px rgba(11, 13, 18, 0.12);
  display: grid;
  place-items: center;
}

.empty-mark svg {
  width: 1.6rem;
  height: auto;
}

.empty-title {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-empty-title);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.2;
}

.empty-sub {
  margin: 0;
  max-width: 28rem;
  font-size: 0.95rem;
  line-height: 1.45;
}

.empty-run-form {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  text-align: left;
  margin-top: 0.35rem;
}

.empty-run-form textarea {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--fs-control);
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
  min-height: 5.5rem;
  line-height: 1.45;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.empty-run-form textarea:focus {
  border-color: var(--accent);
}

.empty-run-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem 0.65rem;
  align-items: flex-end;
}

.empty-run-field {
  display: flex;
  flex-direction: column;
  gap: 0.28rem;
  flex: 1 1 8.5rem;
  min-width: 0;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}

.empty-run-field select {
  font: inherit;
  font-weight: 500;
  font-size: var(--fs-control);
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
  padding: 0.55rem 0.65rem;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.empty-run-field select:focus {
  border-color: var(--accent);
}

.empty-run-controls .btn {
  flex: 0 0 auto;
  min-width: 7rem;
  align-self: stretch;
}

.empty-run-more {
  margin: 0;
  text-align: center;
}

.detail-head {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  align-items: flex-start;
}

.mobile-back {
  display: none;
}

.detail-actions {
  display: flex;
  gap: 0.5rem;
  flex-shrink: 0;
}

.detail-actions-bottom {
  margin-top: 0.25rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--line-soft);
  flex-wrap: wrap;
  justify-content: flex-end;
}

.detail-head h2 {
  font-size: clamp(1.2rem, 2vw, 1.45rem);
  max-width: 48rem;
  font-weight: 700;
  letter-spacing: -0.025em;
  margin-top: 0.2rem;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ClaudeOS#918: the conversation scrolls, the identity does not.

   Scoped to `.detail-pane`, which is the Tasks detail and nothing else. The
   Bugs, Repos, Skills and Memory panes carry their own `*-detail-pane` class
   names -- different class tokens -- so their `.detail-head` is untouched.

   The negative margin bleeds the bar over the pane's own padding: without it
   the conversation would show through the strip above the pinned header as it
   scrolls under. A flat background rather than a blur, following the one
   sticky element this stylesheet already has (`.commit-day`); the pane is
   transparent over the fixed `.atmosphere` gradient, so a pinned bar has to
   bring its own ground. */
.detail-pane .detail-head {
  position: sticky;
  top: 0;
  z-index: 4;
  margin: 0 -1.5rem 0;
  padding: 1.25rem 1.5rem 0.85rem;
  background: color-mix(in srgb, var(--bg-elevated) 92%, var(--bg));
  border-bottom: 1px solid var(--line-soft);
}

/* The <p> carried the UA default 1em top margin, dead height in a bar that is
   now permanently on screen. */
.detail-pane .detail-head #d-id {
  margin: 0;
}

/* Moved out of the dl.meta block so it stays pinned with the id and title --
   the whole point of the complaint was that STATUS scrolled off. `display:
   flex` also collapses the literal spaces updateDetailMeta puts between the
   status pill and the queued pills. */
.detail-head-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  margin: 0.4rem 0 0;
}

.prompt-card {
  margin: 0;
  padding: 0.85rem 1rem 0.95rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--bg-elevated) 88%, var(--bright)) 0%, color-mix(in srgb, var(--bg-elevated) 65%, transparent) 100%);
  box-shadow: var(--shadow-soft);
}

.prompt-card-label {
  margin: 0 0 0.45rem;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}

.prompt-body {
  margin: 0;
  max-height: 14rem;
  overflow: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-family: var(--font-body);
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--ink);
}

.attachments-card {
  margin: 0.75rem 0 0;
  padding: 0.85rem 1rem 0.95rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
}

.attachments-card-label {
  margin: 0 0 0.55rem;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}

.attachments-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.attach-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.attach-thumb-link {
  flex: 0 0 auto;
}

.attach-thumb {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--line-soft);
  background: var(--bg);
}

.attach-meta {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.attach-meta a {
  color: var(--accent-text);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.attach-meta a:hover {
  text-decoration: underline;
}

.file-field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.file-field-label {
  font-size: var(--fs-small);
  font-weight: 600;
}

.file-drop {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.file-drop-compact {
  margin-top: 0.15rem;
  gap: 0.4rem;
}

.file-drop-zone {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.85rem;
  margin: 0;
  padding: 0.9rem 1rem;
  border: 1.5px dashed color-mix(in srgb, var(--accent) 28%, var(--line));
  border-radius: var(--radius);
  background:
    linear-gradient(165deg, color-mix(in srgb, var(--accent-bright) 8%, var(--bright)) 0%, var(--bright) 55%);
  color: var(--ink);
  font-size: 0.92rem;
  font-weight: 400;
  cursor: pointer;
  transition:
    border-color 0.18s var(--ease),
    background 0.18s var(--ease),
    box-shadow 0.18s var(--ease),
    transform 0.18s var(--ease);
}

.file-drop-compact .file-drop-zone {
  padding: 0.7rem 0.85rem;
  gap: 0.7rem;
}

.file-drop-zone:hover {
  border-color: color-mix(in srgb, var(--accent-bright) 55%, var(--line));
  background:
    linear-gradient(165deg, color-mix(in srgb, var(--accent-bright) 14%, var(--bright)) 0%, var(--bright) 60%);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}

.file-drop-zone:focus-within,
.file-drop.is-dragover .file-drop-zone {
  border-style: solid;
  border-color: color-mix(in srgb, var(--accent-bright) 70%, var(--line));
  box-shadow: 0 0 0 3px var(--focus-ring);
  transform: translateY(-1px);
}

.file-drop.is-disabled .file-drop-zone {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
  background: color-mix(in srgb, var(--bg) 55%, var(--bright));
  border-color: var(--line-soft);
  box-shadow: none;
  transform: none;
}

.file-drop-glyph {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 9px;
  color: var(--accent-text);
  background: color-mix(in srgb, var(--accent-bright) 16%, transparent);
}

.file-drop-compact .file-drop-glyph {
  width: 2.15rem;
  height: 2.15rem;
  border-radius: 8px;
}

.file-drop-copy {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
  font-weight: 400;
}

.file-drop-copy strong {
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.file-drop-sub {
  font-size: var(--fs-small);
  color: var(--muted);
  line-height: 1.35;
}

.file-chip-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

/* `display: flex` above outranks the UA's `[hidden] { display: none }`, so an
   empty list was never actually hidden -- it drew its own margin under every
   composer that has one. Harmless-looking until the list moved inside the
   chat field, where its padding and divider became a dead strip and a rule
   across an empty input. */
.file-chip-list[hidden] {
  display: none;
}

/* Pending attachments shown against the composer (ClaudeOS#871). A paste has
   to be visible where the person is looking -- the same place the file appears
   once the message is sent -- or they cannot tell it worked. */
.file-chip-list-attached {
  margin: 0.5rem 0 0.15rem;
}

/* Inside the composer's own frame (ClaudeOS#864). The chips drop their border
   and tint here: a box inside a box reads as a second field, which is the
   confusion this is meant to end. */
.file-chip-list-inside {
  gap: 0.3rem;
  padding: 0.5rem 0.5rem 0.45rem;
  border-bottom: 1px solid var(--line-soft);
}

.file-chip-list-inside .file-chip {
  padding: 0.2rem 0.2rem 0.2rem 0.1rem;
  border: none;
  background: transparent;
}

.file-chip-list-inside .file-chip-thumb,
.file-chip-list-inside .file-chip-icon {
  width: 34px;
  height: 34px;
}

.file-chip {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.45rem 0.55rem 0.45rem 0.5rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg-elevated) 85%, var(--bright));
  animation: rise 0.28s var(--ease) both;
}

.file-chip-thumb {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--line-soft);
  background: var(--bg);
  flex: 0 0 auto;
}

.file-chip-icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--accent-text);
  background: color-mix(in srgb, var(--accent-bright) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--line-soft));
}

.file-chip-meta {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.file-chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-small);
  font-weight: 600;
}

.file-chip-size {
  font-size: 0.74rem;
  color: var(--muted);
}

.file-chip-remove {
  flex: 0 0 auto;
  width: 1.85rem;
  height: 1.85rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.file-chip-remove:hover {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  border-color: color-mix(in srgb, var(--danger) 22%, transparent);
}

.file-drop.is-disabled .file-chip-remove {
  pointer-events: none;
  opacity: 0.5;
}

/* Lead card: the one paragraph that says what happened, above the fold. */
.tldr-card {
  margin: 0;
  padding: 0.85rem 1rem 0.95rem;
  border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--line-soft));
  border-left: 3px solid var(--accent-bright);
  border-radius: var(--radius);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--accent-bright) 10%, var(--bg-elevated)) 0%, var(--bg-elevated) 100%);
  box-shadow: var(--shadow-soft);
}

.tldr-card-label {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
  margin: 0 0 0.4rem;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--accent-text);
}

.tldr-turn {
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--muted);
}

/* Inherits .result-content typography; drops its panel chrome. Scoped so it
   outranks .result-content regardless of which block comes first. */
.tldr-card .tldr-body {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  font-size: 0.98rem;
}

.meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 0.85rem 1.1rem;
  margin: 0;
  padding: 0.95rem 1rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
}

.meta dt {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}

.meta dd {
  margin: 0.25rem 0 0;
  font-weight: 500;
  overflow-wrap: anywhere;
}

.tabs {
  display: flex;
  gap: 0.15rem;
  border-bottom: 1px solid var(--line-soft);
  /* overflow-x alone would compute overflow-y to auto, and the tabs'
     margin-bottom: -1px overflows the box by exactly that 1px. */
  overflow: auto hidden;
}

.tab {
  border: none;
  background: transparent;
  padding: 0.7rem 0.95rem;
  font: inherit;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.tab:hover {
  color: var(--ink);
}

.tab.active {
  color: var(--ink);
  border-bottom-color: var(--accent);
}

.tab-panels {
  min-height: 16rem;
}

.chat-shell {
  position: relative;
}

.chat-view {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 0.35rem 0.15rem 0.75rem;
  scroll-behavior: smooth;
}

/* NOT a scroll container, deliberately. It used to carry
   `max-height: min(58vh, 36rem); overflow-y: auto`, which put a capped
   scroll box INSIDE .detail-pane -- itself already `overflow: auto`. On the
   Tasks view that made three independently scrolling regions on one screen
   (.run-list, .detail-pane, .chat-view), two of them nested, so a wheel
   gesture did something different depending on which pixel the pointer was
   over and neither scrollbar represented the page. The conversation now
   flows into the detail pane and the right-hand side scrolls exactly once. */

.chat-scroll-btn {
  position: absolute;
  right: 0.55rem;
  z-index: 2;
  box-shadow: 0 4px 14px rgba(15, 23, 32, 0.12);
  background: color-mix(in srgb, var(--bg-elevated) 92%, var(--bright));
  border-color: var(--line);
}

.chat-scroll-btn:hover {
  background: var(--surface);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--line));
  color: var(--accent-text);
}

/* Side by side in a lane the shell reserves under the conversation while
   either button shows (.has-scroll-btns, set in updateChatScrollButtons).
   Stacked at the shell's bottom-right they sat on the last message, and a
   user bubble is right-aligned -- the arrow landed on its text. */
.chat-scroll-top {
  bottom: 0.25rem;
  right: 3.1rem;
}

.chat-scroll-bottom {
  bottom: 0.25rem;
}

.chat-shell.has-scroll-btns {
  padding-bottom: 2.9rem;
}

.chat-empty {
  margin: 0.75rem 0 0;
}

/* "Agent is working" — a radar orb, a shimmer across the label, and three
 * trailing dots. The ellipsis lives in the animation, not in the string, so
 * the indicator reads as alive even when a run is quiet for a long stretch.
 * Everything collapses to a static dot under prefers-reduced-motion. */
.chat-live {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  margin: 0.35rem 0 0;
  font-weight: 600;
}

.chat-live-orb {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1rem;
  height: 1rem;
  margin-right: 0.4rem;
}

.chat-live-core {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--accent-bright);
  animation: chat-live-breathe 1.8s var(--ease) infinite;
}

.chat-live-ping {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1.5px solid var(--accent-bright);
  opacity: 0;
  animation: chat-live-ping 1.8s cubic-bezier(0.2, 0.6, 0.35, 1) infinite;
}

.chat-live-ping:nth-child(2) {
  animation-delay: 0.9s;
}

@keyframes chat-live-ping {
  0% { transform: scale(0.45); opacity: 0.55; }
  70% { opacity: 0; }
  100% { transform: scale(1.35); opacity: 0; }
}

@keyframes chat-live-breathe {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(0.78); opacity: 0.72; }
}

/* A light sweep travels along the words. background-clip keeps it tied to the
 * glyphs, so it reads as the text itself catching the light. */
.chat-live-label {
  background-image: linear-gradient(
    100deg,
    var(--muted) 0%,
    var(--muted) 38%,
    var(--accent-bright) 50%,
    var(--muted) 62%,
    var(--muted) 100%
  );
  background-size: 260% 100%;
  background-position: 130% 0;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: chat-live-shimmer 2.6s linear infinite;
}

@keyframes chat-live-shimmer {
  to { background-position: -130% 0; }
}

.chat-live-dots {
  display: inline-flex;
  align-items: flex-end;
  gap: 0.12rem;
  margin-left: 0.14rem;
  padding-bottom: 0.12em;
}

.chat-live-dots i {
  width: 0.2rem;
  height: 0.2rem;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.25;
  animation: chat-live-dot 1.35s var(--ease) infinite;
}

.chat-live-dots i:nth-child(2) { animation-delay: 0.18s; }
.chat-live-dots i:nth-child(3) { animation-delay: 0.36s; }

@keyframes chat-live-dot {
  0%, 65%, 100% { opacity: 0.22; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-0.14rem); }
}

@media (prefers-reduced-motion: reduce) {
  .chat-live-core,
  .chat-live-ping,
  .chat-live-label,
  .chat-live-dots i {
    animation: none;
  }

  .chat-live-ping { display: none; }

  .chat-live-label {
    background: none;
    -webkit-background-clip: initial;
    background-clip: initial;
    color: var(--muted);
  }

  .chat-live-dots i { opacity: 0.55; }
}

.chat-bubble {
  max-width: min(92%, 42rem);
  padding: 0.7rem 0.9rem;
  border-radius: 12px;
  border: 1px solid var(--line-soft);
  background: var(--surface);
  box-shadow: 0 1px 0 rgba(15, 23, 32, 0.03);
  animation: chat-in 0.28s var(--ease) both;
}

@keyframes chat-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

.chat-bubble.user {
  align-self: flex-end;
  background: color-mix(in srgb, var(--accent-bright) 14%, var(--bright));
  border-color: color-mix(in srgb, var(--accent) 28%, var(--line));
}

.chat-bubble.assistant {
  align-self: flex-start;
  background: var(--surface);
}

.chat-bubble.error {
  align-self: stretch;
  border-color: color-mix(in srgb, var(--danger) 35%, var(--line));
  background: color-mix(in srgb, var(--danger) 8%, var(--bright));
}

/* A backend notice (Codex warning, stream retry): worth seeing, not alarming. */
.chat-bubble.notice {
  align-self: stretch;
  border-style: dashed;
  background: transparent;
  color: var(--muted);
}

/* Who said it, and when. The time is text in the bubble, not a tooltip: a
   customer asked for it by name and reads Atlas on a phone, where there is
   no hover (ClaudeOS#922). It wraps under the role on a narrow bubble rather
   than squeezing the label. */
.chat-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.15rem 0.7rem;
  margin-bottom: 0.35rem;
}

.chat-role {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.chat-time {
  flex: none;
  font-size: 0.68rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.chat-bubble.user .chat-role { color: var(--accent-text); }
.chat-bubble.assistant .chat-role { color: var(--signal-text); }
.chat-bubble.error .chat-role { color: var(--danger); }

.chat-body {
  font-size: 0.95rem;
  line-height: 1.5;
  overflow-wrap: anywhere;
}

.chat-body > :first-child { margin-top: 0; }
.chat-body > :last-child { margin-bottom: 0; }
.chat-body p { margin: 0.45rem 0; }
.chat-body pre {
  margin: 0.5rem 0;
  padding: 0.55rem 0.7rem;
  overflow-x: auto;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--log-bg) 92%, var(--embed-raised));
  color: var(--log-ink);
  font-family: var(--font-mono);
  font-size: 0.8rem;
}
.chat-body code {
  font-family: var(--font-mono);
  font-size: 0.86em;
}

/* A preview card per web page a message links (AtlasOS#759). The stack
   holds a hidden slot per link until its card arrives, so it only takes
   space once there is a card in it. */
.link-previews {
  display: grid;
  gap: 0.45rem;
  margin-top: 0.6rem;
}
.link-previews:empty,
.link-previews:not(:has(.link-card)) {
  display: none;
}
.link-card {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  max-width: 34rem;
  padding: 0.6rem 0.65rem 0.6rem 0.75rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.18s var(--ease), background-color 0.18s var(--ease);
}
.link-card:hover {
  border-color: color-mix(in srgb, var(--signal) 45%, var(--line));
  background: color-mix(in srgb, var(--signal) 5%, var(--bg-elevated));
}
.link-card:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
}
.link-card-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 0.12rem;
  min-width: 0;
}
.link-card-site {
  overflow: hidden;
  font-size: 0.74rem;
  color: var(--muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}
.link-card-title,
.link-card-desc {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.link-card-title {
  font-weight: 650;
  line-height: 1.3;
}
.link-card-desc {
  font-size: var(--fs-small);
  line-height: 1.35;
  color: var(--muted);
}
.link-card-img {
  flex: 0 0 auto;
  width: 84px;
  height: 84px;
  border-radius: 6px;
  object-fit: cover;
  background: var(--line-soft);
}
@media (max-width: 520px) {
  .link-card-img {
    width: 60px;
    height: 60px;
  }
}

/* A 3D view inline under the message that links it: the site view
   (AtlasOS#699) or a model file (AtlasOS#707). `.embed-full` grows the same
   frame over the whole app, with a bar to get back -- see openEmbed in app.js.
   The frame is never moved or re-created, so nothing reloads. */
.embed-card {
  margin: 0.6rem 0 0.2rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
  background: var(--embed-bg);
}
.embed-frame {
  display: block;
  width: 100%;
  height: clamp(240px, 42vh, 420px);
  border: 0;
}
.embed-bar {
  display: none;
}
.embed-card.embed-full {
  position: fixed;
  inset: 0;
  /* Over the app, under the restart overlay (80). */
  z-index: 70;
  margin: 0;
  border: 0;
  border-radius: 0;
  display: flex;
  flex-direction: column;
}
.embed-card.embed-full .embed-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-shrink: 0;
  padding: calc(env(safe-area-inset-top, 0px) + 0.55rem)
    calc(env(safe-area-inset-right, 0px) + 0.9rem) 0.55rem
    calc(env(safe-area-inset-left, 0px) + 0.9rem);
  background: var(--embed-bg);
  color: var(--embed-ink);
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}
.embed-card.embed-full .embed-frame {
  flex: 1;
  height: auto;
  min-height: 0;
}
.embed-back {
  flex-shrink: 0;
  min-height: 2.25rem;
  padding: 0.35rem 0.9rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: var(--embed-raised);
  color: inherit;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.embed-back:hover,
.embed-back:focus-visible {
  background: var(--embed-action);
  border-color: var(--embed-action);
}
.embed-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--embed-muted);
}
html.embed-full-open,
html.embed-full-open body {
  overflow: hidden;
}
/* The panels around the chat keep their finished entrance animation's end
   transform (an identity matrix, not `none`), and any transform makes an
   element the containing block for `position: fixed` -- measured: the
   full-screen view filled the chat panel, not the window. openEmbed tags
   that chain while a view is open. Dropping the fill mode does not replay
   the animation; changing its name would. */
.embed-full-host {
  animation-fill-mode: none !important;
}

/* While a card is full screen: Atlas's latest answer and an input, so the
   conversation goes on over the view (AtlasOS#699). Sends through the run's
   follow-up form -- see embedChat in app.js. */
.embed-chat { display: none; }
.embed-card.embed-full .embed-chat {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  flex-shrink: 0;
  padding: 0.6rem calc(env(safe-area-inset-right, 0px) + 0.9rem)
    calc(env(safe-area-inset-bottom, 0px) + 0.8rem) calc(env(safe-area-inset-left, 0px) + 0.9rem);
  background: var(--embed-bg);
  color: var(--embed-ink);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
/* The map on its own (AtlasOS#788) only ever shows full screen; closing it
   removes the card (releaseEmbed), and it must never flash in the page. */
.embed-card.map-home:not(.embed-full) { display: none; }

/* An address in an answer that has no view of it: one tap puts the view
   under the message (AtlasOS#786). Buttons, not frames -- a false match
   costs a chip, not a WebGL context. */
.map-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.55rem 0 0.1rem;
}
.map-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  max-width: 100%;
  min-height: 2rem;
  padding: 0.3rem 0.8rem;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: inherit;
  font: inherit;
  font-size: var(--fs-small);
  cursor: pointer;
  text-align: left;
}
.map-chip .map-chip-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.map-chip:hover,
.map-chip:focus-visible {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
}

.embed-reply { max-height: 26vh; overflow: auto; font-size: 0.92rem; line-height: 1.45; }
.embed-reply:empty { display: none; }
/* Over a map: what goes along with the question (#787 follow-up). */
.embed-where { font-size: 0.78rem; opacity: 0.75; margin: 0 0 0.35rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.embed-where[hidden] { display: none; }

/* A ```mermaid diagram (AtlasOS#790): the sandboxed /diagram page in the same
   card as a 3D view, so full screen and the way back work the same way. Its
   frame grows to the drawing's height, which the frame reports itself
   (capped in app.js). */
.embed-card.diagram-card { background: var(--bg-elevated); }
.embed-card.diagram-card:not(.embed-full) .embed-frame { height: var(--diagram-h, 160px); }
.md-block-error { font-size: var(--fs-small); margin: 0.5rem 0 0.2rem; }

/* Charts in the chat (AtlasOS#790), drawn by web/charts.js from a ```chart
   block. The eight series colours are one fixed order, run through the house
   data-viz validator against this surface in both themes on 2026-09-13:
   worst adjacent colour-vision ΔE 9.1 light / 8.4 dark (target 8), worst
   normal-vision ΔE 19.6 / 19.3 (floor 15). Three light slots sit under 3:1
   on the surface -- which is why every chart carries a table view. There is
   no ninth colour on purpose: charts.js refuses a ninth series. */
.md-chart {
  --chart-surface: #f7f9fc;
  --chart-ink: #0b0d12;
  --chart-muted: #5b6b7c;
  --chart-grid: #e3e8ef;
  --chart-base: #b4c0cd;
  --chart-wash: rgba(11, 13, 18, 0.05);
  --s1: #2a78d6;
  --s2: #eb6834;
  --s3: #1baf7a;
  --s4: #eda100;
  --s5: #e87ba4;
  --s6: #008300;
  --s7: #4a3aa7;
  --s8: #e34948;
  --s-other: #9aa3ad;
  margin: 0.6rem 0;
  padding: 0.75rem 0.85rem 0.5rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: var(--chart-surface);
  color: var(--chart-ink);
}
html[data-theme="dark"] .md-chart {
  --chart-surface: #151a22;
  --chart-ink: #e8eef4;
  --chart-muted: #9aa8b8;
  --chart-grid: #232c38;
  --chart-base: #3d4959;
  --chart-wash: rgba(232, 238, 244, 0.06);
  --s1: #3987e5;
  --s2: #d95926;
  --s3: #199e70;
  --s4: #c98500;
  --s5: #d55181;
  --s6: #008300;
  --s7: #9085e9;
  --s8: #e66767;
  --s-other: #6b7683;
}
.md-chart .md-s1 { --c: var(--s1); }
.md-chart .md-s2 { --c: var(--s2); }
.md-chart .md-s3 { --c: var(--s3); }
.md-chart .md-s4 { --c: var(--s4); }
.md-chart .md-s5 { --c: var(--s5); }
.md-chart .md-s6 { --c: var(--s6); }
.md-chart .md-s7 { --c: var(--s7); }
.md-chart .md-s8 { --c: var(--s8); }
.md-chart .md-other { --c: var(--s-other); }
.md-chart-head { display: flex; flex-direction: column; gap: 0.1rem; margin-bottom: 0.45rem; }
.md-chart-title { font-weight: 600; font-size: 0.95rem; }
.md-chart-sub { color: var(--chart-muted); font-size: 0.82rem; }
.md-chart-legend {
  list-style: none;
  margin: 0 0 0.35rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.9rem;
  font-size: 0.8rem;
  color: var(--chart-muted);
}
.md-chart-legend li { display: inline-flex; align-items: center; gap: 0.35rem; margin: 0; }
.md-chart-swatch { width: 10px; height: 10px; border-radius: 2px; background: var(--c); flex-shrink: 0; }
.md-chart-swatch.is-line { width: 14px; height: 2px; border-radius: 1px; }
.md-chart-plot { position: relative; border-radius: 4px; }
.md-chart-plot svg { display: block; overflow: visible; font-family: var(--font-body); font-size: 12px; }
.md-chart .md-grid { stroke: var(--chart-grid); stroke-width: 1; shape-rendering: crispEdges; }
.md-chart .md-base { stroke: var(--chart-base); stroke-width: 1; shape-rendering: crispEdges; }
.md-chart .md-tick,
.md-chart .md-unit,
.md-chart .md-xlabel { fill: var(--chart-muted); font-size: 11px; }
.md-chart .md-tick { font-variant-numeric: tabular-nums; }
.md-chart .md-direct { fill: var(--chart-ink); font-size: 11px; }
.md-chart .md-bar { fill: var(--c); }
.md-chart .md-line {
  fill: none;
  stroke: var(--c);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.md-chart .md-area { fill: var(--c); fill-opacity: 0.16; }
.md-chart .md-dot { fill: var(--c); stroke: var(--chart-surface); stroke-width: 2; }
.md-chart .md-wash { fill: var(--chart-wash); }
.md-chart .md-cross { stroke: var(--chart-base); stroke-width: 1; }
.md-chart .md-slice {
  fill: var(--c);
  stroke: var(--chart-surface);
  stroke-width: 2;
  stroke-linejoin: round;
  transition: opacity 0.12s;
}
.md-chart svg.is-dim .md-slice:not(.is-active) { opacity: 0.45; }
.md-chart .md-center-value { fill: var(--chart-ink); font-size: 17px; font-weight: 600; }
.md-chart .md-center-label { fill: var(--chart-muted); font-size: 11px; }
.md-chart-pie { display: flex; align-items: center; gap: 1rem 1.25rem; flex-wrap: wrap; }
.md-chart-legend-pie {
  flex: 1 1 11rem;
  flex-direction: column;
  flex-wrap: nowrap;
  gap: 0.3rem;
  margin: 0;
  color: var(--chart-ink);
}
.md-chart-legend-pie li {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr) auto;
  gap: 0.5rem;
  align-items: center;
  padding: 0.1rem 0.3rem;
  border-radius: 4px;
  cursor: default;
}
.md-chart-legend-pie li.is-active { background: var(--chart-wash); }
.md-chart-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.md-chart-val { color: var(--chart-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.md-chart-tip {
  position: absolute;
  z-index: 2;
  pointer-events: none;
  min-width: 6.5rem;
  max-width: 16rem;
  padding: 0.4rem 0.6rem;
  border-radius: 8px;
  background: var(--bg-elevated);
  color: var(--ink);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-soft);
  font-size: 0.8rem;
  line-height: 1.35;
}
.md-chart-tip-head { color: var(--chart-muted); margin-bottom: 0.15rem; overflow-wrap: anywhere; }
.md-chart-tip-row { display: flex; align-items: center; gap: 0.4rem; white-space: nowrap; }
.md-chart-tip-row strong { font-variant-numeric: tabular-nums; }
.md-chart-tip-name { color: var(--chart-muted); overflow: hidden; text-overflow: ellipsis; }
.md-chart-key { width: 12px; height: 2px; border-radius: 1px; background: var(--c); flex-shrink: 0; }
.md-chart-key.is-blank { background: transparent; }
.md-chart-tools { display: flex; justify-content: flex-end; margin-top: 0.3rem; }
.md-chart-toggle {
  font: inherit;
  font-size: 0.78rem;
  color: var(--chart-muted);
  background: none;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  padding: 0.15rem 0.65rem;
  cursor: pointer;
}
.md-chart-toggle:hover,
.md-chart-toggle:focus-visible { color: var(--chart-ink); border-color: var(--line); }
.md-chart-table th.num,
.md-chart-table td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.embed-reply[data-waiting] { opacity: 0.7; font-style: italic; }
.embed-reply p { margin: 0.3rem 0; }
.embed-reply a { color: var(--embed-link); }
.embed-form { display: flex; gap: 0.5rem; }
.embed-form input {
  flex: 1;
  min-width: 0;
  padding: 0.6rem 0.75rem;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: var(--embed-raised-2);
  color: var(--embed-ink);
  font: inherit;
}
.embed-form button {
  padding: 0.55rem 1rem;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: var(--embed-action);
  color: var(--on-dark);
  font: inherit;
  cursor: pointer;
}

.chat-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.5rem;
}

.chat-attach {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 100%;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
  color: var(--accent-text);
  font-size: var(--fs-small);
  text-decoration: none;
}

.chat-attach:hover {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
}

.chat-attach-thumb {
  width: 44px;
  height: 44px;
  object-fit: cover;
  border-radius: 5px;
  border: 1px solid var(--line-soft);
  background: var(--bg);
}

.chat-attach-icon {
  flex: 0 0 auto;
  font-size: 0.9rem;
}

.chat-attach-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 14rem;
}

.chat-activity {
  align-self: stretch;
  display: flex;
  gap: 0.55rem;
  align-items: baseline;
  padding: 0.15rem 0.35rem;
  color: var(--muted);
  font-size: 0.82rem;
  font-family: var(--font-mono);
  animation: chat-in 0.2s var(--ease) both;
}

.chat-activity-kind {
  flex: 0 0 auto;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.68rem;
  color: var(--signal-text);
}

.chat-activity-text {
  overflow-wrap: anywhere;
  opacity: 0.92;
}

.chat-activity.done .chat-activity-kind {
  color: var(--ok);
}

/* The question card (AtlasOS#434). Sits directly above the composer, because
   the answer is typed or clicked there -- a question rendered at the top of a
   long chat is a question the person scrolls away from. */
.ask-card {
  margin-top: 1rem;
  padding: 0.85rem 1rem 0.95rem;
  border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--ask) 45%, transparent);
  background: color-mix(in srgb, var(--ask) 10%, transparent);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.ask-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  align-self: flex-start;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ask);
}

.ask-badge::before {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: currentColor;
  animation: status-knock 1.9s ease-in-out infinite;
}

.ask-question {
  margin: 0;
  font-size: 1.02rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
}

.ask-detail {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--muted);
  white-space: pre-wrap;
}

.ask-options {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
}

.ask-options:empty {
  display: none;
}

.ask-option {
  font: inherit;
  font-size: 0.92rem;
  text-align: left;
  padding: 0.45rem 0.8rem;
  border-radius: var(--radius-sm);
  border: 1px solid color-mix(in srgb, var(--ask) 55%, var(--line));
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.1s ease;
}

.ask-option:hover:not(:disabled) {
  border-color: var(--ask);
  background: color-mix(in srgb, var(--ask) 16%, var(--surface));
}

.ask-option:active:not(:disabled) {
  transform: translateY(1px);
}

.ask-option:disabled {
  opacity: 0.55;
  cursor: progress;
}

.ask-hint {
  margin: 0;
}

.ask-card + .send-form {
  border-top: none;
  padding-top: 0.5rem;
  margin-top: 0.25rem;
}

.send-form {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

.detail-actions-bottom:not(.hidden) + .send-form {
  margin-top: 0.55rem;
}

.send-label {
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}

.send-row {
  /* One number for the height of the typing line, because two things have to
     agree on it: the textarea draws it and the Send button next to the field
     has to match it. The button can no longer just stretch -- the field is as
     tall as its attachments now, and a Send button the height of three
     thumbnails is not a button any more. */
  --send-line-h: 3.9rem;
  display: flex;
  gap: 0.55rem;
  align-items: flex-end;
}

/* The frame belongs to the field, not to the textarea, so a pending
   attachment can sit inside the same border the person types in
   (ClaudeOS#864). The textarea keeps the metrics it had; it just stopped
   drawing its own box. */
.send-field {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.send-field:focus-within {
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.send-field:has(textarea:disabled) {
  background: color-mix(in srgb, var(--bg) 70%, var(--bright));
}

.send-row textarea {
  width: 100%;
  font: inherit;
  font-family: var(--font-body);
  font-size: var(--fs-control);
  padding: 0.62rem 0.75rem;
  border: none;
  border-radius: inherit;
  background: transparent;
  color: var(--ink);
  resize: vertical;
  height: var(--send-line-h);
  min-height: 2.8rem;
  line-height: 1.4;
}

/* The composer's frame (.send-field) carries the focus outline, so the
   textarea inside it does not draw a second one. Paired in
   tests/test_web_css_lint.py. */
.send-row textarea:focus {
  outline: none;
}

.send-row textarea:disabled {
  color: var(--muted);
  cursor: not-allowed;
}

.send-row .btn {
  flex-shrink: 0;
  min-width: 5.5rem;
  align-self: flex-end;
  height: var(--send-line-h);
}

.send-hint {
  margin: 0;
}

.panel {
  display: none;
  animation: rise 0.35s var(--ease) both;
}

.panel.active {
  display: block;
}

.log {
  margin: 0;
  padding: 1rem 1.1rem;
  background: var(--log-bg);
  color: var(--log-ink);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  line-height: 1.5;
  overflow: auto;
  max-height: min(60vh, 36rem);
  white-space: pre-wrap;
  word-break: break-word;
  border-radius: var(--radius);
  border: 1px solid var(--code-line);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}

/* An answer is never its own scroll box (ClaudeOS#621). This used to carry
   `overflow: auto; max-height: min(60vh, 36rem)`, and the three surfaces that
   show what Atlas said -- a chat bubble (`.chat-body result-content`), the
   Result tab, the TL;DR card -- were the only ones that ever inherited it:
   every other user pairs it with `.issue-body`, which already sets
   `max-height: none` and lets the section above do the scrolling. So the cap
   put a 480px scroll box inside `.detail-pane`, which already scrolls -- a
   long answer was cut off mid-table-row with a second scrollbar of its own,
   and the pane's scrollbar said nothing about how much answer was left.
   Measured on a 60-row table at 1200x800: 478px visible of 3984px.

   Same reasoning as `.chat-view` above -- the right-hand side scrolls exactly
   once. Wide tables and code blocks still scroll sideways inside their own
   wrappers (`.result-content .table-scroll`, `.chat-body pre`): that is a
   bounded element inside the answer, not the answer itself. */
.result-content {
  padding: 1rem 1.15rem;
  background: var(--bg-elevated);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  line-height: 1.6;
  overflow-wrap: anywhere;
}

.result-content > :first-child { margin-top: 0; }
.result-content > :last-child { margin-bottom: 0; }

/* A chat message body borrows .result-content for its typography (headings,
   lists, code) -- not for the card. The bubble already is the card; the
   Result tab's padding, fill and border inside it drew a second box and ate
   a quarter of a narrow bubble's width. Two classes, so this wins by
   specificity wherever .result-content is declared. */
.chat-body.result-content {
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  line-height: 1.5;
}

.result-content h1,
.result-content h2,
.result-content h3,
.result-content h4,
.result-content h5,
.result-content h6 {
  margin: 1.4em 0 0.5em;
  font-family: var(--font-display);
  line-height: 1.2;
}

.result-content h1 { font-size: 1.45rem; }
.result-content h2 { font-size: 1.15rem; }
.result-content p { margin: 0.65rem 0; }
.result-content ul,
.result-content ol { padding-left: 1.5rem; }
.result-content li + li { margin-top: 0.25rem; }
.result-content a { color: var(--accent-text); }

.result-content code {
  padding: 0.1em 0.3em;
  background: color-mix(in srgb, var(--line) 45%, transparent);
  font-family: var(--font-mono);
  font-size: 0.85em;
}

.result-content pre {
  padding: 0.9rem 1rem;
  overflow: auto;
  background: var(--code-bg);
  color: var(--code-ink);
  border-radius: var(--radius);
}

.result-content pre code {
  padding: 0;
  background: transparent;
}

.result-content blockquote {
  margin: 0.9rem 0;
  padding: 0.1rem 0 0.1rem 0.9rem;
  border-left: 3px solid var(--accent-hot);
  color: var(--muted);
}

/* Pictures an agent shows (AtlasOS#726): `![alt](src)` in any rendered
   answer. One picture is capped in height so a portrait photo does not push
   the rest of the answer off screen; a paragraph of only pictures is a grid
   of equal tiles, each opening its source. */
.result-content .md-img-link {
  display: inline-block;
  max-width: 100%;
  vertical-align: top;
  color: inherit;
  text-decoration: none;
}
.result-content .md-img {
  display: block;
  max-width: 100%;
  max-height: 420px;
  height: auto;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-soft);
  background: var(--bg);
}
.result-content .md-figure {
  display: inline-flex;
  flex-direction: column;
  gap: 0.25rem;
  max-width: 100%;
}
.result-content .md-caption {
  font-size: 0.72rem;
  line-height: 1.3;
  color: var(--muted);
}
.result-content .md-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 0.6rem;
  align-items: start;
}
.result-content .md-gallery .md-img-link,
.result-content .md-gallery .md-figure {
  display: flex;
  flex-direction: column;
  width: 100%;
}
.result-content .md-gallery .md-img {
  width: 100%;
  max-height: none;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
/* A picture that would not load: its alt text, still linked to the source,
   instead of the browser's broken-image icon. */
.result-content .md-img-broken {
  display: inline-block;
  padding: 0.35rem 0.55rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-size: var(--fs-small);
}

.result-content .table-scroll { overflow-x: auto; }
.result-content table { border-collapse: collapse; width: 100%; }
.result-content th,
.result-content td {
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line);
  text-align: left;
}
.result-content th { background: color-mix(in srgb, var(--line) 30%, transparent); }

/* Buttons (Atlas Web overhaul 2026-09-28): one primary per screen, filled in
   the accent; secondary buttons on the typing surface with a --control-line
   edge a person can find (3:1); ghost for toolbars; danger outlined, and
   .danger-fill solid for the confirm that really deletes. */
.btn {
  font: inherit;
  font-weight: 600;
  border: 1px solid var(--control-line);
  background: var(--surface);
  color: var(--ink);
  padding: 0.58rem 1rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  text-decoration: none;
  transition: transform var(--dur-fast) var(--ease), filter var(--dur-fast) ease, border-color var(--dur-fast) ease, background var(--dur-fast) ease;
}

.btn:hover {
  border-color: var(--ink);
  background: var(--bright);
}

.btn:active {
  transform: translateY(1px);
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

/* A main button that cannot be pressed yet (#1040). Fading the whole button
   faded the fill under its label too: in the dark theme the dark label sat
   on a 45 % magenta over the dark page and nearly vanished. It loses the
   fill instead: the page colour, a dashed edge, no shadow or glow, and the
   label in --muted, which holds 4.5:1 on --bg in both themes
   (tests/test_web_disabled_buttons.py). The :root prefix outranks a look's
   own `.btn.primary` fill scoped under an attribute on <html>, and the
   :hover lines outrank its hover, so the accent never comes back under the
   muted label. */
:root .btn.primary:disabled,
:root .btn.primary:disabled:hover,
:root .btn.danger:disabled,
:root .btn.danger:disabled:hover,
:root .btn.danger-fill:disabled,
:root .btn.danger-fill:disabled:hover {
  opacity: 1;
  background: var(--bg);
  border: 1px dashed var(--control-line);
  color: var(--muted);
  box-shadow: none;
  filter: none;
  cursor: not-allowed;
  transform: none;
}

/* Solid accent, not the old magenta ramp: its bright end put the label at
   3.1:1. Light: #fff5f8 on #B32B5E (5.7:1); dark: ink on #E0457A (4.9:1). */
.btn.primary {
  background: var(--accent);
  border-color: transparent;
  color: var(--accent-ink);
  box-shadow: var(--shadow-primary);
}

.btn.primary:hover {
  background: var(--accent);
  filter: brightness(1.06);
  border-color: transparent;
}

.btn.danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 60%, var(--control-line));
  background: color-mix(in srgb, var(--danger) 6%, var(--surface));
}

.btn.danger:hover {
  background: color-mix(in srgb, var(--danger) 12%, var(--bg-elevated));
}

.btn.ghost {
  background: transparent;
}

.btn.ghost:hover {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}

.btn.ghost.active {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--line));
  color: var(--ink);
}

.btn.small {
  padding: 0.4rem 0.7rem;
  font-size: var(--fs-small);
}

.btn.icon {
  padding: 0.45rem;
  width: 2.15rem;
  height: 2.15rem;
}

.btn-plus {
  width: 0.7rem;
  height: 0.7rem;
  position: relative;
}

.btn-plus::before,
.btn-plus::after {
  content: "";
  position: absolute;
  background: currentColor;
  border-radius: 1px;
}

.btn-plus::before {
  inset: 0 45%;
}

.btn-plus::after {
  inset: 45% 0;
}

/* The phone's floating "New task" button (ClaudeOS#920). Hidden by default
   and switched on inside the 820px block, the same way .nav-toggle is.
   Written `.btn.atlas-fab`, never `.atlas-fab`: `.btn` above declares its own
   `display`, so a one-class rule here would beat it only by source order --
   the exact trap that has left .mobile-back visible on desktop since
   ClaudeOS#314. Two classes win wherever either rule ends up sitting. */
.btn.atlas-fab {
  display: none;
  position: fixed;
  right: max(1rem, env(safe-area-inset-right, 0px));
  /* Above the phone tab bar (SPEC 5). Wherever the tab bar stands down the
     button does too -- they share the guards in the 820px block. */
  bottom: calc(var(--tabbar-h) + 0.75rem + env(safe-area-inset-bottom, 0px));
  /* Under the call panel (60), the full-screen embed (70) and the restart
     overlay (80); above everything that merely scrolls. */
  z-index: 50;
  gap: 0.5rem;
  min-height: 3.25rem;
  padding: 0 1.15rem;
  border-radius: 999px;
  box-shadow: var(--shadow-fab);
}

.btn.atlas-fab .btn-plus {
  width: 0.85rem;
  height: 0.85rem;
}

.mono { font-family: var(--font-mono); font-size: 0.85em; }
.muted { color: var(--muted); }
.hidden { display: none !important; }
.empty {
  padding: 1.5rem 1.15rem;
  color: var(--muted);
  margin: 0;
}

dialog {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0;
  background: var(--bg-elevated);
  color: var(--ink);
  max-width: 32rem;
  width: calc(100% - 2rem);
  box-shadow: var(--shadow-dialog);
}

dialog[open] {
  animation: dialog-in 0.28s var(--ease) both;
}

@keyframes dialog-in {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

dialog::backdrop {
  background: var(--dialog-scrim);
  backdrop-filter: blur(3px);
  animation: fade-in 0.25s ease both;
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* A chat picture, large (AtlasOS#755): the dialog shrinks to the picture,
   which is as big as the viewport allows, with its caption and the way to
   its source underneath. */
dialog.image-lightbox {
  /* fit-content, not auto: a modal dialog is inset 0 on both sides, so
     auto fills the viewport and leaves bands beside a narrower picture. */
  width: fit-content;
  max-width: min(96vw, 1400px);
  overflow: hidden;
}
.lightbox-img {
  display: block;
  width: auto;
  height: auto;
  max-width: min(96vw, 1400px);
  max-height: 80vh;
  margin: 0 auto;
  background: var(--bg);
}
.lightbox-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem 1rem;
  padding: 0.7rem 0.9rem;
}
.lightbox-caption {
  flex: 1 1 14rem;
  min-width: 0;
  margin: 0;
  font-size: var(--fs-small);
  line-height: 1.35;
  color: var(--muted);
  overflow-wrap: anywhere;
}
.lightbox-original {
  white-space: nowrap;
  text-decoration: none;
}
.lightbox-host::before { content: " · "; }
.lightbox-host {
  color: var(--muted);
  font-weight: 400;
}
.lightbox-close {
  position: absolute;
  top: 0.55rem;
  right: 0.55rem;
  width: 2.2rem;
  height: 2.2rem;
  border: none;
  border-radius: 50%;
  background: var(--scrim-dark);
  color: var(--on-dark);
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
}
.lightbox-close:hover,
.lightbox-close:focus-visible { background: var(--scrim-darker); }

/* ‹ › over the picture, stepping through the pictures of the same answer
   (AtlasOS#761). Absolute inside the stage, so they centre on the picture
   and not on the picture plus its caption row. */
.lightbox-stage { position: relative; }
.lightbox-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 2.6rem;
  height: 2.6rem;
  border: none;
  border-radius: 50%;
  background: var(--scrim-dark);
  color: var(--on-dark);
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
}
.lightbox-prev { left: 0.6rem; }
.lightbox-next { right: 0.6rem; }
.lightbox-nav:hover:not(:disabled),
.lightbox-nav:focus-visible { background: var(--scrim-darker); }
.lightbox-nav:disabled {
  opacity: 0.25;
  cursor: default;
}
.lightbox-count {
  font-size: 0.78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

dialog form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: 1.4rem 1.45rem 1.25rem;
}

dialog h2 {
  margin: 0 0 0.15rem;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.03em;
}

dialog label {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-size: var(--fs-small);
  font-weight: 600;
}

dialog label.checkbox-row {
  flex-direction: row;
  align-items: center;
  gap: 0.65rem;
  font-weight: 500;
  cursor: pointer;
}

dialog label.checkbox-row input[type="checkbox"] {
  width: 1.05rem;
  height: 1.05rem;
  flex-shrink: 0;
  margin: 0;
  padding: 0;
  accent-color: var(--accent);
}

dialog input,
dialog select,
dialog textarea {
  font: inherit;
  font-size: var(--fs-control);
  font-weight: 400;
  padding: 0.62rem 0.72rem;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

dialog input:focus,
dialog select:focus,
dialog textarea:focus {
  border-color: var(--accent);
}

dialog textarea {
  resize: vertical;
  min-height: 6rem;
  line-height: 1.45;
}

.hint {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--muted);
}

.capacity-note {
  margin: 0.75rem 0 0;
  padding: 0.65rem 0.85rem;
  border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--queue) 35%, transparent);
  background: color-mix(in srgb, var(--queue) 12%, transparent);
  color: var(--fg);
}

.hint code {
  font-family: var(--font-mono);
  font-size: 0.86em;
  padding: 0.1rem 0.3rem;
  border-radius: 4px;
  background: color-mix(in srgb, var(--bg) 80%, var(--bright));
}

.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.55rem;
  margin-top: 0.2rem;
}

.confirm-dialog {
  max-width: 26rem;
}

/* Settings view: status tiles + access / server / telegram cards */
.settings-body {
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  padding: 0.35rem 1.15rem 1rem;
}

/* --- Settings hub ------------------------------------------------------
   Settings used to be one flat scroll: eleven cards with their forms open,
   then an Advanced disclosure holding seven more. The hub shows one tile
   per area -- icon, one line of what it is for, one line of live status --
   and a tile opens that area's card on its own, with a way back. The cards
   themselves are untouched. */
.settings-hub {
  display: flex;
  flex-direction: column;
  gap: 1.4rem;
  max-width: 70rem;
}

.settings-hub-group-title {
  margin: 0 0 0.65rem;
  font-family: var(--font-display);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

.settings-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16.5rem, 1fr));
  gap: 0.85rem;
}

/* Each group carries a hue for its icons, so the eye can tell "about me"
   from "things I connected" before reading a word. Advanced is deliberately
   grey. */
.settings-hub-group.tone-personal { --tile-hue: var(--accent); }
.settings-hub-group.tone-connections { --tile-hue: var(--signal); }
.settings-hub-group.tone-company { --tile-hue: var(--ask); }
.settings-advanced { --tile-hue: var(--queue); }

.settings-tile {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 0.85rem;
  align-items: start;
  width: 100%;
  min-height: 6.75rem;
  padding: 1rem 0.9rem 0.95rem 1rem;
  text-align: left;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-elevated) 82%, transparent);
  box-shadow: var(--shadow-soft);
  animation: rise 0.5s var(--ease) both;
  transition: transform 0.18s var(--ease), border-color 0.18s ease, box-shadow 0.18s ease;
}

.settings-tile:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--tile-hue) 45%, var(--line));
  box-shadow: var(--shadow-soft), 0 0 0 1px color-mix(in srgb, var(--tile-hue) 25%, transparent);
}

.settings-tile:hover .settings-tile-chevron {
  color: var(--tile-hue);
  transform: translateX(2px);
}

.settings-tile:active {
  transform: translateY(0);
}

.settings-tile:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.settings-tile-icon {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 0.75rem;
  color: var(--tile-hue);
  background: color-mix(in srgb, var(--tile-hue) 13%, transparent);
}

.settings-tile-icon svg {
  width: 1.3rem;
  height: 1.3rem;
}

.settings-tile-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.settings-tile-title {
  font-family: var(--font-display);
  font-size: 0.98rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.25;
}

.settings-tile-desc {
  margin-top: 0.22rem;
  font-size: var(--fs-small);
  line-height: 1.4;
  color: var(--muted);
}

/* The status line is the reason to glance at the hub at all: "Not set up",
   "2 connected", "Deutsch". A dot carries the tone so the words can stay
   short. */
.settings-tile-status {
  display: inline-flex;
  align-items: center;
  gap: 0.42rem;
  margin-top: 0.6rem;
  min-width: 0;
  max-width: 100%;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.settings-tile-status::before {
  content: "";
  flex-shrink: 0;
  width: 0.48rem;
  height: 0.48rem;
  border-radius: 50%;
  background: var(--muted);
}

.settings-tile-status[data-tone="ok"]::before { background: var(--ok); }
.settings-tile-status[data-tone="warn"]::before { background: var(--ask); }
.settings-tile-status[data-tone="signal"]::before { background: var(--signal); }

.settings-tile-chevron {
  align-self: center;
  color: var(--muted);
  transition: color 0.18s ease, transform 0.18s var(--ease);
}

/* Detail: one card at a time, and the way back above it. */
.settings-detail {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  max-width: 58rem;
}

.settings-detail-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

#settings-detail [data-settings-panel]:not(.is-current) {
  display: none;
}

.settings-detail .settings-card-head h3 {
  font-size: 1.15rem;
}

/* The heading takes focus when a card opens, so a screen reader starts
   there. Only a keyboard user needs to see that ring. */
.settings-detail .settings-card-head h3:focus:not(:focus-visible) {
  outline: none;
}

@media (prefers-reduced-motion: reduce) {
  .settings-tile,
  .settings-tile-chevron { transition: none; animation: none; }
}

/* --- Advanced settings disclosure -------------------------------------
   Everything an operator needs and a first-time user never does: network
   binding, serve tokens, filesystem paths, PIDs, the launchd plist. It used
   to sit in the same flat scroll as Language and Appearance, so the third
   thing a beginner met in Settings was "pid 13163 - Python 3.10.16".
   Collapsed by default; <details> so it needs no JavaScript and stays
   keyboard- and screen-reader-operable for free. Now it holds the hub's
   advanced tiles rather than the cards themselves. */
.settings-advanced {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
}

.settings-advanced-summary {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.9rem 1rem 0.9rem 2.25rem;
  position: relative;
  cursor: pointer;
  border-radius: var(--radius);
  list-style: none;
  user-select: none;
  transition: background 0.18s var(--ease);
}

.settings-advanced-summary::-webkit-details-marker { display: none; }

.settings-advanced-summary:hover {
  background: color-mix(in srgb, var(--ink) 4%, transparent);
}

.settings-advanced-summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Chevron, drawn in CSS so it inherits colour and needs no icon asset. */
.settings-advanced-summary::before {
  content: "";
  position: absolute;
  left: 0.95rem;
  top: 1.25rem;
  width: 0.42rem;
  height: 0.42rem;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(-45deg);
  transition: transform 0.18s var(--ease);
}

.settings-advanced[open] > .settings-advanced-summary::before {
  transform: rotate(45deg);
}

.settings-advanced-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--ink);
}

.settings-advanced-hint {
  font-size: var(--fs-small);
  color: var(--muted);
  line-height: 1.4;
}

.settings-advanced-body {
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  padding: 0.35rem 0.75rem 0.9rem;
}

@media (prefers-reduced-motion: reduce) {
  .settings-advanced-summary,
  .settings-advanced-summary::before { transition: none; }
}

/* Two classes (#928): the bare `.stat-grid` rule further down declares its
   four equal columns later in the file, and at equal specificity the later
   rule won, so this one never applied and three tiles stretched over four
   columns. */
.stat-grid.settings-overview {
  grid-template-columns: repeat(auto-fit, minmax(11rem, 16rem));
  margin-bottom: 1rem;
}

.settings-overview .stat-tile {
  animation-delay: 0.04s;
}

.settings-overview .stat-tile:nth-child(2) {
  animation-delay: 0.1s;
}

.settings-overview .stat-tile:nth-child(3) {
  animation-delay: 0.16s;
}

.settings-tile-value {
  font-size: clamp(1.05rem, 1.8vw, 1.35rem);
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.settings-card {
  box-shadow: var(--shadow-app-soft);
  background: var(--bg-elevated);
  animation: rise 0.5s var(--ease) both;
}

.settings-language-label {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0 1rem 1rem;
  font-size: 0.92rem;
  color: var(--muted);
}

.settings-language-label select {
  max-width: 18rem;
  font: inherit;
  color: var(--ink);
  background: var(--bg-elevated);
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  padding: 0.55rem 0.7rem;
}

.settings-card-head {
  align-items: flex-start;
  margin-bottom: 1rem;
}

.settings-card-head h3 {
  margin: 0 0 0.25rem;
}

.settings-card-head .hint {
  margin: 0;
}

.settings-card > .dash-section-head:not(.settings-card-head) {
  margin-bottom: 0.85rem;
}

.settings-access-grid {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 0.85rem;
}

.settings-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12.5rem, 1fr));
  gap: 0.85rem 1rem;
  margin: 0;
}

.settings-meta dt {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}

.settings-meta dd {
  margin: 0.25rem 0 0;
  overflow-wrap: anywhere;
  font-size: 0.88rem;
}

.settings-pref-form {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  margin: 0;
}

.settings-pref-form label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--muted);
}

/* Every text-entry control in a pref form, named by exclusion rather than by
   enumeration. The list used to be text/url/password, so the Jira "Atlassian
   account email" and the two `type="number"` fields (SMTP port, internal cost,
   max concurrent sessions) fell through it entirely and rendered as bare
   21px-tall native inputs with a 2px inset border and Arial, next to styled
   40px siblings. Nothing warns about a type that is not on a list; a new
   `type=` in index.html would have drifted the same way. */
.settings-pref-form input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]) {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--fs-control);
  font-weight: 500;
  color: var(--ink);
  background: color-mix(in srgb, var(--bg) 70%, var(--bg-elevated));
  border: 1px solid var(--control-line);
  border-radius: var(--radius);
  padding: 0.55rem 0.7rem;
}

.settings-pref-actions {
  display: flex;
  justify-content: flex-start;
  gap: 0.5rem;
}

.settings-toggle {
  /* `display` too, not just the direction: the class used to inherit its flex
     context from `.settings-pref-form label`, so the first use outside a
     pref form stacked the box above its own label. */
  display: flex;
  flex-direction: row !important;
  align-items: flex-start;
  gap: 0.75rem !important;
  font-weight: 500 !important;
  color: var(--ink) !important;
  cursor: pointer;
}

.settings-toggle input[type="checkbox"] {
  margin-top: 0.2rem;
  width: 1.05rem;
  height: 1.05rem;
  flex-shrink: 0;
  accent-color: var(--accent);
}

.settings-toggle-title {
  display: block;
  font-weight: 650;
  font-size: 0.92rem;
  margin-bottom: 0.2rem;
}

.settings-toggle .hint {
  display: block;
  font-weight: 500;
}

.project-settings-section {
  margin: 1rem 0 1.25rem;
  padding: 0.9rem 1rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
}

.project-settings-section h3 {
  margin: 0 0 0.75rem;
  font-size: 0.95rem;
}

.access-bind {
  margin: 0;
  font-size: 0.9rem;
}

.access-feature,
.access-block {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  background: color-mix(in srgb, var(--bg) 55%, var(--bg-elevated));
  min-width: 0;
}

.access-feature {
  grid-column: 1 / -1;
  border-color: color-mix(in srgb, var(--accent) 28%, var(--line-soft));
  background:
    linear-gradient(145deg, color-mix(in srgb, var(--accent) 8%, transparent), transparent 55%),
    color-mix(in srgb, var(--bg-elevated) 88%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--bright) 55%, transparent);
}

.access-block-wide {
  grid-column: 1 / -1;
}

.access-block-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.65rem;
}

.access-block-head h4 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.access-feature .access-block-head h4 {
  color: var(--ink);
  letter-spacing: 0.02em;
  font-size: 0.95rem;
  text-transform: none;
}

.access-feature .access-block-head .hint {
  margin: 0.2rem 0 0;
}

.access-block-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.4rem;
}

.access-token {
  margin: 0;
  overflow-wrap: anywhere;
  font-size: 0.85rem;
  padding: 0.55rem 0.65rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg) 70%, var(--bright));
  border: 1px solid var(--line-soft);
}

.access-code {
  margin: 0;
  font-size: clamp(1.55rem, 3.2vw, 2rem);
  font-weight: 600;
  letter-spacing: 0.18em;
  line-height: 1.2;
  padding: 0.65rem 0.75rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg-elevated) 90%, var(--bright));
  border: 1px dashed color-mix(in srgb, var(--accent) 35%, var(--line));
  text-align: center;
}

.access-urls {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

.access-urls li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.55rem 0.65rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg) 65%, var(--bright));
  border: 1px solid var(--line-soft);
}

.access-url-kind {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  min-width: 4.4rem;
}

.access-url {
  overflow-wrap: anywhere;
  flex: 1 1 12rem;
}

.confirm-dialog form {
  gap: 0.75rem;
}

.confirm-message {
  margin: 0;
  font-size: 0.98rem;
  line-height: 1.5;
  color: var(--ink);
}

.confirm-detail {
  margin: 0;
  padding: 0.7rem 0.85rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg) 70%, var(--bright));
  color: var(--muted);
  font-size: var(--fs-small);
  line-height: 1.45;
}

.confirm-dialog.danger .confirm-detail:not(.hidden) {
  border-color: color-mix(in srgb, var(--danger) 28%, var(--line));
  background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
  color: color-mix(in srgb, var(--danger) 70%, var(--ink));
}

.confirm-dialog.danger #confirm-ok {
  background: var(--danger);
  border-color: transparent;
  color: var(--danger-ink);
  box-shadow: 0 8px 20px color-mix(in srgb, var(--danger) 22%, transparent);
}

.confirm-dialog.danger #confirm-ok:hover {
  filter: brightness(1.06);
}

/* Solid, not a gradient: the old orange ramp put a cream label on #ea580c
   at 3.6:1. Light: white on #9a3412 (7.3:1); dark: ink on #fb923c (8.6:1). */
.btn.danger-fill {
  background: var(--danger);
  border-color: transparent;
  color: var(--danger-ink);
  box-shadow: 0 8px 20px color-mix(in srgb, var(--danger) 22%, transparent);
}

.btn.danger-fill:hover {
  filter: brightness(1.06);
  border-color: transparent;
}

.error {
  color: var(--danger);
  margin: 0;
  font-size: var(--fs-small);
  font-weight: 600;
}

/* The views, as a vertical list of rows in the sidebar and in the phone
   drawer alike (Atlas Web overhaul 2026-09-28, SPEC 5): a line icon, the
   label, and in the drawer a one-line description. */
.view-nav {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

.view-tab {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.25;
  text-align: left;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.view-tab:hover {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  color: var(--ink);
}

/* The open view: an accent bar and ink text on a faint tint. Never accent
   text on an accent tint (SPEC 4), and never the colour alone -- the
   button also carries aria-current="page". */
.view-tab.active {
  background: var(--accent-tint);
  color: var(--ink);
  font-weight: 700;
}

.view-tab.active::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5rem;
  bottom: 0.5rem;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--accent);
}

.view-tab-text {
  display: grid;
  gap: 0.1rem;
  min-width: 0;
  flex: 1 1 auto;
}

.view-tab-label {
  overflow-wrap: anywhere;
}

/* The sidebar has no room for them; the drawer shows them. */
.view-tab-desc {
  display: none;
}

.nav-icon {
  width: 1.25rem;
  height: 1.25rem;
  flex-shrink: 0;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Phone navigation ---------------------------------------------------
   The pill nav is 432px wide; it cannot fit a 390px phone (issue #65).
   Below the breakpoint the header collapses to brand + this toggle, and
   every view plus both top actions move into the drawer below. */

.nav-toggle {
  display: none;
  align-items: center;
  gap: 0.55rem;
  min-height: 44px;
  padding: 0.45rem 0.85rem 0.45rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  flex-shrink: 0;
}

.nav-toggle-bars {
  display: grid;
  gap: 3px;
  width: 16px;
}

.nav-toggle-bars span {
  display: block;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}

.nav-toggle-label {
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav-drawer {
  /* Right-hand sheet: override the centred-card defaults on `dialog`. */
  margin: 0 0 0 auto;
  width: min(19rem, 84vw);
  max-width: none;
  height: 100%;
  max-height: 100%;
  border: 0;
  border-left: 1px solid var(--line-soft);
  border-radius: 0;
}

.nav-drawer[open] {
  animation: drawer-in 0.24s var(--ease) both;
}

@keyframes drawer-in {
  from { opacity: 0; transform: translateX(14%); }
  to { opacity: 1; transform: none; }
}

.nav-drawer-inner {
  box-sizing: border-box;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1rem 1.1rem calc(1.1rem + env(safe-area-inset-bottom, 0px));
  /* One scroller for the whole sheet: on a short screen the list, the
     actions and the account foot all stay reachable by one swipe. */
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Focus scrolling stops below the pinned head, and a ring drawn outside a
     button at either end is not cut off by the scroller's edge. */
  scroll-padding-block: 4.75rem 0.5rem;
}

/* The title and the close button stay put while the sheet scrolls under
   them. `top: -1rem` lets the sheet's top padding scroll away first; the
   negative margins carry the opaque background to the sheet's edges. */
.nav-drawer-head {
  position: sticky;
  top: -1rem;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-shrink: 0;
  margin: -1rem -1.1rem 0;
  padding: 1rem 1.1rem 0.5rem;
  background: var(--bg-elevated);
}

.nav-drawer-title {
  margin: 0;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.nav-drawer-views {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  /* Natural height, not a second scroller: the sheet scrolls instead.
     A list squeezed to the space left made every row shrink. */
  flex: none;
}

.nav-drawer .view-tab {
  /* A row is never shorter than its label and description. `min-height`
     replaces a flex item's automatic minimum, so without this a long list
     pressed every row down to 44px and each description ran into the
     label of the row below. */
  flex-shrink: 0;
  width: 100%;
  min-height: 44px;
  text-align: left;
  padding: 0.6rem 0.85rem;
  border-radius: var(--radius-sm);
  font-size: 1rem;
}

.nav-drawer .view-tab-desc {
  display: block;
  color: var(--muted);
  font-size: var(--fs-small);
  font-weight: 400;
  line-height: 1.3;
  /* «Fehlermeldungen» is wider than a 320px sheet at the largest text
     size; break it instead of running past the row. */
  overflow-wrap: anywhere;
  hyphens: auto;
}

/* The drawer scrolls, and a scroller cuts off a ring drawn outside a row
   at its top and bottom edge: draw it inside. */
.nav-drawer .view-tab:focus-visible {
  outline-offset: -2px;
}

.nav-drawer-actions {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  flex-shrink: 0;
}

.nav-drawer-actions .btn {
  justify-content: center;
  min-height: 44px;
}

body.nav-open {
  overflow: hidden;
}

.issue-chip, .label-chip, .milestone-chip {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  padding: 0.12rem 0.4rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--signal) 14%, transparent);
  color: var(--signal-text);
}

.label-chip {
  background: color-mix(in srgb, var(--muted) 12%, transparent);
  color: var(--idle);
}

.milestone-chip {
  background: color-mix(in srgb, var(--accent-bright) 18%, transparent);
  color: var(--accent-on-tint);
}

.issue-gh-link, .issue-title-link {
  color: inherit;
  text-decoration: none;
}

.issue-gh-link:hover, .issue-title-link:hover {
  color: var(--signal-text);
  text-decoration: underline;
}

.label-fieldset {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 0.65rem 0.75rem 0.75rem;
  margin: 0;
}

.label-fieldset legend {
  padding: 0 0.25rem;
  font-size: var(--fs-small);
}

.req-mark {
  color: var(--muted);
  font-weight: 400;
  font-size: 0.78rem;
}

.label-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 0.75rem;
  max-height: 9rem;
  overflow: auto;
}

.label-pick {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--fs-small);
  cursor: pointer;
}

.label-swatch {
  width: 0.65rem;
  height: 0.65rem;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--ink) 15%, transparent);
  flex-shrink: 0;
}

.hint.small {
  font-size: var(--fs-small);
  margin: 0.35rem 0 0;
}

.issues-layout, .schedules-layout, .dashboard-layout, .settings-layout,
.artifacts-layout {
  grid-template-columns: 1fr;
  overflow: auto;
}

.issues-layout {
  /* Toolbar on top, then list + readable issue body — same master-detail
     idea as Runs / Projects, with filters kept full-width so they stay usable. */
  grid-template-columns: minmax(280px, 360px) 1fr;
  grid-template-rows: auto 1fr;
  overflow: hidden;
}

.issues-toolbar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 0.55rem 1rem;
  padding: 1rem 1.15rem 0.55rem;
  border-bottom: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg-elevated) 40%, transparent);
}

.issues-heading {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  min-width: 0;
  margin-right: auto;
}

.issues-toolbar > #btn-new-issue {
  flex: 0 0 auto;
  order: 3;
}

/* Why «New issue» is off (#1040): a line of its own under the toolbar,
   ending under the button it explains. Shown only while the button is. */
.issues-toolbar > .issues-new-off {
  order: 4;
  flex: 1 0 100%;
  text-align: right;
}

.issues-toolbar .pane-tools {
  order: 2;
}

.issues-toolbar h2 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.issues-open-count {
  margin: 0;
  font-size: 0.78rem;
  font-family: var(--font-mono);
  white-space: nowrap;
}

.projects-layout {
  /* Same master-detail split as Runs: list + project history subpage. */
  overflow: hidden;
}

.skills-layout,
.memory-layout {
  overflow: hidden;
}

.skills-pane,
.memory-pane {
  border-right: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg-elevated) 55%, transparent);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
  height: 100%;
}

/* Installed / Available scopes sit between the fixed chrome (head, tabs)
   and the scrollable .skill-list. Without this flex chain, .skill-list's
   `flex: 1; overflow: auto` is a no-op (its parent is not a flex container),
   the list grows with content, and .skills-pane clips it — ClaudeOS#168. */
#skills-scope-installed,
#skills-scope-available {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

.skills-subtabs {
  display: flex;
  gap: 0.3rem;
  padding: 0 0.85rem 0.6rem;
  flex-shrink: 0;
}

.skills-subtab {
  flex: 1;
  border: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  color: var(--muted);
  font: inherit;
  font-weight: 600;
  font-size: var(--fs-small);
  padding: 0.4rem 0.7rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.skills-subtab.active {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}

.skills-tools {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  padding: 0 0.85rem 0.55rem;
  flex-shrink: 0;
}

.skills-tools .inline-filter select,
.skill-search-label input {
  width: 100%;
  box-sizing: border-box;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--control-line);
  background: color-mix(in srgb, var(--bg-elevated) 88%, transparent);
  color: inherit;
  font: inherit;
}

.skill-search-label input:focus,
.skills-tools .inline-filter select:focus {
  border-color: var(--accent);
}

.skills-count {
  margin: 0 0.95rem 0.35rem;
  font-size: 0.78rem;
  flex-shrink: 0;
}

.skill-list,
.memory-list {
  padding: 0.35rem 0.65rem 1rem;
  overflow: auto;
  flex: 1 1 auto;
  min-height: 0;
  list-style: none;
  margin: 0;
  -webkit-overflow-scrolling: touch;
}

.skill-list li + li,
.memory-list li + li {
  margin-top: 0.3rem;
}

.skill-select {
  width: 100%;
  text-align: left;
  border: 1px solid transparent;
  background: transparent;
  padding: 0.8rem 0.85rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: inherit;
  font: inherit;
}

.skill-select:hover {
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
  border-color: var(--line-soft);
}

.skill-select.active {
  background: color-mix(in srgb, var(--accent) 12%, var(--bg-elevated));
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

.skill-select .rid {
  font-weight: 700;
  margin-bottom: 0.2rem;
}

.skill-select .rmeta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.55rem;
  font-size: 0.78rem;
  color: var(--muted);
  margin-bottom: 0.35rem;
}

.skill-card-desc {
  margin: 0;
  font-size: var(--fs-small);
  line-height: 1.4;
  color: color-mix(in srgb, var(--text) 82%, var(--muted));
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.skill-mirror {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.skill-mirror.on {
  color: var(--accent-text);
}

.skill-description {
  margin: 0;
  line-height: 1.5;
  color: color-mix(in srgb, var(--text) 90%, var(--muted));
  overflow-wrap: anywhere;
}

.skill-body-section,
.skill-desc-section {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

/* The description is the whole point of this pane — never let flex shrink it.
   Skill descriptions run 250–700 chars, so with the default `flex-shrink: 1`
   it collapsed to ~2 lines and clipped mid-sentence over the "Skill" heading
   (ClaudeOS#144). Fixed size here; the body below is the scroller. */
.skill-desc-section {
  flex: 0 0 auto;
  min-height: auto;
}

/* Same role as .issue-body-section: scroll the long markdown body while the
   header and description stay put. min-height keeps the body from vanishing
   on a short viewport — .skill-detail-pane .detail then scrolls instead. */
.skill-body-section {
  flex: 1 1 auto;
  min-height: 6rem;
  overflow: auto;
}

/* Layer badge -- the resolver's five layers (project/custom/variant/
   registry/bundled), shown on every skill row and detail pane so which
   layer currently wins (and what it shadows) is never invisible. */
.skill-layer-badge {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  color: var(--muted);
}

.skill-layer-badge.layer-project { color: var(--accent-text); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.skill-layer-badge.layer-variant { color: var(--violet); border-color: color-mix(in srgb, var(--violet) 45%, transparent); }
.skill-layer-badge.layer-registry { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent); }

.skill-install-badge {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
}

.skill-install-badge.on { color: var(--accent-text); }
.skill-install-badge.update { color: var(--accent-text); }

.skill-catalog-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.skill-update-confirm,
.skill-locally-modified {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
  padding: 0.85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.skill-update-confirm h3,
.skill-locally-modified h3 {
  margin: 0;
  font-size: 0.95rem;
}

.skill-changelog-preview h4,
.skill-conflict-choice h4 {
  margin: 0 0 0.2rem;
  font-size: 0.82rem;
}

.skill-conflict-choice {
  border-top: 1px solid var(--line-soft);
  padding-top: 0.55rem;
}

.skill-catalog-action-ok {
  color: var(--accent-text);
}

/* -- Skills: the catalog people see (AtlasOS#601) ------------------------ */

/* One column, one section. The two developer panes (list + detail) are
   still in the markup so the technical view can come back with one class
   change, but they are display:none here -- nothing in the simple face may
   leak an id, a version or a layer badge through a stray flex rule. */
.skills-layout.simple {
  grid-template-columns: 1fr;
}

.skills-layout.simple .skills-pane,
.skills-layout.simple .skill-detail-pane {
  display: none;
}

.skill-catalog-simple {
  display: none;
  flex-direction: column;
  min-height: 0;
  height: 100%;
  overflow: auto;
  padding: 0 0 1.5rem;
  -webkit-overflow-scrolling: touch;
}

.skills-layout.simple .skill-catalog-simple {
  display: flex;
}

.skill-catalog-simple-hint {
  margin: 0 0.95rem 0.7rem;
}

.skill-catalog-simple-tools {
  max-width: 420px;
}

.skill-card-grid {
  list-style: none;
  margin: 0;
  padding: 0.35rem 0.85rem 0.5rem;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 0.75rem;
  flex-shrink: 0;
}

.skill-card {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  min-width: 0;
  padding: 0.95rem 1rem 1rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-elevated) 82%, transparent);
}

.skill-card.is-active {
  border-color: color-mix(in srgb, var(--accent) 38%, var(--line-soft));
}

.skill-card-head {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  min-width: 0;
}

.skill-card-icon {
  flex: 0 0 auto;
  width: 2.7rem;
  height: 2.7rem;
  display: grid;
  place-items: center;
  font-size: 1.5rem;
  line-height: 1;
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 12%, var(--bg));
}

.skill-card-heading {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.skill-card-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

.skill-card-state {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
}

.skill-card-state.on {
  color: var(--accent-text);
}

.skill-card-state.update {
  color: var(--accent-bright);
}

.skill-card-summary {
  margin: 0;
  flex: 1 1 auto;
  font-size: 0.88rem;
  line-height: 1.45;
  color: color-mix(in srgb, var(--text) 85%, var(--muted));
  overflow-wrap: anywhere;
}

.skill-card-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
  min-height: 1.8rem;
}

.skill-card-note {
  margin: 0;
  font-size: var(--fs-small);
  line-height: 1.4;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
}

.skill-card-note.ok {
  color: var(--accent-text);
}

.skill-card-note.error {
  color: var(--danger);
}

.skill-own-section {
  margin: 1.1rem 0 0;
  padding: 1rem 0 0;
  border-top: 1px solid var(--line-soft);
  flex-shrink: 0;
}

.skill-own-section h3 {
  margin: 0 0.95rem 0.2rem;
  font-size: 0.95rem;
}

.skill-own-section .hint {
  margin: 0 0.95rem 0.6rem;
}

/* -- Skills > Mine: the user's own skills (AtlasOS#321) ------------------ */

.custom-skill-form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.custom-skill-form .field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.custom-skill-form .field-label {
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.01em;
}

.custom-skill-form input,
.custom-skill-form textarea {
  width: 100%;
  box-sizing: border-box;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--control-line);
  background: color-mix(in srgb, var(--bg-elevated) 88%, transparent);
  color: inherit;
  font: inherit;
  resize: vertical;
}

.custom-skill-form textarea#custom-skill-body {
  font-size: var(--fs-control);
  line-height: 1.5;
  min-height: 16rem;
}

.custom-skill-form input:focus,
.custom-skill-form textarea:focus {
  border-color: var(--accent);
}

/* An id is the directory name and cannot be renamed in place, so on an
   existing skill the input is shown disabled rather than hidden -- the
   value still has to be readable, it just is not editable. */
.custom-skill-form input:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.custom-skill-form .hint {
  margin: 0;
  font-size: var(--fs-small);
}

.skills-tools #btn-custom-skill-new {
  align-self: flex-start;
}

.projects-pane {
  border-right: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg-elevated) 55%, transparent);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
  height: 100%;
}

.projects-tools {
  padding: 0 0.85rem 0.55rem;
}

.project-search-label {
  display: block;
}

.project-search-label input {
  width: 100%;
  box-sizing: border-box;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--control-line);
  background: color-mix(in srgb, var(--bg-elevated) 88%, transparent);
  color: inherit;
  font: inherit;
}

.project-search-label input:focus {
  border-color: var(--accent);
}

.project-row {
  display: flex;
  align-items: stretch;
  gap: 0.2rem;
}

.project-row .project-select {
  flex: 1;
  min-width: 0;
}

.btn-favorite-toggle {
  flex: 0 0 auto;
  align-self: center;
  width: 2.2rem;
  height: 2.2rem;
  padding: 0;
  border-radius: var(--radius-sm);
  font-size: 1.05rem;
  line-height: 1;
  color: var(--muted);
}

.btn-favorite-toggle[aria-pressed="true"],
.btn-favorite-toggle.is-on {
  color: var(--accent-text);
}

.project-detail-pane,
.issue-detail-pane,
.skill-detail-pane,
.memory-detail-pane {
  min-height: 0;
  height: 100%;
  overflow: hidden;
  padding: 1.15rem 1.35rem 1.6rem;
  display: flex;
  flex-direction: column;
}

/* Constrain the flex child so the inner scroller (commit list / issue body)
   gets a real height budget — without this, long content clips with no scroll. */
.project-detail-pane .detail,
.issue-detail-pane .detail {
  min-height: 0;
  overflow: hidden;
}

/* The three scope wrappers (Installed / Available / Mine) sit between the
   pane and the scroller, and a plain <div> flex item defaults to
   min-height:auto -- so it grows past the pane instead of handing its
   height budget down, and `.skill-detail-pane { overflow: hidden }` then
   clips whatever did not fit with no way to scroll to it. That is
   ClaudeOS#178, and it is what put the new skill editor's Save button
   permanently off-screen at 1280x720 (AtlasOS#321). `.hidden` is
   `display: none !important`, so the display here cannot un-hide a pane. */
#skill-detail-scope-installed,
#skill-detail-scope-available,
#skill-detail-scope-custom {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.skill-detail-pane .detail,
.memory-detail-pane .detail {
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.schedules-pane, .dashboard-pane, .settings-pane, .artifacts-pane {
  padding: 0 0 1.5rem;
  width: min(1100px, 100%);
  margin: 0 auto;
  height: auto;
  overflow: visible;
}

.issues-pane {
  border-right: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg-elevated) 55%, transparent);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
  height: 100%;
  width: auto;
  margin: 0;
  padding: 0;
}

.dashboard-body {
  display: flex;
  flex-direction: column;
  gap: 1.4rem;
  padding: 0.35rem 1.15rem 1rem;
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.75rem;
}

.stat-tile {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  padding: 0.95rem 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-soft);
  border-top: 3px solid color-mix(in srgb, var(--queue) 55%, transparent);
  animation: rise 0.5s var(--ease) both;
}

.stat-tile.tone-ok { border-top-color: var(--ok); }
.stat-tile.tone-fail { border-top-color: var(--fail); }
.stat-tile.tone-run { border-top-color: var(--run); }
.stat-tile.tone-signal { border-top-color: var(--signal); }
.stat-tile.tone-muted { border-top-color: var(--muted); }

/* Six customer tiles, not eight: "Unmerged" and "Repos" moved into the
   dashboard's "Technical details" disclosure (ClaudeOS#913). Three columns so
   six tiles fill two even rows rather than four plus a gap. The <=820px rule
   further down re-states both of these selectors on purpose -- a bare
   `.stat-grid` there loses on specificity to a two-class selector, however
   late in the file it sits. */
.stat-grid.dash-overview-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* The two relocated tiles: compact and left-aligned, not stretched across the
   disclosure's full width. Two classes, so it outranks the bare `.stat-grid`
   above regardless of order. */
.stat-grid.dash-technical-grid {
  grid-template-columns: repeat(auto-fit, minmax(11rem, 16rem));
}

.stat-label {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.stat-value {
  font-family: var(--font-display);
  font-size: clamp(1.45rem, 2.4vw, 1.85rem);
  font-weight: 780;
  letter-spacing: -0.03em;
  line-height: 1;
}

/* One 14-day chart needs 38.5rem, or its date axis truncates: at 38.5rem a
   day column is 36.3px and the widest label this axis can produce is the
   de-CH "Mo., 14." at 33.9px, so it clears by 2.4px (ClaudeOS#916).

   The fixed `1fr 1fr` this replaces could not meet that at ANY window size,
   which is worse than the 821-1166px band the issue describes. `.dashboard-
   pane` is `width: min(1100px, 100%)` (app.css:4374), so `.dashboard-charts`
   is capped at 1063.2px however wide the screen is; halved, each chart got a
   488px box and a 29.7px column. Measured live at an 868px window: 14 of 14
   labels ellipsed in BOTH charts. So these two stack full width today at
   every size, and that is the fix, not a side effect -- one readable chart
   above another beats two truncated ones. auto-fit rather than a flat `1fr`
   so that two-up comes back on its own if that 1100px cap is ever raised
   past 1248px, which is the width at which it would finally be honest.

   min(38.5rem, 100%) is the auto-fit overflow guard: without it a container
   narrower than 38.5rem still gets a 38.5rem track and the page scrolls
   sideways. repeat(auto-fit, minmax(...)) is used 5x elsewhere in this file
   (1351, 3117, 3189, 4716, 5743), but the min() guard is new here and is
   load-bearing -- every one of those floors is 11-14rem, an order of
   magnitude below a viewport, so none of them ever needed it. */
.dashboard-charts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(38.5rem, 100%), 1fr));
  gap: 1rem;
}

.dashboard-columns {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 1rem;
}

.saved-section .dash-subsection {
  margin-top: 1.1rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line-soft);
}

.saved-section .dash-subsection h4 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.saved-figures {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.85rem;
}

.saved-figure {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.saved-figure-value {
  font-family: var(--font-display);
  font-size: 1.55rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.15;
}

.saved-chart .sessions-bar-track i {
  /* Shaded against --ink, not --bright, on purpose. In the light theme
     `--surface: var(--surface)` is self-referential, so --bright (which is
     defined as var(--surface)) is invalid there and every color-mix using it
     collapses to `background: none` -- which is why the commits chart's bars
     are invisible in light mode. --ink is defined in both themes. */
  background: linear-gradient(180deg, color-mix(in srgb, var(--ok) 88%, var(--ink)), var(--ok));
}

.saved-chart .sessions-bar-value {
  font-size: 0.65rem;
}

.commits-chart .sessions-bar-track i {
  background: linear-gradient(180deg, color-mix(in srgb, var(--signal) 85%, var(--bright)), var(--signal));
}

.project-pie-wrap {
  min-height: 8rem;
}

.project-pie-layout {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 1.25rem 1.5rem;
  align-items: center;
}

.project-pie {
  width: 10.5rem;
  height: 10.5rem;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  box-shadow:
    inset 0 0 0 1.55rem color-mix(in srgb, var(--bg-elevated) 92%, var(--bright)),
    0 0 0 1px color-mix(in srgb, var(--line-soft) 80%, transparent);
}

.project-pie-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.55rem;
}

.project-pie-legend li {
  display: grid;
  grid-template-columns: 0.7rem minmax(0, 1fr) auto;
  gap: 0.55rem;
  align-items: center;
  min-width: 0;
}

.project-pie-swatch {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 0.2rem;
}

.project-pie-name {
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.project-pie-meta {
  font-size: 0.72rem;
  white-space: nowrap;
}

.dash-section {
  padding: 1rem 1.05rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-app-soft);
}

.dash-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.85rem;
}

.dash-section-head h3 {
  margin: 0;
}

.dash-section h3 {
  margin: 0 0 0.85rem;
  font-family: var(--font-display);
  font-size: var(--fs-section-title);
  font-weight: 700;
  letter-spacing: -0.02em;
}

.sessions-chart {
  min-height: 9.5rem;
}

.sessions-chart-empty {
  margin: 0;
  padding: 1.5rem 0;
  text-align: center;
}

.sessions-bars {
  list-style: none;
  margin: 0;
  padding: 0.15rem 0 0;
  display: grid;
  grid-template-columns: repeat(14, minmax(0, 1fr));
  gap: 0.35rem;
  align-items: end;
  min-height: 9rem;
}

.sessions-bar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  min-width: 0;
}

.sessions-bar-value {
  font-size: 0.68rem;
  color: var(--muted);
  line-height: 1;
}

.sessions-bar.is-zero .sessions-bar-value {
  opacity: 0.45;
}

.sessions-bar-track {
  width: 100%;
  max-width: 1.55rem;
  height: 6.4rem;
  border-radius: 0.45rem 0.45rem 0.2rem 0.2rem;
  background: color-mix(in srgb, var(--line) 45%, transparent);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}

.sessions-bar-track i {
  display: block;
  width: 100%;
  min-height: 0;
  border-radius: inherit;
  background: linear-gradient(180deg, var(--accent-bright), var(--accent));
  transition: height 0.45s var(--ease);
}

.sessions-bar-label {
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--muted);
  text-align: center;
  line-height: 1.15;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* Seven columns instead of fourteen: renderDaySeriesChart() writes the count
   it actually drew, and the bar goes back to its full width so half as many
   bars do not read as a sparse chart. Keyed off the rendered count rather
   than a viewport breakpoint, because a chart's box and the window are not
   the same width here (ClaudeOS#916).

   The LABEL deliberately stays at the narrow-width 0.55rem: at a 320px
   viewport a 7-column track is 32.4px and a 0.62rem "Mo., 14." would be
   33.9px, so bumping it would start truncating again on the smallest
   phones. Halving the column count is what cures the truncation; a larger
   axis type on phones needs a shrink-to-fit label and is a separate job. */
.sessions-chart[data-day-count="7"] .sessions-bar-track {
  max-width: 1.55rem;
}

.status-bars {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}

.status-bar-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.35rem;
}

.status-bar-track {
  height: 0.45rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--line) 55%, transparent);
  overflow: hidden;
}

.status-bar-track i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--accent), var(--accent-bright));
}

.usage-meta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.85rem 1rem;
  margin: 0;
}

/* Both usage panels carry three rows since ClaudeOS#913 split them, and three
   items in a two-column grid leave an orphan beside an empty cell. One column
   each; the notes already span the full width either way. */
#stat-usage,
#stat-usage-technical {
  grid-template-columns: 1fr;
}

.usage-meta dt {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}

.usage-meta dd {
  margin: 0.25rem 0 0;
  font-weight: 600;
}

.usage-meta .hint,
.usage-meta .usage-note {
  grid-column: 1 / -1;
  margin-top: 0.2rem;
}

.backend-stats {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.65rem;
}

.backend-stats li {
  padding: 0.75rem 0.85rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--bg) 55%, transparent);
}

.backend-name {
  font-weight: 700;
  margin-bottom: 0.25rem;
}

.backend-meta {
  font-size: 0.78rem;
}

.pane-tools {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.inline-filter select {
  font: inherit;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  padding: 0.35rem 0.55rem;
  background: var(--bg-elevated);
  color: inherit;
}

.issue-list, .project-list, .schedule-list, .skill-list, .artifact-list, .memory-list {
  list-style: none;
  margin: 0;
  padding: 0.35rem 1.15rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.issue-list {
  padding: 0.35rem 0.65rem 1rem;
  overflow: auto;
  flex: 1;
  gap: 0.3rem;
}

.issue-list li + li {
  margin-top: 0;
}

.issue-select {
  width: 100%;
  text-align: left;
  border: 1px solid transparent;
  background: transparent;
  padding: 0.8rem 0.85rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: inherit;
  font: inherit;
  transition: background 0.18s ease, border-color 0.18s ease;
}

.issue-select:hover {
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
  border-color: var(--line-soft);
}

.issue-select.active {
  background: color-mix(in srgb, var(--accent) 12%, var(--bg-elevated));
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  box-shadow: inset 3px 0 0 var(--accent-bright);
}

.issue-select .rid {
  font-size: 0.72rem;
  color: var(--muted);
  margin-bottom: 0.2rem;
}

.issue-select .rtask {
  font-weight: 600;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.issue-select .rmeta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.45rem;
  align-items: center;
  margin-top: 0.4rem;
}

.issue-run-chip {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  padding: 0.12rem 0.4rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--run) 14%, transparent);
  color: var(--run);
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.45rem;
  align-items: center;
}

.issue-detail-pane .detail-head,
.issue-detail-pane .meta {
  flex-shrink: 0;
}

.issue-body-section {
  margin-top: 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  min-height: 0;
  flex: 1;
  /* Same role as .commit-list on Projects: scroll long bodies while the
     detail header (Open in GitHub / Run) stays reachable. */
  overflow: auto;
}

.issue-body-label {
  margin: 0;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  flex-shrink: 0;
}

.issue-body {
  /* Size to markdown content; the section above is the scroll container. */
  flex: 0 0 auto;
  min-height: 0;
  max-height: none;
  line-height: 1.55;
  font-size: 0.98rem;
}

.issue-body h1 { font-size: 1.35rem; }
.issue-body h2 { font-size: 1.15rem; }
.issue-body h3 { font-size: 1.05rem; }

#id-title {
  font-size: clamp(1.15rem, 2vw, 1.45rem);
  line-height: 1.25;
  overflow-wrap: anywhere;
  display: block;
  -webkit-line-clamp: unset;
  max-width: none;
}

.project-list {
  padding: 0.35rem 0.65rem 1rem;
  overflow: auto;
  flex: 1;
  gap: 0.3rem;
}

.project-list li + li {
  margin-top: 0;
}

.project-select {
  width: 100%;
  text-align: left;
  border: 1px solid transparent;
  background: transparent;
  padding: 0.8rem 0.85rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: inherit;
  font: inherit;
}

.project-select:hover {
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
  border-color: var(--line-soft);
}

.project-select.active {
  background: color-mix(in srgb, var(--accent) 12%, var(--bg-elevated));
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

.project-select .rid {
  font-weight: 700;
  margin-bottom: 0.2rem;
}

.project-select .rmeta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.55rem;
  font-size: 0.78rem;
  color: var(--muted);
}

.issue-card, .project-card, .schedule-card {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  align-items: flex-start;
  padding: 0.95rem 1rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-elevated) 80%, transparent);
  box-shadow: var(--shadow-soft);
}

.schedule-card {
  flex-direction: column;
  align-items: stretch;
}

.schedule-body {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  min-width: 0;
  width: 100%;
}

.schedule-top {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  align-items: flex-start;
}

.schedule-runs {
  border-top: 1px solid var(--line-soft);
  padding-top: 0.55rem;
}

.schedule-runs > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
  user-select: none;
}

.schedule-runs > summary::-webkit-details-marker {
  display: none;
}

.schedule-runs > summary::before {
  content: "▸";
  display: inline-block;
  width: 0.9rem;
  color: var(--muted);
  transition: transform 0.12s ease;
}

.schedule-runs[open] > summary::before {
  transform: rotate(90deg);
}

.schedule-runs-body {
  margin-top: 0.55rem;
}

.schedule-run-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
}

.schedule-run-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.55rem 0.75rem;
  border-bottom: 1px solid var(--line-soft);
}

.schedule-run-row:last-child {
  border-bottom: 0;
}

.schedule-run-main {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.55rem;
  min-width: 0;
}

button.linkish {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  color: var(--accent-text);
  cursor: pointer;
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 0.12em;
}

button.linkish:hover {
  filter: brightness(1.1);
}

.status-pill.archived {
  color: var(--idle);
}

.status-pill.archived::before {
  animation: none;
  opacity: 0.55;
}

.history-section {
  margin-top: 0.35rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  min-height: 0;
  flex: 1;
}

.history-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.history-section h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.history-count {
  margin: 0;
  font-size: 0.78rem;
  font-family: var(--font-mono);
}

.commit-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  overflow: auto;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-elevated) 78%, transparent);
  box-shadow: var(--shadow-soft);
}

.commit-day {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0;
  padding: 0.5rem 0.95rem 0.4rem;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  background: color-mix(in srgb, var(--bg-elevated) 92%, var(--bg));
  border-bottom: 1px solid var(--line-soft);
}

.commit-row {
  position: relative;
  display: grid;
  grid-template-columns: 0.7rem 4.6rem minmax(0, 1fr) minmax(7.5rem, auto);
  gap: 0.35rem 0.65rem;
  align-items: baseline;
  padding: 0.48rem 0.95rem 0.48rem 0.55rem;
  border-bottom: 1px solid var(--line-soft);
  transition: background 0.15s ease;
}

.commit-row:last-child {
  border-bottom: 0;
}

.commit-row:hover {
  background: color-mix(in srgb, var(--accent-bright) 7%, transparent);
}

.commit-rail {
  position: relative;
  width: 0.7rem;
  align-self: stretch;
}

.commit-rail::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -0.48rem;
  bottom: -0.48rem;
  width: 1px;
  background: color-mix(in srgb, var(--accent-bright) 35%, var(--line));
  transform: translateX(-50%);
}

.commit-row:first-of-type .commit-rail::before,
.commit-day + .commit-row .commit-rail::before {
  top: 0.85rem;
}

.commit-row:last-child .commit-rail::before {
  bottom: calc(100% - 0.85rem);
}

.commit-rail::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 0.72rem;
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
  background: var(--bg-elevated);
  border: 1.5px solid var(--accent);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--bg-elevated) 90%, transparent);
}

.commit-sha-col {
  min-width: 0;
}

.commit-sha {
  color: var(--accent-text);
  text-decoration: none;
  font-size: 0.78rem;
  font-weight: 600;
}

a.commit-sha:hover {
  text-decoration: underline;
}

.commit-row .commit-main {
  min-width: 0;
  overflow: hidden;
  font-weight: 500;
  line-height: 1.35;
}

.commit-subject-row {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  min-width: 0;
  max-width: 100%;
}

.commit-msg {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.commit-run-chip,
.commit-turn-chip {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0.08rem 0.38rem;
  border-radius: 999px;
  line-height: 1.3;
}

.commit-run-chip {
  background: color-mix(in srgb, var(--signal) 14%, transparent);
  color: var(--signal-text);
}

.commit-turn-chip {
  background: color-mix(in srgb, var(--muted) 12%, transparent);
  color: var(--idle);
}

.commit-row .commit-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.1rem;
  font-size: 0.75rem;
  color: var(--muted);
  min-width: 0;
  text-align: right;
}

.commit-author {
  max-width: 11rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink);
  font-weight: 600;
}

.commit-when {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.issue-main, .project-main, .schedule-main {
  min-width: 0;
  flex: 1;
}

.issue-main .rmeta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.45rem;
  align-items: center;
  margin-top: 0.35rem;
}

/* No height cap since ClaudeOS#917: now that this is collapsed behind a
   <details>, a capped box would be a scroll area nested inside the page
   scroll -- on touch it either steals the page gesture or clips the prompt
   mid-sentence. Expanded, it is simply as tall as the prompt is. These two
   declarations beat `.prompt-body` (app.css:1026) on source order at equal
   specificity. */
.schedule-prompt {
  margin: 0.45rem 0 0;
  max-height: none;
  overflow: visible;
  padding: 0.55rem 0.65rem;
  border: 1px solid var(--line-soft);
  border-radius: calc(var(--radius) - 2px);
  background: color-mix(in srgb, var(--bg-elevated) 60%, transparent);
  font-size: 0.85rem;
}

/* Borrows the disclosure triangle of `.schedule-runs > summary` above, so
   the two collapsibles on one routine card read as siblings. */
.schedule-prompt-wrap {
  margin-top: 0.45rem;
}

.schedule-prompt-wrap > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--muted);
  user-select: none;
}

.schedule-prompt-wrap > summary::-webkit-details-marker {
  display: none;
}

.schedule-prompt-wrap > summary::before {
  content: "\25b8";
  display: inline-block;
  width: 0.9rem;
  color: var(--muted);
  transition: transform 0.12s ease;
}

.schedule-prompt-wrap[open] > summary::before {
  transform: rotate(90deg);
}

/* Prose, not identifiers: these two lost `.mono` in ClaudeOS#917 and need a
   wrap rule of their own, because a long zone name or project path used to
   rely on `.mono`'s. */
.schedule-cadence,
.schedule-when {
  overflow-wrap: anywhere;
}

.schedule-main .rmeta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.45rem;
  align-items: center;
  margin-top: 0.35rem;
}

.issue-side, .project-side, .schedule-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.55rem;
  flex-shrink: 0;
}

.issue-actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.issue-actions a.btn {
  text-decoration: none;
}

.scheduler-status {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  align-items: flex-start;
  margin: 0 1.15rem 0.85rem;
  padding: 0.9rem 1rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--signal) 8%, var(--bg-elevated));
}

.scheduler-status-main {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  min-width: 0;
}

.scheduler-summary {
  margin: 0;
  font-weight: 600;
}

.scheduler-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  justify-content: flex-end;
}

.timing-fieldset {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 0.75rem 0.9rem 0.9rem;
  margin: 0;
}

.timing-fieldset legend {
  padding: 0 0.35rem;
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--muted);
}

/* The picker replaced three radio rows of free text. `.timing-row` and its
   overrides went with them; what is left is a pattern <select> plus only the
   controls that pattern needs. */
.timing-pattern-label {
  display: block;
  margin-top: 0.35rem;
}

.timing-parts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.7rem;
  margin-top: 0.7rem;
}

/* `display: flex !important` is needed to beat the dialog's generic
   `label { display: ... }` rule -- which means .hidden's `display: none`
   loses to it, so the hidden parts stayed on screen. Re-assert it. */
.timing-part.hidden {
  display: none !important;
}

.timing-part {
  display: flex !important;
  align-items: center;
  gap: 0.45rem;
  margin: 0 !important;
  font-size: 0.9rem;
  color: var(--muted);
}

.timing-part > span {
  white-space: nowrap;
}

.timing-part input,
.timing-part select {
  margin: 0 !important;
  width: auto !important;
  min-width: 0;
}

.timing-part input[type="number"] { width: 5rem !important; }
.timing-part input[type="time"] { width: 8rem !important; }
.timing-part input[type="datetime-local"] { width: 14rem !important; }
.timing-part input[type="text"] { width: 13rem !important; }

/* The plain-language read-back. It is the whole point of the picker: the
   person creating a routine should be able to confirm what they asked for
   without decoding a cron expression. */
.timing-preview {
  margin: 0.8rem 0 0;
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--ink);
  font-size: 0.88rem;
  line-height: 1.45;
}

.timing-preview:empty { display: none; }

.issue-run .run-link,
#id-run .run-link {
  border: 0;
  background: transparent;
  color: var(--accent-text);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  cursor: pointer;
  text-decoration: underline;
  padding: 0;
}

/* The Connect button plus its Teams opt-in, kept together on the right of
   the connectors card head. */
.settings-connect-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.4rem;
}

.check-row {
  display: flex !important;
  flex-direction: row !important;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.9rem;
}

.check-row input {
  width: auto !important;
}

.danger-text {
  color: var(--danger) !important;
}

.small {
  font-size: var(--fs-small);
  margin-top: 0.35rem;
}

/* Phone: swap to the short label; desktop keeps .label-long. */
.label-short {
  display: none;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

@media (max-width: 820px) {
  /* The phone top bar, 56px (SPEC 5): the wordmark, the call button and the
     account button. #63 gave the nav its own row and made it scroll
     horizontally; #65 replaced that with the drawer, and the overhaul moved
     the views to #tabbar at the bottom, which opens the drawer as «Menü». */
  .top {
    flex-wrap: nowrap;
    gap: 0.5rem;
    min-height: var(--topbar-h);
    padding: 0.35rem 0.6rem 0.35rem 1rem;
  }

  .view-nav {
    display: none;
  }

  /* Call Atlas stays in the header instead of moving into the drawer with the
     other two actions. A call is the one control here with a state that has to
     stay visible while it runs -- an open microphone, and the only way to hang
     up. The drawer is a modal sheet that closes on every pick, so a Call button
     living only in there would take the live dot and the "End call" tap out of
     sight for the whole call. The map and «Werkzeuge» carry no such state,
     which is why they move to the drawer. */
  .top-actions {
    margin-left: auto;
    gap: 0.4rem;
  }

  .top-actions > :not(.voice-btn) {
    display: none;
  }

  /* «Neue Aufgabe» left .top-actions for the sidebar; on a phone the FAB and
     the drawer carry it. */
  .top #btn-new {
    display: none;
  }

  .voice-btn {
    min-height: 44px;
  }

  /* The call panel moves to the bottom edge: a thumb reaches there, not the
     top right, and hang-up is the control that most needs reaching. */
  .voice-panel {
    top: auto;
    right: 0.75rem;
    left: 0.75rem;
    bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
    width: auto;
  }

  /* Three classes, not two: a later `.btn.small { min-height: 2.5rem }` has
     the same specificity as `.voice-panel-actions .btn` and, coming later,
     won -- measured at 40px on a phone, short of the 44px tap target. */
  .voice-panel .voice-panel-actions .btn {
    min-height: 44px;
    flex: 1;
  }

  /* #btn-nav-menu stays in the DOM (app.js labels it and opens the drawer
     through it), but on a phone the tab bar's «Menü» is the way in. */
  .nav-toggle {
    display: none;
  }

  /* The product's primary action, back where a thumb reaches it
     (ClaudeOS#920). It stays in the drawer as well; this is an additional
     way in, not a move. */
  .btn.atlas-fab {
    display: inline-flex;
  }

  /* Where it would sit on top of something, it stands down. Every test on a
     view is `:not(.hidden)` because syncMobilePanes leaves `mobile-detail` on
     a view until its selection clears -- open a task, then go to Settings,
     and a bare `.mobile-detail` test would wrongly hide the button there too.
     Same shape as the `.top` rule above.
       - A detail screen owns the whole phone screen and brings its own
         bottom controls: the Tasks detail ends in #send-form, whose Send
         button is exactly where this button would be.
       - A call owns the bottom edge (.voice-panel moves there above), and
         hanging up is the control that most needs reaching. The button would
         be buried under an opaque panel and still be in the tab order. The
         panel also stays up for eight seconds after the call ends to show the
         final cost, so the button returns a moment late on purpose.
       - The full-screen embed (the map) covers the button visually at z 70,
         but covering is not removing: without this rule the button stays in
         the tab order and in the accessibility tree behind an opaque sheet.
         A class on <html>, so no :has() is needed.
       - First-run setup has its own terminal input row at the bottom, and no
         task can run until the wizard is finished anyway.
     Deliberately NOT guarded against: #restart-overlay (z 80) covers the
     button the same way, but it is shown for the seconds around a restart
     during which nothing else is operable either, so a fifth rule would buy
     nothing. */
  body:has(.layout.mobile-detail:not(.hidden)) .btn.atlas-fab,
  body:has(#voice-panel:not(.hidden)) .btn.atlas-fab,
  body:has(#view-setup:not(.hidden)) .btn.atlas-fab,
  html.embed-full-open .btn.atlas-fab,
  /* The tab bar stands down in exactly the same places (SPEC 5): a detail
     screen brings its own way back, the call panel takes the bottom edge,
     setup has its own input row, and the map covers the page. */
  body:has(.layout.mobile-detail:not(.hidden)) .tabbar,
  body:has(#voice-panel:not(.hidden)) .tabbar,
  body:has(#view-setup:not(.hidden)) .tabbar,
  html.embed-full-open .tabbar {
    display: none;
  }

  /* A strip of empty space at the end of everything that scrolls underneath
     the button, so the last row is never stuck beneath it. A pseudo-element
     rather than `padding-bottom`: five of these (.issue-list, .project-list,
     .skill-list, .memory-list, .skill-catalog-simple) are flex scroll
     containers, and WebKit drops a scroll container's end padding once its
     flex content overflows -- iOS Safari being the browser this button exists
     for. .run-list and the Tasks board's columns (.run-board-list) are plain
     `overflow: auto` blocks and the four *-pane entries are the `overflow:
     visible` content of views whose <main> is the scroller; they take the
     same spacer for consistency. */
  .run-list::after,
  .run-board-list::after,
  .issue-list::after,
  .project-list::after,
  .skill-list::after,
  .memory-list::after,
  .skill-catalog-simple::after,
  .dashboard-pane::after,
  .schedules-pane::after,
  .artifacts-pane::after,
  .settings-pane::after {
    content: "";
    display: block;
    flex: 0 0 auto;
    height: 4.25rem;
  }

  /* Brand compaction from #63 — still worth it with the single-row header. */
  .brand {
    gap: 0.2rem;
  }

  /* A fixed height: the wordmark need not grow with the text size, and the
     top bar has to keep room for the call and account buttons. Where it
     still runs out, the wordmark gives way, not the buttons. */
  .brand-lockup {
    height: 22px;
  }

  .top .brand {
    flex-shrink: 1;
    overflow: hidden;
  }

  .tagline {
    display: none;
  }

  /* Not only for the (now hidden) top actions: the Issues detail
     "Open in GitHub" button uses the same pair of labels. */
  .label-long {
    display: none;
  }

  .label-short {
    display: inline;
  }

  /* Full-screen Issues detail: drop global header so the body can breathe.
     "← Issues" brings it, and with it the menu, straight back. */
  body:has(#view-issues.layout.mobile-detail:not(.hidden)) .top {
    display: none;
  }

  .layout {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr);
  }

  /* Settings is the phone-facing view (that is where pairing is read), so its
     action rows wrap instead of squeezing four buttons onto one line. */
  .settings-body {
    padding: 0.2rem 0.9rem 1rem;
    gap: 0.95rem;
  }

  .access-feature,
  .access-block-head,
  .access-block-actions {
    flex-wrap: wrap;
    gap: 0.4rem;
  }

  .settings-card-head {
    flex-wrap: wrap;
  }

  .access-code {
    letter-spacing: 0.14em;
  }

  /* Phone: list XOR detail — stacked 38vh/62vh left neither usable. */
  #view-runs.layout:not(.mobile-detail) .detail-pane,
  #view-projects.layout:not(.mobile-detail) .project-detail-pane,
  #view-issues.layout:not(.mobile-detail) .issue-detail-pane,
  #view-skills.layout:not(.mobile-detail) .skill-detail-pane,
  #view-memory.layout:not(.mobile-detail) .memory-detail-pane {
    display: none;
  }

  #view-runs.layout.mobile-detail .runs-pane,
  #view-projects.layout.mobile-detail .projects-pane,
  #view-issues.layout.mobile-detail .issues-pane,
  #view-issues.layout.mobile-detail .issues-toolbar,
  #view-skills.layout.mobile-detail .skills-pane,
  #view-memory.layout.mobile-detail .memory-pane {
    display: none;
  }

  #view-issues.layout {
    grid-template-columns: 1fr;
    grid-template-rows: auto minmax(0, 1fr);
  }

  #view-issues.layout.mobile-detail {
    grid-template-rows: minmax(0, 1fr);
  }

  .issues-toolbar {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "heading new"
      "tools tools";
    align-items: center;
    gap: 0.45rem 0.65rem;
    padding: 0.65rem 0.85rem 0.5rem;
  }

  /* The reason sits right under the button's row, above the filters; the
     row exists only while there is a reason, so no empty gap is left. */
  .issues-toolbar:has(> .issues-new-off:not(.hidden)) {
    grid-template-areas:
      "heading new"
      "off off"
      "tools tools";
  }

  .issues-toolbar > .issues-new-off {
    grid-area: off;
    text-align: left;
  }

  .issues-heading {
    grid-area: heading;
    order: unset;
    justify-content: flex-start;
    width: auto;
  }

  .issues-toolbar > #btn-new-issue {
    grid-area: new;
    order: unset;
    margin-left: 0;
    padding: 0.45rem 0.75rem;
    font-size: 0.85rem;
  }

  .issues-toolbar .pane-tools {
    grid-area: tools;
    order: unset;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
    gap: 0.4rem;
    width: 100%;
    justify-content: stretch;
  }

  .issues-toolbar .inline-filter {
    min-width: 0;
    display: block;
  }

  .issues-toolbar .inline-filter:nth-child(3) {
    grid-column: 1 / 3;
  }

  .issues-toolbar .inline-filter select {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
  }

  .issues-toolbar #btn-refresh-issues {
    grid-column: 3;
    grid-row: 2;
    justify-self: stretch;
    align-self: stretch;
  }

  .runs-pane,
  .projects-pane,
  .issues-pane {
    border-right: none;
    border-bottom: none;
  }

  .detail-pane,
  .project-detail-pane,
  .issue-detail-pane {
    padding: 0.85rem 0.9rem 1.15rem;
  }

  /* Same reason as the desktop rule: on the Tasks detail the pinned head owns
     the top padding, or the sticky clamp leaves a 13.6px strip of conversation
     above the bar (ClaudeOS#918). */
  .detail-pane {
    padding-top: 0;
  }

  .mobile-back {
    display: inline-flex;
    align-items: center;
    width: auto;
    order: -1;
    margin-bottom: 0;
  }

  .issue-detail-pane .detail-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.45rem 0.65rem;
    align-items: start;
  }

  .issue-detail-pane .mobile-back {
    grid-column: 1 / -1;
    justify-self: start;
  }

  .issue-detail-pane .detail-head > div:not(.detail-actions) {
    min-width: 0;
    grid-column: 1 / -1;
  }

  .issue-detail-pane .detail-actions {
    grid-column: 1 / -1;
    width: 100%;
    justify-content: stretch;
  }

  .issue-detail-pane .detail-actions .btn {
    flex: 1 1 auto;
    padding: 0.45rem 0.65rem;
    font-size: 0.85rem;
  }

  #id-title {
    font-size: 1.1rem;
    margin-top: 0.1rem;
  }

  .issue-detail-pane .meta {
    margin-top: 0.55rem;
    gap: 0.45rem 0.75rem;
    padding: 0.65rem 0.75rem;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  }

  .issue-body-section {
    margin-top: 0.75rem;
  }

  .issue-select {
    padding: 0.65rem 0.7rem;
  }

  .detail-head {
    flex-wrap: wrap;
  }

  /* The Tasks header is pinned (ClaudeOS#918), so on a phone every row in it
     is permanently missing from the conversation. `.detail-head` wraps here,
     which puts Accept/Hide/Stop on a row of their own -- and the identical
     three buttons are already at the foot of the pane, shown and hidden by
     the same code (.detail-actions-bottom). So the copy in the pinned bar is
     pure height, and goes. */
  .detail-pane .detail-head .detail-actions {
    display: none;
  }

  /* The bleed has to match this pane's own padding, not the desktop one. */
  .detail-pane .detail-head {
    margin: 0 -0.9rem 0;
    padding: 0.85rem 0.9rem 0.6rem;
  }

  /* One line of title is enough to say what is open; the full text stays on
     the element's title attribute and in the prompt card below. */
  .detail-pane .detail-head h2 {
    -webkit-line-clamp: 1;
  }

  .detail-actions {
    flex-wrap: wrap;
  }

  .detail-actions-bottom {
    justify-content: stretch;
  }

  .detail-actions-bottom .btn {
    flex: 1 1 auto;
  }

  .commit-row {
    grid-template-columns: 0.7rem 4.2rem minmax(0, 1fr);
    grid-template-rows: auto auto;
  }

  .commit-row .commit-meta {
    grid-column: 3;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: flex-start;
    align-items: center;
    gap: 0.35rem 0.65rem;
    text-align: left;
  }

  .commit-author {
    max-width: none;
  }

  .project-card, .schedule-card, .scheduler-status {
    flex-direction: column;
  }

  .schedule-top {
    flex-direction: column;
  }

  .project-side, .schedule-side, .scheduler-actions {
    align-items: stretch;
    width: 100%;
  }

  .stat-grid,
  .stat-grid.dash-overview-grid,
  .stat-grid.dash-technical-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .stat-grid.settings-overview,
  .settings-access-grid {
    grid-template-columns: 1fr;
  }

  .dashboard-charts,
  .dashboard-columns,
  .usage-meta {
    grid-template-columns: 1fr;
  }

  .saved-figures {
    grid-template-columns: 1fr;
  }

  .project-pie-layout {
    grid-template-columns: 1fr;
    justify-items: center;
  }

  .project-pie-legend {
    width: 100%;
  }

  .sessions-bars {
    gap: 0.22rem;
  }

  .sessions-bar-label {
    font-size: 0.55rem;
  }

  .sessions-bar-track {
    max-width: 1.15rem;
    height: 5.4rem;
  }
}

/* Phone-width header budget (AtlasOS: Call button on mobile). Measured at
   390px: brand lockup 170px + Call 79px + toggle 127px, plus gaps and padding,
   comes to 432px -- and `.top` does not wrap, so the brand ran straight
   underneath the Call button. The toggle's label is the one piece that is
   genuinely redundant, since every view repeats its own name in the pane
   heading directly below it, so that is what gives way. It is clipped rather
   than display:none because it IS the button's accessible name and app.js
   keeps it set to the current view. */
@media (max-width: 560px) {
  .nav-toggle-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
  }

  .nav-toggle {
    padding: 0.45rem 0.8rem;
    gap: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}

/* --- Hosted setup wizard ------------------------------------------- */
#view-setup.layout {
  grid-template-columns: 1fr;
  overflow: auto;
  justify-items: center;
  padding: 1.5rem 1.25rem 2.5rem;
  background:
    radial-gradient(ellipse 80% 50% at 50% -10%, color-mix(in srgb, var(--accent) 18%, transparent), transparent 60%),
    var(--bg);
}

.setup-wizard {
  width: min(720px, 100%);
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  animation: rise 0.55s var(--ease) both;
}

.setup-wizard-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.setup-eyebrow {
  margin: 0 0 0.35rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-bright);
}

.setup-wizard-head h2 {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 3vw, 2rem);
  font-weight: 800;
  letter-spacing: -0.03em;
}

.setup-lead {
  margin: 0.4rem 0 0;
  color: var(--muted);
  max-width: 38ch;
  line-height: 1.45;
}

.setup-stepper {
  list-style: none;
  margin: 0;
  padding: 0.35rem;
  display: flex;
  gap: 0.35rem;
  overflow-x: auto;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  background: color-mix(in srgb, var(--bg-elevated) 80%, transparent);
}

.setup-stepper li {
  flex: 1 1 0;
  min-width: 4.5rem;
}

.setup-stepper button {
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.55rem 0.4rem;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}

.setup-stepper button[aria-current="step"] {
  background: color-mix(in srgb, var(--accent) 22%, var(--bg-elevated));
  color: var(--bright);
}

.setup-stepper button.is-done {
  color: var(--bright);
}

.setup-stepper button.is-done::before {
  content: "✓ ";
  color: var(--accent-bright);
}

.setup-error {
  margin: 0;
}

.setup-card {
  border: 1px solid var(--line-soft);
  border-radius: calc(var(--radius) + 4px);
  background: color-mix(in srgb, var(--bg-elevated) 92%, transparent);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.22);
  padding: 1.35rem 1.35rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

.setup-card-head h3 {
  margin: 0.15rem 0 0.35rem;
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.setup-step-kicker {
  margin: 0;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.setup-callout {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 1rem 1.05rem;
  background: color-mix(in srgb, var(--bg) 55%, var(--bg-elevated));
}

.setup-callout strong {
  display: block;
  margin-bottom: 0.25rem;
}

.setup-callout p {
  margin: 0;
  color: var(--muted);
  line-height: 1.45;
}

.setup-callout-success {
  border-color: color-mix(in srgb, var(--accent-bright) 45%, var(--line));
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-elevated));
}

.setup-path {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  word-break: break-all;
}

.setup-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
}

.setup-terminal-wrap {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--log-bg);
  overflow: hidden;
}

.setup-terminal-output {
  margin: 0;
  min-height: 200px;
  max-height: 320px;
  overflow: auto;
  padding: 0.9rem 1rem;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--log-ink);
}

.setup-terminal-input-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.5rem;
  margin-top: 0.55rem;
}

.setup-terminal-input-row input {
  width: 100%;
}

.setup-starter-skills {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  max-height: 280px;
  overflow: auto;
  padding-right: 0.25rem;
}

.setup-skill-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.35rem 0.65rem;
  align-items: start;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg) 40%, transparent);
}

.setup-skill-row .muted {
  grid-column: 2;
  font-size: var(--fs-small);
}

.setup-card-foot {
  display: flex;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.55rem;
  padding-top: 0.35rem;
  border-top: 1px solid var(--line-soft);
}

@media (max-width: 640px) {
  #view-setup.layout {
    padding: 1rem 0.75rem 2rem;
  }

  .setup-wizard-head {
    flex-direction: column;
  }

  .setup-stepper {
    border-radius: var(--radius);
  }

  .setup-card {
    padding: 1.05rem;
  }
}

/* -- Developer settings: private skill repositories (AtlasOS#285) -------- */

/* The logo is the way into the hidden menu, so it has to feel clickable
   without advertising itself. No hover affordance, no cursor change — only
   the tap highlight is suppressed, because seven fast taps on a phone would
   otherwise flash a grey box seven times. */
#brand-logo {
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}

.dev-repo-block {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* The card holds more than one block now, and without this the next block's
   heading sits directly on the previous block's last hint. */
.dev-repo-block + .dev-repo-block {
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line-soft);
}

.dev-repo-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.dev-repo-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 0.65rem 0.85rem;
  background: color-mix(in srgb, var(--bg) 55%, var(--bg-elevated));
  min-width: 0;
}

.dev-repo-copy {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.dev-repo-copy strong {
  font-weight: 600;
}

.dev-repo-copy .hint {
  margin: 0;
  overflow-wrap: anywhere;
}

.dev-repo-warn {
  font-size: var(--fs-small);
  color: var(--warn, var(--muted));
}

.dev-repo-ok {
  margin: 0;
}

@media (max-width: 640px) {
  .dev-repo-row {
    flex-direction: column;
    align-items: stretch;
  }
}

/* -- Integration credentials (AtlasOS#293) ------------------------------- */

.secret-list {
  list-style: none;
  margin: 0 0 0.6rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.secret-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 0.6rem 0.85rem;
  background: color-mix(in srgb, var(--bg) 55%, var(--bg-elevated));
  min-width: 0;
}

.secret-copy {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.secret-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.45rem;
  min-width: 0;
}

.secret-name {
  font-family: var(--font-mono, monospace);
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* Scope badge -- company vs. the seat's own. Same idiom as the skill layer
   badge: which value a run will actually see is a category, not a footnote,
   and it was previously grey text wedged between the timestamp and a
   sentence, where two rows of the same name read as duplicates. */
.secret-scope-badge {
  display: inline-block;
  flex-shrink: 0;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  color: var(--muted);
}

.secret-scope-badge.scope-company {
  color: var(--signal-text);
  border-color: color-mix(in srgb, var(--signal) 45%, transparent);
  background: color-mix(in srgb, var(--signal) 10%, transparent);
}

.secret-scope-badge.scope-personal {
  color: var(--accent-on-tint);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* Shadowed: still listed, still removable, but not the value in force --
   so it must not carry the same weight as the row that wins. */
.secret-scope-badge.is-shadowed {
  color: var(--muted);
  border-color: var(--line-soft);
  background: none;
  text-decoration: line-through;
}

.secret-copy .hint {
  margin: 0;
}

.secret-actions {
  display: flex;
  gap: 0.4rem;
  flex-shrink: 0;
}

.secrets-writeonly {
  margin: 0;
}

@media (max-width: 640px) {
  .secret-row {
    flex-direction: column;
    align-items: stretch;
  }
  .secret-actions {
    justify-content: flex-end;
  }
}


/* -- People / seats -------------------------------------------------------
   The seat list used to be bare <li>s inside .secret-list: the name, the
   email in brackets, the role and the status all ran together on one text
   baseline, and Suspend landed wherever that sentence happened to end -- so
   no two rows lined up and the button moved as you read down the list.
   Every sibling list in Settings (secrets, connectors, ownCloud) is a
   .secret-row card, so a seat is one too, on a fixed grid: identity left,
   state right, action last and always in the same column.

   The state is a pill rather than grey run-on text on purpose. "suspended"
   is the one word in this card somebody must not skim past -- it used to be
   set in the same muted grey as "active", two characters apart. */

.seat-head-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
}

.seat-head-title h3 {
  margin: 0;
}

.seat-count {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
  padding: 0.08rem 0.45rem;
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  white-space: nowrap;
}

.seat-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.5rem 0.85rem;
}

.seat-avatar {
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--accent-on-tint);
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  user-select: none;
}

.seat-copy {
  gap: 0.1rem;
}

.seat-name {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.seat-email {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  overflow-wrap: anywhere;
}

/* Marks your own row -- the one row whose Suspend button is deliberately
   absent, which otherwise reads as a rendering glitch. */
.seat-you {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.08rem 0.35rem;
  border-radius: 999px;
  color: var(--accent-on-tint);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.seat-tags {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.seat-role {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.12rem 0.45rem;
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  color: var(--muted);
  white-space: nowrap;
}

.seat-role.is-admin {
  color: var(--accent-text);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}

.status-pill.seat-active { color: var(--ok); }
.status-pill.seat-invited { color: var(--ask); }
.status-pill.seat-suspended { color: var(--fail); }

.seat-row.is-invited {
  border-style: dashed;
}

.seat-row.is-suspended {
  border-color: color-mix(in srgb, var(--fail) 25%, var(--line-soft));
  background: color-mix(in srgb, var(--fail) 5%, var(--bg-elevated));
}

.seat-row.is-suspended .seat-avatar,
.seat-row.is-suspended .seat-copy {
  opacity: 0.6;
}

/* The invite links are shown exactly once, so the field is wide, selectable
   and has its own Copy -- an admin reading a URL off the screen to retype it
   is how an invite gets lost. */
.invite-link-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.5rem 0.75rem;
}

.invite-link-row .seat-email {
  grid-column: 1 / -1;
  font-size: 0.72rem;
}

.invite-link-row input {
  width: 100%;
  font-family: var(--font-mono);
  font-size: var(--fs-control);
  color: var(--muted);
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  padding: 0.35rem 0.5rem;
}

@media (max-width: 640px) {
  .seat-row {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    row-gap: 0.55rem;
  }
  .seat-tags,
  .seat-row .secret-actions {
    grid-column: 2;
    justify-content: flex-start;
  }
}


/* --- Memory (AtlasOS#306) ------------------------------------------------
   The list is name + description; the detail pane is an editable form,
   because on a hosted seat this is the only way to correct a fact the agent
   wrote -- there is no shell to edit the file with. */
.memory-pane > .hint {
  margin: 0 0.95rem 0.6rem;
}

.memory-type-badge {
  display: inline-block;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.1rem 0.42rem;
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  color: var(--text-muted);
}

.memory-problems {
  margin: 0 1.1rem 0.6rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  font-size: var(--fs-small);
}

.memory-problems ul {
  margin: 0.35rem 0 0;
  padding-left: 1.1rem;
}

.memory-form textarea {
  width: 100%;
  min-height: 14rem;
  resize: vertical;
  font-family: var(--font-mono);
  font-size: var(--fs-control);
  line-height: 1.5;
}

.memory-form label {
  display: block;
  margin-bottom: 0.75rem;
}

.memory-form label > span {
  display: block;
  font-size: var(--fs-small);
  color: var(--text-muted);
  margin-bottom: 0.25rem;
}

.memory-form input[type="text"],
.memory-form select {
  width: 100%;
  box-sizing: border-box;
}

.memory-form input[readonly] {
  opacity: 0.75;
  cursor: default;
}

.memory-form-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
}

/* Artifacts is a full-width library, not a master/detail pane, so its chrome
   lines up with .pane-head's own padding rather than the two-pane inset. */
.artifacts-pane > .hint,
.artifacts-pane > .skills-count,
.artifacts-pane > .empty,
.artifacts-pane > .error {
  padding: 0 1.15rem;
  margin: 0 0 0.5rem;
}

.artifacts-pane > .skills-tools {
  padding: 0 1.15rem 0.6rem;
}

/* Artifacts — the library of files runs produced (AtlasOS#311). One row per
   file: what it is on the left, where it came from underneath, size and
   download on the right. */
/* Every link in the chain needs min-width: 0, or the nowrap meta line sets a
   min-content width and the whole pane grows past a phone screen instead of
   ellipsing. */
.artifacts-pane,
.artifact-list,
.artifact-list > li {
  min-width: 0;
}

.artifact-row {
  min-width: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 0.65rem 0.8rem;
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
  color: inherit;
  text-decoration: none;
}

.artifact-row:hover {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

.artifact-kind {
  flex-shrink: 0;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.3rem 0.45rem;
  min-width: 3.1rem;
  text-align: center;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent-on-tint);
}

.artifact-main {
  min-width: 0;
  flex: 1 1 auto;
}

.artifact-name {
  display: block;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.artifact-meta {
  display: block;
  font-size: 0.76rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.artifact-size {
  flex-shrink: 0;
  font-size: 0.78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* A produced file linked from an agent's own message. Reads as a download,
   not as a plain link, so it is obvious it leaves the container. */
.chat-body a.file-link,
.result-content a.file-link {
  font-weight: 600;
}

.chat-body a.file-link::before,
.result-content a.file-link::before {
  content: "↓ ";
}

/* =====================================================================
   Beginner-comfort pass
   ---------------------------------------------------------------------
   Measured on the Settings view before this block: 30 interactive controls
   under 36px tall, including 17px checkboxes. WCAG 2.2 puts the floor at
   24x24 and the comfortable touch target at 44x44; hint text -- which is
   where every explanation in this UI lives -- was set at 0.8rem, the
   smallest type on the page.

   Deliberately additive and at the end of the file: it overrides the
   component rules above without editing them, so the diff stays readable
   and reverting the whole idea is one delete.
   ===================================================================== */

/* Controls a finger can hit. Height only -- padding stays with the
   component so nothing reflows sideways. */
.btn,
.view-tab,
select,
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="file"]) {
  min-height: 2.5rem;
}

.btn.small {
  min-height: 2.25rem;
  font-size: var(--fs-small);
}

.btn.icon {
  min-width: 2.5rem;
}

/* Coarse pointers get the full 44px. A phone is the device a
   non-technical user is most likely to be holding. */
@media (pointer: coarse) {
  .btn,
  .view-tab,
  select,
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="file"]) {
    min-height: 2.75rem;
  }
  .btn.small { min-height: 2.5rem; }
  .btn.icon { min-width: 2.75rem; }
}

/* 17px was below the WCAG floor and genuinely fiddly with a trackpad. */
dialog label.checkbox-row input[type="checkbox"],
.settings-toggle input[type="checkbox"] {
  width: 1.25rem;
  height: 1.25rem;
}

/* Every explanation in this product is in a .hint. Setting them in the
   smallest type on the page told the reader they were skippable -- exactly
   backwards for someone who does not already know what the control does. */
.hint {
  font-size: var(--fs-small);
  line-height: 1.5;
}

/* Empty states are a beginner's most common first screen, and they are the
   one place the product gets to say "here is what this is for" without
   anyone having to ask. Give them room to read as an invitation rather
   than as an error message. */
.empty,
.empty-state,
.runs-empty {
  line-height: 1.55;
}

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

/* "More options" inside the New task dialog. Model, thoroughness, website
   access and the project folder are all things Atlas picks sensibly on its
   own; presenting them above the box you actually type into made the dialog
   read as a configuration form rather than as a question. */
.run-more-options {
  border-top: 1px solid var(--line-soft);
  padding-top: 0.6rem;
}

.run-more-options > summary {
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--muted);
  padding: 0.4rem 0;
  list-style: none;
  min-height: 2.25rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.run-more-options > summary::-webkit-details-marker { display: none; }

.run-more-options > summary::before {
  content: "";
  width: 0.38rem;
  height: 0.38rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.18s var(--ease);
}

.run-more-options[open] > summary::before { transform: rotate(45deg); }

.run-more-options > summary:hover { color: var(--ink); }

.run-more-options > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.run-more-options-body {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding-top: 0.5rem;
}

@media (prefers-reduced-motion: reduce) {
  .run-more-options > summary::before { transition: none; }
}

/* Files → Input (inputs.py): the upload block above the library list, the
   rows themselves, and the "Use Input files" picker under a composer. */
.inputs-upload {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0 0.85rem 0.6rem;
  flex-shrink: 0;
}

.inputs-upload-row {
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.inputs-scope {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  flex: 1;
  min-width: 12rem;
  font-size: var(--fs-small);
  color: var(--muted);
}

/* A library row is a div, not a link: the name downloads, the actions are
   buttons, and the whole row must not be one big download target. */
.input-row {
  cursor: default;
}

.input-row .artifact-name a {
  color: inherit;
  text-decoration: none;
}

.input-row .artifact-name a:hover {
  text-decoration: underline;
}

.input-row .artifact-title {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-width: 0;
}

.input-row .artifact-actions {
  display: flex;
  gap: 0.3rem;
  flex-shrink: 0;
}

.inputs-pick {
  margin-top: 0.5rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 0.45rem 0.7rem;
  background: color-mix(in srgb, var(--bg-elevated) 60%, transparent);
}

.inputs-pick > summary {
  cursor: pointer;
  font-size: var(--fs-small);
  font-weight: 600;
}

.inputs-pick-count {
  color: var(--muted);
  font-weight: 500;
  margin-left: 0.4rem;
}

.inputs-pick .hint {
  margin: 0.3rem 0 0;
}

.inputs-pick-list {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  max-height: 12rem;
  overflow: auto;
}

/* The app's form labels stack their contents; a pick row is one line. */
.inputs-pick-list label {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  margin: 0;
  font-size: var(--fs-small);
  font-weight: 500;
  cursor: pointer;
}

.inputs-pick-list input[type="checkbox"] {
  flex: 0 0 auto;
  width: auto;
  margin: 0;
}

.inputs-pick-list label span.name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.inputs-pick-list .secret-scope-badge {
  margin-left: auto;
}

.inputs-pick.is-disabled {
  opacity: 0.6;
  pointer-events: none;
}

/* ---- Overview: setup that is not finished -------------------------------
   Sits above both the stats and the empty state, because on a seat with no
   agent login the empty state's "give Atlas its first task" is an invitation
   to a task that cannot run. Two tones: `blocking` when nothing can run
   (warm --ask, the colour this UI already uses for "waiting on you"), plain
   surface when Atlas works and some optional steps were never done. */
.dash-setup {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.9rem 1.25rem;
  margin: 0 0 1.15rem;
  padding: 0.9rem 1.05rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
}

.dash-setup.blocking {
  border-color: color-mix(in srgb, var(--ask) 38%, var(--line));
  background: color-mix(in srgb, var(--ask) 9%, var(--bg-elevated));
}

.dash-setup-text {
  min-width: min(100%, 18rem);
  flex: 1 1 22rem;
}

.dash-setup h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.dash-setup .dash-setup-body {
  margin: 0.3rem 0 0;
  color: var(--muted);
}

/* The remaining steps, as a plain inline run of names -- a bulleted column
   would out-weigh the sentence that says why they matter. */
.dash-setup-steps {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.55rem;
  margin: 0.5rem 0 0;
  padding: 0;
  list-style: none;
}

.dash-setup-steps:empty {
  display: none;
}

.dash-setup-steps li {
  padding: 0.1rem 0.5rem;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  font-size: var(--fs-small);
  color: var(--muted);
}

.dash-setup .btn {
  flex: 0 0 auto;
}

/* ---- Overview, before there is anything to show -------------------------
   The dashboard's first screen used to be one grey sentence in the top-left
   corner of an otherwise black page, which reads as "broken" rather than as
   "new". The illustration is the same dashboard drawn empty: ghost stat
   tiles, a dashed baseline, bars that have not been earned yet, and the
   Atlas mark riding the trend line up out of them. It animates once on
   arrival and then keeps only a slow ambient loop, so the page feels alive
   without asking for attention it does not deserve. */
.dash-empty {
  display: grid;
  justify-items: center;
  gap: 0.8rem;
  text-align: center;
  color: var(--muted);
  margin: 0;
  padding: clamp(1.25rem, 5vh, 3rem) 1.15rem 2.5rem;
}

.dash-empty-art {
  width: min(100%, 22rem);
  color: var(--ink);
  margin-bottom: 0.35rem;
}

.dash-empty-art svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.dash-empty .empty-title,
.dash-empty .empty-sub {
  margin: 0;
}

.dash-empty .btn {
  margin-top: 0.4rem;
}

/* Entry: tiles settle, bars grow off the baseline left to right, the trend
   is drawn last, and the mark arrives on top of it. --i staggers each one;
   an nth-child rule would break the moment a shape is added. */
.dash-empty-art .pill {
  animation: dash-empty-fade 0.5s var(--ease) both;
  animation-delay: calc(var(--i, 0) * 80ms);
}

.dash-empty-art .bar {
  transform-box: fill-box;
  transform-origin: bottom;
  animation: dash-empty-grow 0.62s var(--ease) both;
  animation-delay: calc(240ms + var(--i, 0) * 70ms);
}

.dash-empty-art .glow {
  animation: dash-empty-fade 0.9s var(--ease) 0.85s both,
             dash-empty-breathe 5.5s ease-in-out 1.7s infinite;
}

/* The dashes travel along the path forever -- a chart still filling in. */
.dash-empty-art .trend {
  animation: dash-empty-fade 0.7s var(--ease) 0.8s both,
             dash-empty-flow 2.6s linear 0.8s infinite;
}

.dash-empty-art .mark {
  transform-box: fill-box;
  transform-origin: center;
  animation: dash-empty-pop 0.55s var(--ease) 1.05s both,
             dash-empty-float 4.5s ease-in-out 1.7s infinite;
}

@keyframes dash-empty-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes dash-empty-grow {
  from { transform: scaleY(0.04); opacity: 0; }
  to { transform: scaleY(1); opacity: 1; }
}

@keyframes dash-empty-pop {
  from { transform: translateY(6px) scale(0.6); opacity: 0; }
  to { transform: translateY(0) scale(1); opacity: 1; }
}

@keyframes dash-empty-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

@keyframes dash-empty-breathe {
  0%, 100% { opacity: 0.75; }
  50% { opacity: 1; }
}

@keyframes dash-empty-flow {
  from { stroke-dashoffset: 0; }
  to { stroke-dashoffset: -16; }
}

@media (prefers-reduced-motion: reduce) {
  .dash-empty-art .pill,
  .dash-empty-art .bar,
  .dash-empty-art .glow,
  .dash-empty-art .trend,
  .dash-empty-art .mark {
    animation: none;
  }
}

/* On-prem proxy pairing commands (ClaudeOS#677): long, copied whole. */
.onprem-cmd code {
  word-break: break-all;
  user-select: all;
}

/* On-prem proxy: the command-line path is the exception, so it folds away (ClaudeOS#720). */
.onprem-advanced {
  margin: 0.75rem 0;
}
.onprem-advanced summary {
  cursor: pointer;
}

/* Reports a seat has filed (AtlasOS#892). Read-only by design: there is no
   link because the tracker is private, and no comment thread because triage
   is internal. A row is therefore a state, a title and a number. */
.reports-list {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.reports-row {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--line);
  border-radius: 8px;
}
.reports-state {
  flex: 0 0 auto;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  border: 1px solid currentColor;
}
.reports-state.is-open { color: var(--accent-text); }
.reports-state.is-done { color: var(--muted); }
.reports-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}
.reports-num {
  flex: 0 0 auto;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* === overhaul:foundation === */
/* Shared components and accessibility rules of the Atlas Web overhaul
   (SPEC 4). Owned by the foundation; phase-1 areas build on them. */

/* Focus: one ring, everywhere, in the accent (WCAG 2.4.7). No rule in this
   file removes it without putting one back -- tests/test_web_css_lint.py
   pairs every `outline: none` with its replacement. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* On the dark chrome of full-screen embeds and the picture lightbox, the
   light theme's accent drops under 3:1; the bright shade holds 5:1 there. */
.embed-card :focus-visible,
.image-lightbox :focus-visible {
  outline-color: var(--accent-bright);
}

/* The file input inside a drop zone is visually hidden, so the zone shows
   the ring for it. */
.file-drop-zone:has(:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Every drop zone in the markup has its input BESIDE the label, not inside
   it (index.html: the reply box, the inputs, a new task, an issue run), so
   the rule above never matched and Tab reached an input with no ring at
   all, in both looks. The zone after the focused input shows it. */
.file-drop > input:focus-visible + .file-drop-zone {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Placeholders were Chrome's own #757575: 3.5:1 on the dark fields, under
   the 4.5:1 a hint needs. The muted ink holds it on every field in both
   themes; opacity 1 because Firefox fades placeholders on its own. */
input::placeholder,
textarea::placeholder {
  color: var(--muted);
  opacity: 1;
}

/* Form controls are never set under 1rem (16px at the default size): iOS
   zooms into anything smaller and stays zoomed, and it is the size a reader
   with the browser default can actually read. Many controls inherit a
   label's 0.875rem through `font: inherit`; this closes that path too.
   :is() gives every entry the input selector's weight, so a container rule
   such as `.empty-run-field select` cannot undercut it.

   The same weight means a component rule cannot make a control LARGER by
   setting font-size either. To do that, set the size on the control's
   parent (1em follows it) or set `--control-size: 1.15rem` on the control
   itself; both only ever raise it above the floor. */
:is(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="hidden"]), select, textarea) {
  font-size: max(var(--fs-control), var(--control-size, 1em));
}

/* Status pill (StatusPill). Full view: the brand's mono uppercase label.
   Simple view: the same dot and colour with a plain word in sentence case,
   at a size a first-time reader does not squint at. The colour never
   stands alone -- the word is always there. */
html:not([data-mode="full"]) .status-pill {
  font-family: var(--font-body);
  font-size: var(--fs-status-simple);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}

html:not([data-mode="full"]) .status-pill::first-letter {
  text-transform: uppercase;
}

/* List rows: one comfortable target per row (SPEC 4). .list-row is the
   shared shape for the lists phase 1 rebuilds. */
.run-list li button,
.skill-select,
.issue-select,
.project-select,
.artifact-row {
  min-height: var(--row-min);
}

/* Inside a scroller the ring would be clipped at the scroller's edge (the
   tab strip even hides its vertical overflow), so these draw it inset. */
.tab:focus-visible,
.run-list li button:focus-visible,
.skill-select:focus-visible,
.issue-select:focus-visible,
.project-select:focus-visible {
  outline-offset: -2px;
}

.tab {
  min-height: var(--target-min);
}

.list-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: var(--row-min);
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
}

/* Cards: the one surface every panel shares. */
.card {
  padding: 1rem 1.05rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-app-soft);
}

/* Empty states read as an invitation, at body size. */
.empty {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

.empty-state {
  display: grid;
  justify-items: center;
  gap: 0.6rem;
  padding: 2rem 1.25rem;
  text-align: center;
  color: var(--muted);
}

.empty-state-title {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-section-title);
  font-weight: 700;
}

/* Group labels (sidebar groups, settings groups): the brand's small caps. */
.group-label {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-group-label);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

/* Filter chips: single- or multi-select buttons in a `role="group"`, the
   state in aria-pressed (Tasks filters, Knowledge types, weekday picks).
   The selected chip is ink on ink, like the other selected controls here;
   never accent text on an accent tint. */
.chip-group {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: var(--target-min);
  padding: 0.35rem 0.9rem;
  border: 1px solid var(--control-line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-small);
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.chip:hover {
  border-color: var(--ink);
  background: var(--bright);
}

.chip[aria-pressed="true"] {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--bg);
}

.chip-count {
  font-variant-numeric: tabular-nums;
  opacity: 0.8;
}

/* On/Off switch: a <button role="switch" aria-checked> named by the thing
   it switches. The whole button is the 44px target; the track inside is
   the picture. */
.switch {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  min-height: var(--target-min);
  padding: 0 0.15rem;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.switch-track {
  position: relative;
  flex-shrink: 0;
  width: 2.75rem;
  height: 1.5rem;
  border: 1px solid var(--control-line);
  border-radius: 999px;
  background: var(--surface);
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.switch-track::after {
  content: "";
  position: absolute;
  top: 0.1875rem;
  left: 0.1875rem;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  background: var(--control-line);
  transition: transform var(--dur) var(--ease), background var(--dur-fast) var(--ease);
}

.switch[aria-checked="true"] .switch-track {
  border-color: var(--accent);
  background: var(--accent);
}

.switch[aria-checked="true"] .switch-track::after {
  background: var(--accent-ink);
  transform: translateX(1.25rem);
}

.switch:disabled {
  cursor: default;
  opacity: 0.55;
}

.switch-label {
  font-size: var(--fs-small);
  font-weight: 600;
}

/* Segmented choice: native radios in a <fieldset>, drawn as one strip
   (Ansicht, Darstellung, Textgrösse). The radio stays in the tab order and
   in the accessibility tree; it is only transparent, and the segment shows
   its focus. */
.segmented {
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

.segmented legend {
  margin-bottom: 0.4rem;
  padding: 0;
  font-size: var(--fs-small);
  font-weight: 600;
}

.segmented-options {
  display: flex;
  flex: 1 1 100%;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}

.segmented-options label {
  position: relative;
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--target-min);
  padding: 0.35rem 0.6rem;
  font-size: var(--fs-small);
  font-weight: 600;
  text-align: center;
  cursor: pointer;
}

.segmented-options label + label {
  border-left: 1px solid var(--control-line);
}

.segmented-options input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.segmented-options label:has(input:checked) {
  background: var(--ink);
  color: var(--bg);
}

.segmented-options label:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: -4px;
}

/* The text-size strip (.segmented-wrap) names the sizes it sets, so its
   words grow with them: at «Maximal» on a phone they no longer fitted one
   row and overflow: hidden cut the last one («Maxin»), in both looks.
   Tighter sides keep it on one row down to about 375px; narrower, it wraps
   instead of cutting. */
.segmented-wrap .segmented-options {
  flex-wrap: wrap;
}

.segmented-wrap .segmented-options label {
  padding-inline: 0.3rem;
}

/* Scrollers with a sticky bar keep the focused element out from under it
   (WCAG 2.4.11): the task header pins at the top of .detail-pane, and the
   reply region joins the bottom in phase 1. */
.detail-pane {
  scroll-padding-top: 6rem;
  scroll-padding-bottom: 7rem;
}

.commit-list {
  scroll-padding-top: 2.5rem;
}

/* Dialogs on a phone: the actions stay in reach at the bottom while the
   form scrolls -- except on a short landscape screen, where a pinned bar
   would take half of what is left. */
@media (max-width: 820px) and (min-height: 501px) {
  dialog form > .dialog-actions:last-child {
    position: sticky;
    bottom: 0;
    z-index: 1;
    margin: 0.2rem -1.45rem -1.25rem;
    padding: 0.75rem 1.45rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
    background: var(--bg-elevated);
    border-top: 1px solid var(--line);
  }

  /* The dialog is the scroller; a field tabbed into stays above the bar. */
  dialog {
    scroll-padding-bottom: 5.5rem;
  }
}

/* Motion is transform and opacity on the house curve, and none of it runs
   for a reader who asked for less (the global rule at the 560px block
   already stops animations and transitions; this also stops smooth
   scrolling). */
@media (prefers-reduced-motion: reduce) {
  html:focus-within { scroll-behavior: auto; }
  * { scroll-behavior: auto !important; }
}

/* --- The app shell (SPEC 5) -----------------------------------------------
   Above 820px the header is a fixed sidebar, 248px wide, and an 88px rail
   between 821 and 1180px; at 820px and below it is a 56px top bar, and
   #tabbar sits at the bottom of the page. Layers: sidebar and tab bar 40,
   FAB 50, call panel 60, full-screen embed 70, toasts 75, restart overlay
   80. The header is a stacking context, so nothing fixed or popover-like
   lives inside it; the account menu, the help sheet and the drawer are
   dialogs, and dialogs sit in the top layer above all of these. */

.icon-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.account-btn {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  width: 100%;
  min-height: 3.25rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.account-btn:hover {
  border-color: var(--control-line);
  background: var(--bright);
}

.account-avatar {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--ink);
  color: var(--bg-elevated);
  font-family: var(--font-display);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1;
}

.account-btn-text {
  display: grid;
  min-width: 0;
  line-height: 1.3;
}

.account-btn-name {
  overflow: hidden;
  font-size: var(--fs-small);
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-btn-role {
  color: var(--muted);
  font-size: var(--fs-small);
}

.account-btn-role:empty {
  display: none;
}

/* The count of tasks waiting for the person. The number is decoration: the
   button's own aria-label says it in words («Aufgaben, 2 warten auf Sie»). */
.nav-badge {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  min-width: 1.4rem;
  height: 1.4rem;
  margin-left: auto;
  padding: 0 0.4rem;
  border-radius: 999px;
  background: var(--ask);
  color: var(--bg-elevated);
  font-size: 0.8125rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

@media (min-width: 821px) {
  :root {
    --shell-inset: var(--sidebar-w);
  }

  body {
    padding-left: var(--shell-inset);
  }

  .top {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: var(--z-shell);
    width: var(--shell-inset);
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    justify-content: flex-start;
    gap: 0.25rem;
    padding: 1.35rem 0.75rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
    border-bottom: 0;
    border-right: 1px solid var(--line);
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .top .brand {
    padding: 0 0.6rem;
  }

  .top .brand-lockup {
    height: 1.4rem;
  }

  .top .tagline {
    font-size: var(--fs-small);
    line-height: 1.35;
  }

  .top .shell-new {
    width: 100%;
    min-height: var(--target-min);
    margin: 0.95rem 0 0.75rem;
  }

  .top .view-nav .view-tab {
    min-height: var(--target-min);
  }

  /* Werkzeuge, Einstellungen and the account sit at the foot. */
  .top .top-actions {
    flex-direction: column;
    align-items: stretch;
    gap: 0.125rem;
    margin-top: auto;
    padding-top: 0.85rem;
  }

  .shell-group-label {
    padding: 0 0.75rem 0.3rem;
  }

  .top .top-actions > .btn {
    justify-content: flex-start;
    gap: 0.75rem;
    width: 100%;
    min-height: var(--target-min);
    padding: 0.5rem 0.75rem;
    border-color: transparent;
    background: transparent;
    box-shadow: none;
    font-size: 1rem;
  }

  .top .top-actions > .btn:hover {
    background: color-mix(in srgb, var(--ink) 6%, transparent);
  }

  .top #btn-map {
    color: var(--muted);
  }

  .top #btn-map:hover {
    color: var(--ink);
  }

  /* A live call keeps its outline: an open microphone is readable at a
     glance, in the sidebar as in the old header. */
  .top .top-actions > .voice-btn.live {
    border-color: color-mix(in srgb, var(--accent-bright) 65%, var(--line));
  }

  /* The dot sits where the icons sit, so every label lines up. */
  .top .voice-btn .voice-dot {
    margin: 0 0.375rem;
  }

  .view-nav-foot {
    margin-top: 0.35rem;
    padding-top: 0.35rem;
    border-top: 1px solid var(--line-soft);
  }

  .top .account-btn {
    margin-top: 0.5rem;
  }

  /* The account menu opens beside the sidebar's foot, like a popover. */
  .account-menu {
    position: fixed;
    inset: auto auto 1rem calc(var(--shell-inset) + 0.75rem);
    margin: 0;
  }

  .account-menu::backdrop {
    background: color-mix(in srgb, var(--dialog-scrim) 40%, transparent);
    backdrop-filter: none;
  }
}

/* The rail: icons with a short label under each, the mark alone. */
@media (min-width: 821px) and (max-width: 1180px) {
  :root {
    --shell-inset: var(--sidebar-rail-w);
  }

  .top {
    gap: 0.15rem;
    padding: 1rem 0.3rem calc(0.6rem + env(safe-area-inset-bottom, 0px));
  }

  /* The mark alone is the same SVG as the wordmark, cropped to its first 60
     of 425 units -- never a second #brand-logo (the knock listens on it). */
  .top .brand {
    width: calc(1.9rem * 60 / 66);
    margin: 0 auto 0.25rem;
    padding: 0;
    overflow: hidden;
  }

  .top .brand-lockup {
    height: 1.9rem;
    max-width: none;
  }

  .top .tagline {
    display: none;
  }

  .top .shell-new,
  .top .view-nav .view-tab,
  .top .top-actions > .btn {
    flex-direction: column;
    justify-content: center;
    gap: 0.2rem;
    min-height: 3.5rem;
    padding: 0.45rem 0.15rem;
    font-size: 0.75rem;
    line-height: 1.15;
    text-align: center;
  }

  .top .shell-new {
    margin: 0.75rem 0;
  }

  .top .view-tab-text {
    flex: 0 0 auto;
    max-width: 100%;
  }

  .top .view-tab-label {
    font-size: 0.75rem;
    hyphens: auto;
  }

  .top .view-tab.active::before {
    top: 0.85rem;
    bottom: 0.85rem;
  }

  .top .label-long,
  .top .shell-group-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  .top .label-short {
    display: block;
  }

  .top .voice-btn .voice-dot {
    margin: 0.375rem 0;
  }

  .top .nav-badge {
    position: absolute;
    top: 0.3rem;
    left: calc(50% + 0.3rem);
    min-width: 1.25rem;
    height: 1.25rem;
    margin: 0;
    padding: 0 0.3rem;
    font-size: 0.75rem;
  }

  .top .account-btn {
    justify-content: center;
    min-height: var(--target-min);
    padding: 0.3rem;
    border-color: transparent;
    background: transparent;
  }

  .top .account-btn:hover {
    background: color-mix(in srgb, var(--ink) 6%, transparent);
  }

  .top .account-btn-text {
    display: none;
  }
}

/* A short sidebar: every row stays on screen instead of scrolling. An
   admin in «Erweitert» has twelve rows, which needed about 800px of window
   (measured: 797 in glass, 800 in Klassisch), more than a 13-inch laptop's
   browser gives, so the account at the foot sat below the edge under a
   scrollbar (#1068). The sidebar is a size container, so the test is its
   own height in rem: the floating glass capsule (24px shorter) and a larger
   text size tighten it sooner, which a media query cannot see. Two steps:
   the tagline and the spacing go first; only if that is not enough does a
   mouse get 38px rows (a touch screen keeps 44px). */
@media (min-width: 821px) {
  .top {
    container: sidebar / size;
    /* What still overflows (the largest text, a very short window) scrolls
       with a thin bar, not the platform's full-width track. */
    scrollbar-width: thin;
    scrollbar-color: color-mix(in srgb, var(--ink) 28%, transparent) transparent;
  }

  /* The mark is shown whole or not at all: as a flex item of a short
     sidebar it shrank first, to a 2px sliver of the logo. */
  .top .brand {
    flex-shrink: 0;
  }
}

@media (min-width: 1181px) {
  @container sidebar (max-height: 48.5rem) {
    .top .tagline {
      display: none;
    }

    .top .shell-new {
      margin: 0.6rem 0 0.4rem;
    }

    .top .top-actions {
      padding-top: 0.5rem;
    }

    .view-nav-foot {
      margin-top: 0.2rem;
      padding-top: 0.2rem;
    }

    .top .account-btn {
      min-height: var(--target-min);
      margin-top: 0.25rem;
      padding-block: 0.3rem;
    }
  }
}

@media (min-width: 1181px) and (pointer: fine) {
  @container sidebar (max-height: 44.5rem) {
    .top .shell-new,
    .top .view-nav .view-tab,
    .top .top-actions > .btn {
      min-height: 38px;
      padding-block: 0.375rem;
    }
  }
}

/* The rail's rows are 56px, label under icon: twelve of them needed 855px
   (1024x768 scrolled by 87px, an iPad in landscape by 35px). 44px still
   holds icon and label, and is the touch minimum, so no pointer test. */
@media (min-width: 821px) and (max-width: 1180px) {
  @container sidebar (max-height: 52rem) {
    .top .shell-new,
    .top .view-nav .view-tab,
    .top .top-actions > .btn {
      min-height: var(--target-min);
      padding-block: 0.25rem;
    }

    .top .shell-new {
      margin: 0.3rem 0;
    }

    .view-nav-foot {
      margin-top: 0.2rem;
      padding-top: 0.2rem;
    }

    .top .account-btn {
      margin-top: 0.25rem;
    }
  }

  /* Shorter still (1024x700): the rail drops its mark. The page heading
     inside .brand stays, it is visually hidden but read. */
  @container sidebar (max-height: 44.5rem) {
    .top .brand {
      margin-bottom: 0;
    }

    .top .brand-lockup {
      display: none;
    }
  }
}

/* The phone tab bar: off above 820px. */
.tabbar {
  display: none;
}

.tabbar-item {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  min-height: var(--tabbar-h);
  padding: 0.4rem 0.2rem;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.15;
  cursor: pointer;
}

.tabbar-item[aria-current="page"] {
  color: var(--ink);
  font-weight: 700;
}

.tabbar-item[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 0;
  left: 28%;
  right: 28%;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: var(--accent);
}

/* Inside the bar's own edge, where an outside ring would be cut off. */
.tabbar-item:focus-visible {
  outline-offset: -3px;
}

.tabbar-icon {
  position: relative;
  display: inline-grid;
  place-items: center;
}

.tabbar-icon .nav-icon {
  width: 1.4rem;
  height: 1.4rem;
}

.tabbar .nav-badge {
  position: absolute;
  top: -0.4rem;
  left: calc(100% - 0.5rem);
  min-width: 1.2rem;
  height: 1.2rem;
  margin: 0;
  padding: 0 0.3rem;
  font-size: 0.75rem;
}

.tabbar-label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 820px) {
  /* In flow, last before the dialogs: the open view shrinks above it, so
     nothing scrolls underneath and every list keeps its end spacer. */
  .tabbar {
    position: relative;
    z-index: var(--z-shell);
    display: flex;
    flex-shrink: 0;
    align-items: stretch;
    padding: 0 max(0.25rem, env(safe-area-inset-right, 0px)) env(safe-area-inset-bottom, 0px) max(0.25rem, env(safe-area-inset-left, 0px));
    border-top: 1px solid var(--line);
    background: var(--bg-elevated);
  }

  /* The top bar's account button: the avatar alone, named «Konto». */
  .top .account-btn {
    width: auto;
    min-width: var(--target-min);
    min-height: var(--target-min);
    flex-shrink: 0;
    justify-content: center;
    margin: 0;
    padding: 0.2rem;
    border-color: transparent;
    background: transparent;
  }

  .top .account-btn-text {
    display: none;
  }

  .top .account-avatar {
    width: 2rem;
    height: 2rem;
    font-size: 0.8125rem;
  }

}

@keyframes sheet-in {
  from {
    opacity: 0;
    transform: translateY(12%);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* The drawer's foot: who is signed in, the text size, the account menu. */
.nav-drawer-account {
  display: grid;
  flex-shrink: 0;
  gap: 0.6rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--line-soft);
}

.nav-drawer-person {
  margin: 0;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.nav-drawer-person:empty {
  display: none;
}

.nav-drawer-field {
  display: grid;
  gap: 0.35rem;
  font-size: var(--fs-small);
  font-weight: 600;
}

.nav-drawer-field select {
  width: 100%;
  min-height: var(--target-min);
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-control);
  font-weight: 400;
}

.nav-drawer-account .btn {
  justify-content: center;
  min-height: 44px;
}

/* --- Account menu ------------------------------------------------------- */
.account-menu {
  width: min(24rem, calc(100vw - 2rem));
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.account-menu-inner {
  display: grid;
  gap: 1rem;
  padding: 1.1rem 1.15rem 1.15rem;
}

.account-menu-head {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
}

.account-avatar-lg {
  width: 2.75rem;
  height: 2.75rem;
  font-size: 1rem;
}

.account-menu-who {
  display: grid;
  flex: 1 1 auto;
  gap: 0.1rem;
  min-width: 0;
  padding-top: 0.15rem;
}

.account-menu-name {
  margin: 0;
  font-size: var(--fs-pane-title);
  letter-spacing: 0;
  overflow-wrap: anywhere;
}

.account-menu-email,
.account-menu-role {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-small);
  overflow-wrap: anywhere;
}

.account-menu-email:empty,
.account-menu-role:empty {
  display: none;
}

.account-menu-note {
  margin: 0;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bright);
  font-size: var(--fs-small);
}

.account-menu-prefs {
  display: grid;
  gap: 0.9rem;
}

.segmented > .hint {
  flex: 1 1 100%;
  margin-top: 0.4rem;
}

.account-field select {
  width: 100%;
}

.account-status {
  margin: -0.4rem 0 0;
  color: var(--muted);
  font-size: var(--fs-small);
}

.account-status:empty {
  margin: -1rem 0 0;
}

.account-status.error {
  color: var(--danger);
  font-weight: 600;
}

.account-menu-links,
.account-menu-foot {
  display: grid;
  gap: 0.125rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--line-soft);
}

.account-menu-foot:not(:has(.btn:not(.hidden))) {
  display: none;
}

.account-link {
  display: flex;
  align-items: center;
  min-height: var(--target-min);
  padding: 0.5rem 0.65rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.account-link:hover {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}

.account-menu-foot .btn {
  justify-content: flex-start;
  min-height: var(--target-min);
  padding-inline: 0.65rem;
  border-color: transparent;
}

.account-menu-foot .hint {
  padding: 0 0.65rem 0.2rem;
}

/* --- Help sheet --------------------------------------------------------- */
.help-sheet {
  max-width: 40rem;
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.help-sheet-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem 0.75rem;
  border-bottom: 1px solid var(--line-soft);
  background: var(--bg-elevated);
}

.help-sheet-head h2 {
  margin: 0;
  letter-spacing: 0;
}

.help-sheet-body {
  display: grid;
  gap: 1.15rem;
  padding: 1.1rem 1.25rem 0.75rem;
}

.help-section h3 {
  margin: 0 0 0.3rem;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
}

.help-section p,
.help-list {
  margin: 0;
  line-height: var(--lh-body);
}

.help-list {
  display: grid;
  gap: 0.3rem;
  padding-left: 1.2rem;
}

.help-sheet-actions {
  position: sticky;
  bottom: 0;
  margin: 0;
  padding: 0.75rem 1.25rem calc(0.9rem + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--line-soft);
  background: var(--bg-elevated);
}

/* --- Problem melden ----------------------------------------------------- */
.report-dialog {
  max-width: 30rem;
}

.report-fields {
  display: grid;
  gap: 0.9rem;
}

.report-dialog.sent .report-fields {
  display: none;
}

.report-status {
  margin: 0;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bright);
}

/* On a phone the account menu and the help sheet are bottom sheets. Last,
   so they win over the base rules above at equal weight. */
@media (max-width: 820px) {
  .account-menu,
  .help-sheet {
    inset: auto 0 0 0;
    width: 100%;
    max-width: none;
    max-height: min(90dvh, calc(100dvh - 1rem));
    margin: 0;
    border: 0;
    border-top: 1px solid var(--line);
    border-radius: 16px 16px 0 0;
  }

  .account-menu[open],
  .help-sheet[open] {
    animation: sheet-in 0.28s var(--ease) both;
  }

  .account-menu-inner {
    padding-bottom: calc(1.15rem + env(safe-area-inset-bottom, 0px));
  }
}

/* --- Skip link and the wrapper of every view (SPEC 5) ----------------------
   The skip link is the first thing Tab reaches. It stays out of sight until
   it has focus and then sits above everything, the sidebar included. */
.skip-link {
  position: fixed;
  top: 0.5rem;
  left: 0.5rem;
  z-index: calc(var(--z-overlay) + 1);
  display: inline-flex;
  align-items: center;
  min-height: var(--target-min);
  padding: 0.6rem 1rem;
  border: 2px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  box-shadow: var(--shadow-dialog);
  transform: translateY(calc(-100% - 1rem));
}

.skip-link:focus,
.skip-link:focus-visible {
  transform: none;
}

/* The flex item that fills the page below the header; each view fills it
   the way it used to fill <body>. */
.app-content {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
}

/* Reached by the skip link: the ring sits inside the edge, where nothing
   clips it. */
.app-content:focus-visible {
  outline-offset: -2px;
}

/* --- Toasts (SPEC 5) ---------------------------------------------------------
   #toast-region is a manual popover: in the top layer while it holds a
   toast, hidden otherwise. Without the Popover API it is a fixed box at the
   toast layer, and a toast raised while a modal dialog is open becomes a
   line inside that dialog instead. */
.toast-region {
  position: fixed;
  inset: auto 1rem 1rem auto;
  z-index: var(--z-toast);
  display: none;
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
  width: min(26rem, calc(100vw - 2rem));
  max-height: calc(100dvh - 2rem);
  margin: 0;
  padding: 0;
  overflow: visible;
  border: 0;
  background: transparent;
  color: var(--ink);
  pointer-events: none;
}

.toast-region.has-toasts {
  display: flex;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.75rem 0.5rem 0.75rem 1rem;
  border: 1px solid var(--line);
  border-left: 4px solid var(--ok);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  color: var(--ink);
  box-shadow: var(--shadow-dialog);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  pointer-events: auto;
  animation: toast-in var(--dur) var(--ease) both;
}

.toast-info {
  border-left-color: var(--run);
}

.toast-error {
  border-left-color: var(--danger);
}

.toast-body {
  flex: 1 1 auto;
  min-width: 0;
  padding-right: 0.5rem;
}

.toast-message,
.toast-detail {
  margin: 0;
  overflow-wrap: anywhere;
}

.toast-detail {
  margin-top: 0.25rem;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--fs-small);
}

.toast-close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--target-min);
  height: var(--target-min);
  margin: -0.5rem 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.toast-close:hover {
  background: var(--accent-tint);
  color: var(--ink);
}

/* Inside an open dialog (no Popover API): a plain line above the buttons. */
.toast-inline-host {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.toast-inline-host .toast {
  box-shadow: none;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(0.5rem);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .toast {
    animation: none;
  }
}

/* On a phone the toasts sit above the tab bar, the width of the screen. */
@media (max-width: 820px) {
  .toast-region {
    inset: auto 0.75rem calc(var(--tabbar-h) + 0.75rem + env(safe-area-inset-bottom, 0px)) 0.75rem;
    width: auto;
  }
}

/* The confirm dialog's one field (the replacement for prompt()). A field
   offered for copying reads like the text it holds. */
.confirm-field input[readonly] {
  font-family: var(--font-mono);
  background: var(--bright);
}

/* =========================================================================
   Atlas Web overhaul 2026-09-28: area sections.
   Everything below is owned area by area in phase 1. Each area writes only
   between its own banner and the next one, and puts its phone rules in its
   own @media (max-width: 820px) block inside its section. Shared tokens,
   components and utility classes live above (see the top of this file and
   the overhaul:foundation section); an area overrides them here, never by
   editing them.
   ========================================================================= */

/* === overhaul:home === */
/* The Overview (SPEC 6.1). One column of the person's next actions -- the
   composer and its examples, what waits for them, what happened lately, the
   time saved -- and the old statistics folded into «Aktivität» below. The
   column is narrower than the old dashboard: a sentence typed into a 1100px
   box reads like a spreadsheet cell. */

/* The shared `1fr` track grows to the widest child's min-content; at 390px
   the setup card's flex basis made the whole column 509px wide. */
#view-dashboard.dashboard-layout {
  grid-template-columns: minmax(0, 1fr);
}

.dashboard-pane.home-pane {
  display: flex;
  flex-direction: column;
  gap: 1.35rem;
  width: min(60rem, 100%);
  padding: 0 1.15rem 2rem;
}

.home-pane > .pane-head {
  align-items: flex-start;
  gap: 1rem;
  padding: 1.75rem 0 0;
}

.home-head-text {
  display: grid;
  gap: 0.3rem;
  min-width: 0;
}

.home-greeting {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-body);
  font-weight: 600;
}

.pane-head h2.home-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-setup-title);
  font-weight: 750;
  letter-spacing: -0.025em;
  line-height: 1.15;
  text-wrap: balance;
}

.home-pane .dash-setup {
  position: relative;
  margin: 0;
  background: var(--bg-elevated);
  border-color: var(--line);
}

.home-pane .dash-setup.blocking {
  border-color: color-mix(in srgb, var(--ask) 45%, var(--line));
  background: color-mix(in srgb, var(--ask) 9%, var(--bg-elevated));
}

.home-pane .dash-setup h3 {
  font-size: var(--fs-section-title);
}

.home-pane .dash-setup .btn {
  min-height: var(--target-min);
}

/* The close button sits in the card's corner at every width. As a flex item
   it wrapped onto a row of its own under «Gespräch starten» on a phone. */
.dash-setup-dismiss {
  position: absolute;
  top: 0.35rem;
  right: 0.35rem;
  width: var(--target-min);
  height: var(--target-min);
  margin: 0;
}

.dash-setup[data-kind="recommend"] :is(#dashboard-setup-title, #dashboard-setup-body) {
  padding-right: 2.75rem;
}

/* «Empfohlen»: one row per thing the reader can do, each with its own
   button, instead of the step-name pills. */
.dash-setup[data-kind="recommend"] .dash-setup-steps {
  display: grid;
  gap: 0.6rem;
  margin-top: 0.8rem;
}

.dash-setup[data-kind="recommend"] .dash-setup-steps li {
  padding: 0;
  border: 0;
  color: var(--ink);
  font-size: var(--fs-body);
}

.home-rec-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--line-soft);
}

.home-rec-text {
  flex: 1 1 16rem;
  min-width: 0;
}

.home-rec-title {
  margin: 0;
  font-weight: 700;
}

.home-rec-body {
  margin: 0.15rem 0 0;
  color: var(--muted);
  font-size: var(--fs-small);
  line-height: 1.45;
}

.home-rec-action {
  flex: 0 0 auto;
  min-height: var(--target-min);
}

/* The composer: one large field, attach and «Starten» under it. The
   textarea carries the frame, so the page-wide focus ring is its focus
   indicator and nothing here has to replace it. */
/* min-width 0 and shrinkable tracks: the composer never takes its width
   from what it contains, so nothing inside can push it past the screen. */
.home-composer {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.6rem;
  min-width: 0;
}

.home-field {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.6rem;
  min-width: 0;
  border-radius: var(--radius);
}

.home-field textarea {
  --control-size: 1.0625rem;
  width: 100%;
  box-sizing: border-box;
  min-height: 7.5rem;
  padding: 0.95rem 1.05rem;
  border: 1px solid var(--control-line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.5;
  resize: vertical;
  box-shadow: var(--shadow-app-soft);
  transition: border-color var(--dur-fast) var(--ease);
}

.home-field textarea:focus {
  border-color: var(--accent);
}

.home-field textarea:disabled {
  background: color-mix(in srgb, var(--bg) 60%, var(--surface));
  color: var(--muted);
  cursor: not-allowed;
  box-shadow: none;
}

.home-field.is-dragover textarea {
  border-color: var(--accent);
  border-style: dashed;
  background: color-mix(in srgb, var(--accent) 6%, var(--surface));
}

/* Attached files sit between the text and the buttons. */
.home-field .file-chip-list-inside {
  padding: 0;
  border-bottom: 0;
}

.home-slot-hint {
  margin: -0.2rem 0 0;
  color: var(--muted);
  font-size: var(--fs-small);
}

/* The row wraps: on a phone at a large text size, attach, the microphone
   and «Starten» (8.5rem) no longer fit one line, and a row that cannot wrap
   made the whole composer wider than the screen and cut «Starten» off at
   the right edge (375px at «Gross» with the microphone, 320px already at
   normal size). «Starten» keeps the right-hand end when it wraps. */
.home-field-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 0.75rem;
}

.home-field-foot > .home-start {
  margin-left: auto;
}

.home-attach,
.home-start {
  min-height: var(--target-min);
}

.home-start {
  min-width: 8.5rem;
  font-size: 1.0625rem;
}

.home-attach-icon,
.home-example-icon {
  flex-shrink: 0;
}

/* «Einmal jetzt erledigen» / «Als Routine einrichten». */
.home-recurrence {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
  padding: 0.85rem 1rem;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line));
  border-radius: var(--radius);
  background: var(--accent-tint);
}

.home-recurrence-text {
  flex: 1 1 18rem;
  margin: 0;
  font-weight: 600;
}

.home-recurrence-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.home-recurrence-actions .btn {
  min-height: var(--target-min);
}

.home-examples {
  display: grid;
  gap: 0.55rem;
}

.home-example-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.home-example-routine {
  border-style: dashed;
}

.chip.home-example:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

/* Sections under the composer share one card shape. */
.home-section {
  padding: 1rem 1.05rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-app-soft);
}

.home-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.home-section-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-section-title);
  font-weight: 700;
  letter-spacing: -0.015em;
}

.home-section > .home-section-title {
  margin-bottom: 0.75rem;
}

.home-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* «Wartet auf Sie»: the one thing on this page that is somebody else
   waiting, so it carries the ask colour. */
.home-waiting {
  border-left: 4px solid var(--ask);
}

.home-waiting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem 1rem;
  min-height: var(--row-min);
  padding: 0.65rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
}

.home-row-text {
  flex: 1 1 auto;
  min-width: 0;
}

.home-question {
  margin: 0;
  font-weight: 600;
  line-height: 1.4;
  overflow-wrap: anywhere;
}

.home-row-meta {
  margin: 0.15rem 0 0;
  overflow: hidden;
  color: var(--muted);
  font-size: var(--fs-small);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.home-answer {
  flex: 0 0 auto;
  min-height: var(--target-min);
}

.home-columns {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: 1.35rem;
  align-items: start;
}

/* Either card alone takes the whole row. */
.home-columns:has(> .home-section.hidden) {
  grid-template-columns: minmax(0, 1fr);
}

.home-recent .home-list {
  gap: 0.35rem;
}

.home-recent-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  min-height: var(--row-min);
  padding: 0.5rem 0.75rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.home-recent-row:hover {
  border-color: var(--line);
  background: var(--bright);
}

.home-recent-title {
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.home-recent-time {
  color: var(--muted);
  font-size: var(--fs-small);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.home-all-tasks {
  font-size: var(--fs-small);
  font-weight: 600;
  min-height: var(--target-min);
}

.home-saved {
  display: grid;
  align-content: start;
}

.home-saved .home-section-head {
  margin-bottom: 0.35rem;
}

.home-estimate {
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  font-size: var(--fs-small);
  font-weight: 600;
}

.home-saved-value {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-stat);
  font-weight: 780;
  letter-spacing: -0.03em;
  line-height: 1.1;
}

.home-saved-note {
  margin: 0.4rem 0 0;
  line-height: 1.45;
}

.home-scope {
  justify-self: start;
  margin-top: 0.6rem;
}

/* «Aktivität»: the same disclosure as #dash-technical inside it. Its body
   brings the old dashboard with its own padding, so this one adds none. */
.home-activity .home-activity-body {
  padding: 0 0 0.5rem;
}

.home-activity .dashboard-body {
  padding: 0.35rem 0.75rem 0.5rem;
}

/* The old tiles were rebuilt on every 5-second poll, which re-ran their
   entrance animation each time; inside the disclosure they just sit. */
.home-activity .stat-tile {
  animation: none;
}

@media (max-width: 820px) {
  .dashboard-pane.home-pane {
    gap: 1.1rem;
    padding: 0 1rem 1.5rem;
  }

  .home-pane > .pane-head {
    padding-top: 1.15rem;
  }

  .home-columns {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.1rem;
  }

  .home-field textarea {
    min-height: 6.5rem;
  }

  /* One thumb's reach: «Starten» fills the row, attach sits beside it. */
  .home-field-foot .home-start {
    flex: 1 1 auto;
  }

  .home-waiting-row {
    flex-wrap: wrap;
  }

  .home-waiting-row .home-answer {
    flex: 1 1 100%;
  }

  .home-recent-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "title title" "pill time";
    row-gap: 0.3rem;
  }

  .home-recent-title {
    grid-area: title;
  }

  .home-recent-row .status-pill {
    grid-area: pill;
    justify-self: start;
  }

  .home-recent-time {
    grid-area: time;
  }

  .home-activity .dashboard-body {
    padding: 0.35rem 0.5rem 0.5rem;
  }
}

/* === overhaul:tasks === */
/* The Tasks view, the task detail and the new-task dialog (SPEC 6.2). */

/* --- The list: search, filter chips, count ------------------------------ */
.runs-tools {
  display: grid;
  gap: 0.55rem;
  flex-shrink: 0;
  padding: 0 1.15rem 0.6rem;
}

.runs-search {
  width: 100%;
  box-sizing: border-box;
  min-height: var(--target-min);
  padding: 0.5rem 0.8rem;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
}

.runs-search::placeholder {
  color: var(--muted);
}

.runs-filters .chip {
  padding: 0.3rem 0.8rem;
}

.runs-count {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-small);
}

.runs-count:empty {
  display: none;
}

/* Date headings between the rows, and the pinned «Wartet auf Sie» group. */
.run-list li.run-group {
  margin: 0.95rem 0 0.3rem;
  padding: 0 0.4rem;
}

.run-list li.run-group:first-child {
  margin-top: 0.25rem;
}

.run-list li.run-group + li {
  margin-top: 0;
}

.run-list li.run-group[data-group="waiting"] .run-group-label {
  color: var(--ask);
}

/* A row: title and time on the first line, status under it. Full view puts
   the number above the title (runItemHtml's .only-full parts). `.rid` gives
   up its own box so its two spans join the row's grid. */
.run-list li button:not(.run-continue) {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "task time"
    "meta meta";
  column-gap: 0.75rem;
  row-gap: 0.4rem;
  align-items: baseline;
}

html[data-mode="full"] .run-list li button:not(.run-continue) {
  grid-template-areas:
    "id time"
    "task task"
    "meta meta";
  row-gap: 0.25rem;
}

.run-list .rid {
  display: contents;
}

.run-list .rid-id {
  grid-area: id;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--muted);
}

.run-list .rtime {
  grid-area: time;
  font-family: var(--font-body);
  font-size: var(--fs-small);
  color: var(--muted);
}

.run-list .rtask {
  grid-area: task;
  margin: 0;
  white-space: normal;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-height: 1.35;
}

.run-list .rmeta {
  grid-area: meta;
  font-size: 0.8125rem;
}

/* «Weiter» on an interrupted row: a word, not a mono label. */
.run-list li button.run-continue {
  min-height: 2.25rem;
  padding: 0.25rem 0.8rem;
  font-family: var(--font-body);
  font-size: var(--fs-small);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}

.run-list li.can-continue .rmeta {
  padding-right: 6rem;
}

.run-list li.runs-older {
  display: grid;
  justify-items: center;
  gap: 0.35rem;
  margin-top: 0.75rem;
  padding: 0.25rem 0;
}

.run-list li.runs-older.hidden {
  display: none;
}

.run-list li.runs-older .btn {
  min-height: var(--target-min);
}

.run-list li.runs-older p {
  margin: 0;
  font-size: var(--fs-small);
}

/* --- List or board (ClaudeOS#1030) --------------------------------------- */

/* The heading, the switch and «Aktualisieren» share a row while they fit;
   in a narrow pane or at a large text size the controls move under the
   heading, still at the right, instead of running out of the pane. */
.runs-pane .pane-head {
  flex-wrap: wrap;
  row-gap: 0.5rem;
}

.runs-pane .pane-head-actions {
  margin-left: auto;
}

/* The switch in the pane head: two segments with an icon and a word. */
.tasks-layout {
  flex-wrap: nowrap;
}

.tasks-layout .segmented-options {
  flex: 0 0 auto;
}

.tasks-layout .segmented-options label {
  gap: 0.4rem;
  padding: 0.35rem 0.75rem;
}

.tasks-layout svg {
  flex: none;
  width: 1rem;
  height: 1rem;
}

/* From the large text size up, the two words pushed the switch under the
   heading and took 35-50px off the list, and at the largest size on a 320px
   phone «Aktualisieren» ran off the pane: the segments show only their
   icons there. The words stay the radios' names for a screen reader, and a
   tooltip for a mouse (data-i18n-title). */
html:is([data-text-size="large"], [data-text-size="larger"], [data-text-size="largest"]) .tasks-layout .segmented-options label > span {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

html:is([data-text-size="large"], [data-text-size="larger"], [data-text-size="largest"]) .tasks-layout .segmented-options label {
  justify-content: center;
  min-width: var(--target-min);
  padding: 0.35rem 0.6rem;
}

/* Which of the two shows is CSS from the attribute the <head> script
   pre-paints, so a reload never flashes the list before the board. #run-list
   stays in the DOM either way: app.js reads it. */
.run-board,
.run-board-older {
  display: none;
}

html[data-tasks-layout="board"] #run-list {
  display: none;
}

html[data-tasks-layout="board"] .run-board:not(.is-empty) {
  display: grid;
}

html[data-tasks-layout="board"] .run-board-older:not(.hidden) {
  display: grid;
}

/* The four status chips are the board's columns; «Alle» and «Ausgeblendet»
   stay. app.js resets a pressed status chip when the board comes on. */
html[data-tasks-layout="board"] .runs-filters .chip[data-run-filter="waiting"],
html[data-tasks-layout="board"] .runs-filters .chip[data-run-filter="running"],
html[data-tasks-layout="board"] .runs-filters .chip[data-run-filter="done"],
html[data-tasks-layout="board"] .runs-filters .chip[data-run-filter="problems"] {
  display: none;
}

/* The board is the only thing here that scrolls sideways; each column's
   list scrolls down on its own. Widths in rem, so columns grow with the
   text size and the board scrolls rather than squeezing them. */
.run-board {
  grid-auto-flow: column;
  /* Never wider than the board: at the largest text beside an open task
     one 16rem column no longer fits, and its edge hid behind the panel. */
  grid-auto-columns: minmax(min(16rem, 100%), 1fr);
  grid-template-rows: minmax(0, 1fr);
  gap: 0.75rem;
  flex: 1;
  min-height: 0;
  overflow: auto hidden;
  overscroll-behavior-x: contain;
  padding: 0.35rem 1.15rem 1rem;
}

.run-board-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  border: 1px solid var(--line-soft);
  border-top: 3px solid var(--run);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-elevated) 50%, var(--bg));
  overflow: hidden;
}

/* The state's colour as a strip; the column's word always says it too. */
.run-board-col[data-col="waiting"] {
  border-top-color: var(--ask);
}

.run-board-col[data-col="done"] {
  border-top-color: var(--ok);
}

.run-board-col[data-col="problems"] {
  border-top-color: var(--fail);
}

.run-board-head {
  flex-shrink: 0;
  padding: 0.7rem 0.85rem 0.5rem;
}

.run-board-title {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  margin: 0;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-section-title);
  font-weight: 700;
}

.run-board-count {
  color: var(--muted);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.run-board-list {
  flex: 1;
  min-height: 0;
  margin: 0;
  padding: 0.1rem 0.55rem 0.75rem;
  list-style: none;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  /* The cards ask how wide their column is (@container below), not the
     screen: a phone column at the largest text is narrower than a desktop
     one at normal size. */
  container-type: inline-size;
}

.run-board-list:empty {
  display: none;
}

.run-board-empty {
  margin: 0;
  padding: 0.1rem 0.85rem 0.9rem;
  color: var(--muted);
  font-size: var(--fs-small);
}

.run-board-list li {
  position: relative;
}

.run-board-list li + li {
  margin-top: 0.5rem;
}

/* Only a card that is genuinely new rises in, as in the list. */
.run-board-list li.new-item {
  animation: rise 0.45s var(--ease) both;
}

/* A card: the list row's markup (runItemHtml) plus one line of context,
   on its own surface. Full view adds the number above the title. */
.run-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "task time"
    "note note"
    "meta meta";
  column-gap: 0.75rem;
  row-gap: 0.4rem;
  align-items: baseline;
  width: 100%;
  min-height: var(--row-min);
  padding: 0.75rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.18s ease, transform 0.18s var(--ease);
}

html[data-mode="full"] .run-card {
  grid-template-areas:
    "id time"
    "task task"
    "note note"
    "meta meta";
  row-gap: 0.3rem;
}

.run-card:hover {
  border-color: var(--control-line);
}

.run-card:active {
  transform: scale(0.995);
}

/* The open task: the list's bar, on the card. */
.run-card.active {
  border-color: color-mix(in srgb, var(--accent-bright) 45%, var(--line));
  box-shadow: inset 3px 0 0 var(--accent-bright);
}

/* Drawn inside: the column clips anything outside its list. */
.run-card:focus-visible {
  outline-offset: -2px;
}

.run-card .rid {
  display: contents;
}

.run-card .rid-id {
  grid-area: id;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
}

.run-card .rtime {
  grid-area: time;
  color: var(--muted);
  font-size: var(--fs-small);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.run-card .rtask,
.run-card .rnote {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}

.run-card .rtask {
  grid-area: task;
  font-weight: 600;
  line-height: 1.35;
}

.run-card .rnote {
  grid-area: note;
  color: var(--muted);
  font-size: var(--fs-small);
  line-height: 1.4;
}

.run-card .rmeta {
  grid-area: meta;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 0.5rem;
  color: var(--muted);
  font-size: 0.8125rem;
}

/* Nothing on a card may be wider than the card: the number never breaks
   at its hyphen, and a folder or an issue may break anywhere. */
.run-card .rid-id {
  white-space: nowrap;
}

.run-card .rmeta > .only-full {
  min-width: 0;
  max-width: 100%;
}

.run-card .issue-chip {
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* A narrow column -- a phone at a large text size, about 14 of its own
   letters wide: the time no longer fits beside the title (Simple view) or
   the number (Full view) and goes on a line of its own, so the title keeps
   the card's width. It goes first, where the DOM has it (runItemHtml), so
   the card is read in the order it is seen (WCAG 1.3.2). One status word
   wider than the card («Fehlgeschlagen» at 150%) is hyphenated (html
   lang), or broken as a last resort. `em` here is the column's own size,
   so the threshold grows with the text. */
@container (max-width: 14em) {
  .run-card {
    grid-template-areas:
      "time time"
      "task task"
      "note note"
      "meta meta";
  }

  html[data-mode="full"] .run-card {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "id"
      "time"
      "task"
      "note"
      "meta";
  }

  .run-card .rtime {
    justify-self: start;
  }

  .run-card .status-pill {
    max-width: 100%;
    hyphens: auto;
    overflow-wrap: break-word;
  }
}

/* «Weiter» on an interrupted card is a sibling of the card's button (a
   button cannot hold another), placed in a row of its own at the card's
   foot: the card keeps that room free under the status line. A gutter
   beside the status line was too narrow for a status word that cannot
   wrap, and the button, taller than one line, rose into the note. */
.run-board-list li.can-continue .run-card {
  padding-bottom: calc(0.7rem + 2.25rem + 0.5rem);
}

.run-board-list li .run-continue {
  position: absolute;
  right: 0.8rem;
  bottom: 0.7rem;
  min-height: 2.25rem;
  padding: 0.25rem 0.8rem;
  border: 1px solid color-mix(in srgb, var(--run) 55%, transparent);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--run) 12%, var(--surface));
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-small);
  font-weight: 600;
  cursor: pointer;
}

.run-board-list li .run-continue:hover {
  border-color: var(--run);
}

.run-board-list li .run-continue:disabled {
  opacity: 0.6;
  cursor: progress;
}

.run-board-older {
  flex-shrink: 0;
  justify-items: center;
  gap: 0.35rem;
  padding: 0 1.15rem 0.85rem;
}

.run-board-older .btn {
  min-height: var(--target-min);
}

/* «Ältere laden» and «Wird geladen» share one cell, so the button is as big
   as the larger of the two in either state; only one is shown, and the
   hidden one is not read either. */
.run-board-older .btn:has(> .older-busy) {
  display: inline-grid;
  place-items: center;
}

.run-board-older .btn > span {
  grid-area: 1 / 1;
}

.run-board-older .btn:not(.is-loading) > .older-busy,
.run-board-older .btn.is-loading > .older-idle {
  visibility: hidden;
}

.run-board-older p {
  margin: 0;
}

@media (min-width: 821px) {
  /* No task open: the board has the whole view. */
  html[data-tasks-layout="board"] #view-runs.layout {
    grid-template-columns: minmax(0, 1fr);
  }

  html[data-tasks-layout="board"] #view-runs.layout:not(.run-open) .detail-pane {
    display: none;
  }

  html[data-tasks-layout="board"] #view-runs.layout:not(.run-open) .runs-pane {
    border-right: none;
  }

  /* A task open: it takes the right, the board stays in view on the left
     and scrolls sideways once its columns no longer fit. Never more than
     half: at the largest text on a rail-width screen 24rem is most of the
     view, and the board would shrink to a sliver. */
  html[data-tasks-layout="board"] #view-runs.layout.run-open {
    grid-template-columns: minmax(0, 1fr) minmax(min(24rem, 50%), 40%);
  }

  /* «← Aufgaben» closes the panel and gives the board its room back. Two
     classes and the view, or `.btn`'s own display wins by source order. */
  html[data-tasks-layout="board"] #view-runs .btn.mobile-back {
    display: inline-flex;
  }

  /* Beside «Ausblenden» on a pointer screen: as tall as its neighbour and
     the switch above the board (the phone keeps its own small-button
     size), and level with it -- the phone's top margin would push it down. */
  html[data-tasks-layout="board"] #view-runs .detail-pane .btn.mobile-back {
    min-height: max(var(--target-min), 2.75rem);
    margin: 0;
  }

  /* The panel beside the board is narrow: «← Aufgaben» and the task's
     actions share the top row while they fit, and the actions drop to a
     row of their own, still at the right, when they do not (the largest
     text on a rail-width screen) -- the two never share pixels. The title
     gets the full width under them. */
  html[data-tasks-layout="board"] #view-runs .detail-pane .detail-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem 0.75rem;
  }

  html[data-tasks-layout="board"] #view-runs .detail-head > .detail-actions {
    order: 1;
    margin-left: auto;
    max-width: 100%;
    flex-wrap: wrap;
    justify-content: flex-end;
  }

  html[data-tasks-layout="board"] #view-runs .detail-head > div:not(.detail-actions) {
    order: 2;
    flex: 1 0 100%;
    min-width: 0;
  }
}

@media (max-width: 820px) {
  /* One column at a time, the next one peeking in at the edge; the columns
     snap as they are swiped. The board sits inside .runs-pane, so an open
     task replaces it exactly as it replaces the list. */
  .run-board {
    grid-auto-columns: min(calc(100% - 2.5rem), 24rem);
    gap: 0.6rem;
    padding: 0.2rem 0.9rem 0;
    scroll-padding-inline: 0.9rem;
    scroll-snap-type: x mandatory;
  }

  .run-board-col {
    scroll-snap-align: start;
    border-bottom: none;
    border-radius: var(--radius) var(--radius) 0 0;
  }

  /* «Ältere laden» under the board sits outside every scroller, so no end
     spacer keeps it clear of the fixed «Neue Aufgabe» button (.atlas-fab):
     it moves to the left of it instead -- the button's width, about 9.7rem
     at every text size, plus its right offset. */
  .run-board-older {
    grid-template-columns: minmax(0, 1fr) calc(max(1rem, env(safe-area-inset-right, 0px)) + 10rem);
    justify-items: start;
    column-gap: 0;
    padding: 0.5rem 0.9rem 0.75rem;
  }

  /* As tall as «Neue Aufgabe» and level with it (its 0.75rem above the tab
     bar): the row then covers the button's whole height, so the note above
     never runs under it, at any text size. */
  .run-board-older > .btn {
    grid-column: 1;
    min-height: max(var(--target-min), 3.25rem);
  }

  /* «Es gibt keine älteren Aufgaben» on a line of its own, the full width
     of the bar, above the button's row (index.html puts it first): squeezed
     beside the button it ran to three to five lines. That row keeps its
     height when the button has gone with the last page, so the note stays
     clear of «Neue Aufgabe». */
  .run-board-older > p {
    grid-column: 1 / -1;
  }

  /* Holds the row only: `!important` against .hidden's own, and the button
     stays invisible, unfocusable and unread (visibility). */
  .run-board-older:has(> p:not(.hidden)) > .btn.hidden {
    display: inline-grid !important;
    visibility: hidden;
  }
}

/* Where «Ältere laden» has no room beside the button -- a small phone at
   the two largest text sizes -- the control stays centred and clears it
   from below instead. Elsewhere it may wrap onto two lines; only the longer
   label of a load in progress («Ältere Aufgaben werden geladen») can reach
   under the button at the largest size, for the moment the control is
   disabled anyway. Clearing it from below everywhere would cost a 390px
   phone at the largest size its one whole card. */
@media (max-width: 340px) {
  html[data-text-size="larger"] .run-board-older {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    padding-right: 0.9rem;
    padding-bottom: calc(3.25rem + 0.75rem + 0.5rem);
  }

  /* The padding already clears the button: no row held for it. */
  html[data-text-size="larger"] .run-board-older:has(> p:not(.hidden)) > .btn.hidden {
    display: none !important;
  }
}

@media (max-width: 380px) {
  html[data-text-size="largest"] .run-board-older {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    padding-right: 0.9rem;
    padding-bottom: calc(3.25rem + 0.75rem + 0.5rem);
  }

  /* The padding already clears the button: no row held for it. */
  html[data-text-size="largest"] .run-board-older:has(> p:not(.hidden)) > .btn.hidden {
    display: none !important;
  }
}

/* Short screens -- a phone on its side, a small phone at a large text size,
   a low window: the pane head, the search and the chips took nearly all the
   height, and the list or the board under them got a sliver or nothing
   (740x360: no card at any text size; 360x740 at the largest size: one
   row). There the pane scrolls as one: the head scrolls away, the list and
   the board's columns grow with what they hold, and the board keeps only
   its sideways scroll. One scroller, so a swipe never gets caught inside a
   column. The phone's end spacers (.run-list::after, .run-board-list::after)
   still end it above the «Neue Aufgabe» button. A media query cannot see
   the text size, so a phone counts as short up to 760px, where the largest
   size leaves the list a few rows; a desktop window only below 520px. A
   taller phone keeps one scroller per column, so a long «Erledigt» never
   leaves the other columns scrolled to empty space. */
@media (max-height: 520px), (max-width: 820px) and (max-height: 760px) {
  .runs-pane {
    overflow-y: auto;
    overscroll-behavior-y: contain;
  }

  .run-board {
    flex: none;
    grid-template-rows: auto;
    padding-bottom: 1rem;
  }

  .run-board-list {
    flex: none;
    overflow: visible;
  }

  .run-list {
    flex: none;
    overflow: visible;
  }
}

/* A tall phone is short too at the two largest text sizes: the pane head,
   the search and the chips grow with the text, while «Ältere laden» under
   the board takes its own rows, and each column kept 64-96px at the largest
   size on 360x780 to 375x812 (190-280px up to 430x932) -- less than one
   card. A media query cannot see the text size, so the attribute does: up
   to 940px high the pane scrolls as one there as well. A tablet keeps one
   scroller per column. */
@media (max-width: 820px) and (max-height: 940px) {
  html:is([data-text-size="larger"], [data-text-size="largest"]) .runs-pane {
    overflow-y: auto;
    overscroll-behavior-y: contain;
  }

  html:is([data-text-size="larger"], [data-text-size="largest"]) .run-board {
    flex: none;
    grid-template-rows: auto;
    padding-bottom: 1rem;
  }

  html:is([data-text-size="larger"], [data-text-size="largest"]) .run-board-list {
    flex: none;
    overflow: visible;
  }

  html:is([data-text-size="larger"], [data-text-size="largest"]) .run-list {
    flex: none;
    overflow: visible;
  }
}

/* --- The inline composer: Simple view asks only for the text ------------ */
.empty-run-controls {
  justify-content: flex-end;
}

/* --- The open task ------------------------------------------------------- */

/* The pane's bottom padding goes while a task is open: the reply dock ends
   the pane itself, flush with its edge. */
.detail-pane:has(> #detail:not(.hidden)) {
  padding-bottom: 0;
}

/* Scrolling something into view stops above the pinned reply dock, whose
   height wireTasks() measures (WCAG 2.4.11). */
.detail-pane {
  scroll-padding-bottom: calc(var(--reply-dock-h, 7rem) + 0.5rem);
}

/* In Simple view the card's label is a word at reading size. */
html:not([data-mode="full"]) .tldr-card-label {
  font-size: var(--fs-small);
  letter-spacing: 0;
  text-transform: none;
}

.answer-preview {
  margin: 0;
  color: var(--ink);
  white-space: pre-line;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

.answer-jump {
  margin-top: 0.55rem;
  min-height: 2.25rem;
  font-weight: 600;
}

/* «Das hat nicht geklappt». */
.run-problem {
  display: grid;
  gap: 0.6rem;
  margin: 0;
  padding: 1rem 1.05rem 1.05rem;
  border: 1px solid color-mix(in srgb, var(--fail) 40%, var(--line));
  border-left: 3px solid var(--fail);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--fail) 7%, var(--bg-elevated));
}

.run-problem-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-section-title);
  font-weight: 700;
  color: var(--ink);
}

.run-problem-sentence {
  margin: 0;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

.run-problem-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.run-problem-actions .btn {
  min-height: var(--target-min);
}

.run-problem-technical > summary {
  display: inline-flex;
  align-items: center;
  min-height: 2.25rem;
  color: var(--muted);
  font-size: var(--fs-small);
  font-weight: 600;
  cursor: pointer;
}

.run-problem-detail {
  margin: 0.35rem 0 0;
  padding: 0.65rem 0.75rem;
  max-height: 14rem;
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* «Noch nicht übernommen». */
.run-unmerged {
  display: grid;
  gap: 0.55rem;
  justify-items: start;
  margin: 0;
  padding: 0.9rem 1rem 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
}

.run-unmerged-head,
.run-unmerged-explain {
  margin: 0;
}

.run-unmerged-explain {
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

/* «Kopieren» and «Teilen» under an answer. */
.chat-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.35rem;
  margin-top: 0.55rem;
}

.chat-actions .btn.small {
  min-height: 2.25rem;
  padding: 0.25rem 0.75rem;
  font-size: var(--fs-small);
}

/* The activity line under «Atlas arbeitet daran». It lines up with the
   label, right of the orb. */
.chat-progress {
  display: grid;
  justify-items: start;
  gap: 0.35rem;
  margin: 0.2rem 0 0.25rem;
  padding-left: 1.75rem;
}

.chat-progress-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.6rem;
  margin: 0;
  font-size: var(--fs-small);
  color: var(--ink);
}

.chat-progress-elapsed {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

.chat-progress-note {
  margin: 0;
  font-size: var(--fs-small);
}

.chat-progress .btn {
  min-height: 2.25rem;
}

/* The files the task made. */
.run-files {
  display: grid;
  gap: 0.55rem;
  margin-top: 1rem;
}

.run-files-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-section-title);
  font-weight: 700;
}

.run-files-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.run-files-list li {
  min-width: 0;
  max-width: 100%;
}

.run-file-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  max-width: 100%;
  min-height: var(--target-min);
  box-sizing: border-box;
  padding: 0.35rem 0.8rem 0.35rem 0.4rem;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: var(--fs-small);
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}

.run-file-chip:hover {
  border-color: var(--ink);
  background: var(--bright);
}

.run-file-ext {
  flex: none;
  padding: 0.2rem 0.4rem;
  border-radius: 5px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent-text);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 600;
}

.run-file-name {
  min-width: 0;
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.run-file-size {
  flex: none;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* --- The reply dock (SPEC 6.2) -------------------------------------------
   Pinned to the bottom of .detail-pane, which stays the one scroller; it
   bleeds over the pane's side padding like the pinned header does, and
   brings its own ground. While Atlas waits for an answer the question card
   sits in it, right above the reply box. A dock taller than most of the
   screen (a phone with its keyboard up) scrolls itself rather than cover
   the whole conversation. `margin-top: auto` keeps it at the foot of the
   pane when the conversation is still short (#detail is a flex column
   that fills the pane), so the reply box never floats mid-screen. */
.detail-pane .reply-dock {
  position: sticky;
  bottom: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  max-height: 75dvh;
  overflow-y: auto;
  margin: auto -1.5rem 0;
  padding: 0.75rem 1.5rem 1rem;
  border-top: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg-elevated) 92%, var(--bg));
  box-shadow: 0 -8px 24px color-mix(in srgb, var(--ink) 6%, transparent);
}

.reply-dock .ask-card {
  margin: 0;
  padding: 0.75rem 0.9rem 0.8rem;
  gap: 0.5rem;
}

.reply-dock .ask-badge {
  font-size: var(--fs-small);
  letter-spacing: 0;
  text-transform: none;
}

.reply-dock .ask-question.is-clamped {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
}

.reply-dock .ask-more {
  align-self: flex-start;
  min-height: 2.25rem;
  font-size: var(--fs-small);
  font-weight: 600;
}

.reply-dock .ask-option {
  min-height: var(--target-min);
  padding: 0.5rem 1rem;
  font-size: var(--fs-body);
}

.reply-dock .ask-hint {
  font-size: var(--fs-small);
}

.reply-dock .send-form,
.reply-dock .ask-card + .send-form {
  margin: 0;
  padding: 0;
  border-top: none;
  gap: 0.4rem;
}

/* The label still names the field for a screen reader; the placeholder and
   the dock say what it is to everyone else. */
.reply-dock .send-form > .send-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

.reply-dock .send-row {
  --send-line-h: 3.1rem;
}

/* The drop zone becomes one small «Datei anhängen oder einfügen» control;
   dropping and pasting still work on the whole form. */
.reply-dock .file-drop-compact {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
}

.reply-dock .file-drop-compact > .send-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

.reply-dock .file-drop-compact .file-drop-zone {
  width: auto;
  min-height: 2.25rem;
  padding: 0.2rem 0.8rem 0.2rem 0.35rem;
  gap: 0.45rem;
  border: 1px solid var(--control-line);
  border-radius: 999px;
  background: var(--surface);
  box-shadow: none;
  transform: none;
}

.reply-dock .file-drop-compact .file-drop-glyph {
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
}

.reply-dock .file-drop-compact .file-drop-glyph svg {
  width: 1rem;
  height: 1rem;
}

.reply-dock .file-drop-compact .file-drop-copy strong {
  font-size: var(--fs-small);
}

.reply-dock .file-drop-compact .file-drop-sub {
  display: none;
}

.reply-dock .send-hint,
.reply-dock .send-note {
  font-size: var(--fs-small);
}

/* Simple view: while «Das hat nicht geklappt» explains a failure, the raw
   error bubble in the chat says the same thing in the harness's words. The
   card keeps those words under «Technische Angaben»; Full view shows both.
   CSS only, so the append-only chat is never touched. */
html:not([data-mode="full"]) #detail.has-problem .chat-bubble.error {
  display: none;
}

/* On a wide screen the pinned header carries Accept, Hide and Stop, so the
   copies at the foot of the pane are only height; phones hide the header's
   set and keep these. «Regelmässig wiederholen» lives only down here. */
@media (min-width: 821px) {
  .detail-actions-bottom > :not(#btn-repeat-run) {
    display: none;
  }

  .detail-actions-bottom:not(:has(> #btn-repeat-run:not(.hidden))) {
    display: none;
  }
}

@media (max-width: 820px) {
  .runs-tools {
    padding: 0 0.9rem 0.5rem;
  }

  /* The bleed follows the phone pane's own padding. */
  .detail-pane .reply-dock {
    margin: auto -0.9rem 0;
    padding: 0.6rem 0.9rem 0.75rem;
  }

  .reply-dock .file-drop-compact .file-drop-zone {
    min-height: var(--target-min);
  }

  .chat-progress {
    padding-left: 0;
  }

  .run-problem-actions .btn,
  .run-unmerged .btn {
    flex: 1 1 auto;
  }
}

/* === overhaul:routines-files === */
/* Routines and Files (SPEC 6.3, 6.4). */

/* --- Routinen ------------------------------------------------------------- */

.schedules-intro {
  margin: 0 1.15rem 0.9rem;
  max-width: 46rem;
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  color: var(--muted);
}

/* One calm notice while the routine timer is stopped, for everyone; the
   «Fortsetzen» button is admin-only in the markup. */
.scheduler-notice {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin: 0 1.15rem 0.9rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--line);
  border-left: 4px solid var(--queue);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-app-soft);
}

.scheduler-notice-icon {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--queue);
}

.scheduler-notice-copy {
  flex: 1 1 auto;
  min-width: 0;
}

.scheduler-notice-copy p {
  margin: 0;
  font-size: var(--fs-small);
  line-height: 1.45;
  color: var(--muted);
}

.scheduler-notice-copy .scheduler-notice-title {
  font-size: var(--fs-body);
  font-weight: 700;
  color: var(--ink);
}

html[data-role="admin"] .scheduler-notice-member {
  display: none;
}

.scheduler-notice > .btn {
  flex-shrink: 0;
}

/* The operator line (admins, Full view): compact, one row. */
.scheduler-status {
  align-items: center;
  padding: 0.6rem 0.85rem;
  border-color: var(--line);
  background: var(--bg-elevated);
}

.scheduler-status-main {
  align-items: center;
}

.scheduler-summary,
#scheduler-detail {
  font-size: var(--fs-small);
}

.scheduler-actions .btn.small {
  min-height: var(--target-min);
}

#schedule-list {
  gap: 0.75rem;
}

/* The card: the one surface every panel shares (SPEC 4). */
.schedule-card {
  padding: 1rem 1.1rem 0.85rem;
  border: 1px solid var(--line);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-app-soft);
}

.schedule-top {
  align-items: flex-start;
}

.schedule-main .schedule-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

.schedule-sid {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  font-weight: 500;
  color: var(--muted);
}

.schedule-main .rmeta {
  gap: 0.35rem 0.6rem;
  margin-top: 0.3rem;
  font-size: var(--fs-body);
}

.schedule-main .rmeta .schedule-cadence {
  color: var(--ink);
  font-weight: 600;
}

.schedule-main .rmeta .mono,
.schedule-expr {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  overflow-wrap: anywhere;
}

.schedule-expr,
.schedule-assistant,
.schedule-owner {
  display: inline-flex;
  align-items: center;
  min-height: 1.6rem;
  padding: 0.1rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bright);
  color: var(--muted);
  font-size: var(--fs-small);
  font-weight: 600;
}

.schedule-main .schedule-when {
  margin-top: 0.25rem;
  font-size: var(--fs-small);
  line-height: 1.45;
  color: var(--muted);
}

.schedule-error {
  margin-top: 0.55rem;
  padding: 0.5rem 0.7rem;
  border-left: 3px solid var(--danger);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--danger) 7%, var(--bg-elevated));
}

.schedule-error-text {
  margin: 0;
  color: var(--ink);
  font-size: var(--fs-small);
  font-weight: 600;
}

.schedule-error-details > summary {
  cursor: pointer;
  width: fit-content;
  margin-top: 0.2rem;
  color: var(--muted);
  font-size: var(--fs-small);
}

.schedule-error-details p {
  margin: 0.35rem 0 0;
  font-size: var(--fs-small);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

.schedule-prompt-wrap > summary,
.schedule-runs > summary {
  min-height: var(--target-min);
  font-size: var(--fs-small);
}

.schedule-prompt-wrap > summary {
  width: fit-content;
}

.schedule-prompt-wrap {
  margin-top: 0.2rem;
}

.schedule-prompt-wrap .schedule-prompt {
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

.schedule-runs {
  border-top-color: var(--line);
  padding-top: 0.1rem;
}

/* ▸, «Verlauf» and the count sat at the three ends of a space-between row.
   The label follows the triangle; the count sits on the right. */
.schedule-runs > summary {
  justify-content: flex-start;
  gap: 0.35rem;
}

.schedule-runs > summary .schedule-runs-count {
  margin-left: auto;
  font-size: var(--fs-small);
  font-weight: 500;
}

.schedule-run-row {
  min-height: var(--row-min);
  padding: 0.45rem 0.75rem;
}

.schedule-run-when {
  font-size: var(--fs-small);
  font-weight: 600;
}

.schedule-run-id {
  font-size: var(--fs-small);
}

.schedule-run-row .btn.small {
  min-height: var(--target-min);
}

.schedules-pane .schedule-runs-body > .hint,
.schedules-pane .schedule-runs-body > .error {
  font-size: var(--fs-small);
}

/* The switch on top, «Bearbeiten» and ⋯ side by side below it. */
.schedule-side {
  align-items: flex-end;
  gap: 0.35rem;
}

.schedule-switch {
  flex-direction: row-reverse;
}

.schedule-switch .switch-label {
  min-width: 2rem;
  text-align: right;
  color: var(--muted);
}

.schedule-switch[aria-checked="true"] .switch-label {
  color: var(--ink);
}

.schedule-switch[aria-busy="true"] {
  cursor: progress;
  opacity: 0.7;
}

.schedule-actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.schedule-actions > .btn {
  min-height: var(--target-min);
}

.schedule-menu-wrap {
  position: relative;
}

.btn.icon.schedule-menu-btn {
  width: var(--target-min);
  height: var(--target-min);
  padding: 0;
}

.schedule-menu-btn[aria-expanded="true"] {
  border-color: var(--ink);
  background: var(--bright);
}

.schedule-menu {
  position: absolute;
  top: calc(100% + 0.3rem);
  right: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  min-width: 13rem;
  padding: 0.3rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-dialog);
}

.schedule-menu.open-up {
  top: auto;
  bottom: calc(100% + 0.3rem);
}

.schedule-menu [role="menuitem"] {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: var(--target-min);
  padding: 0.45rem 0.75rem;
  border: 0;
  border-radius: calc(var(--radius-sm) - 2px);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.schedule-menu [role="menuitem"]:hover,
.schedule-menu [role="menuitem"]:focus-visible {
  background: var(--bright);
}

.schedule-menu [role="menuitem"]:focus-visible {
  outline-offset: -2px;
}

.schedule-menu [role="menuitem"].danger-text {
  color: var(--danger);
}

.schedules-empty {
  margin: 0.5rem 1.15rem 0;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
}

.schedules-empty p {
  margin: 0;
  max-width: 34rem;
}

.schedules-empty .btn {
  margin-top: 0.35rem;
}

/* --- The routine dialog --------------------------------------------------- */

#new-schedule-dialog {
  max-width: 38rem;
}

#new-schedule-form textarea {
  resize: vertical;
}

#new-schedule-form .timing-fieldset {
  padding: 0.7rem 0.95rem 0.95rem;
  border-color: var(--line);
}

#new-schedule-form .timing-fieldset legend {
  font-size: var(--fs-small);
  color: var(--ink);
}

/* `dialog label` stacks its children; a timing part reads as one line
   ("um [09:00]", "am Tag [15]"), so it runs in a row. */
#new-schedule-form .timing-part {
  flex-direction: row;
  flex-wrap: wrap;
  font-size: var(--fs-small);
}

/* «Jede Woche»: seven chips; several may be pressed. */
#new-schedule-form .timing-weekdays {
  flex-wrap: wrap;
  flex-basis: 100%;
  align-items: center;
}

.weekday-chips {
  gap: 0.35rem;
}

.weekday-chip {
  min-width: 3.1rem;
  justify-content: center;
  padding: 0.35rem 0.7rem;
}

/* The summary sentence: what, when, and where the result appears. */
#new-schedule-form .routine-summary {
  margin: 0;
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  background: var(--bright);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

.routine-more {
  border-top: 1px solid var(--line);
  padding-top: 0.2rem;
}

.routine-more > summary {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  width: fit-content;
  min-height: var(--target-min);
  font-size: var(--fs-small);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.routine-more > summary::-webkit-details-marker {
  display: none;
}

.routine-more > summary::before {
  content: "\25b8";
  display: inline-block;
  width: 0.9rem;
  color: var(--muted);
  transition: transform var(--dur-fast) var(--ease);
}

.routine-more[open] > summary::before {
  transform: rotate(90deg);
}

.routine-more-body {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: 0.4rem 0 0.2rem;
}

.routine-zone {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.4rem;
}

.routine-zone-toggle {
  min-height: var(--target-min);
  font-size: var(--fs-small);
  font-weight: 600;
}

#new-schedule-form #timing-part-timezone {
  flex-direction: column !important;
  align-items: stretch;
  width: 100%;
  color: var(--ink);
  font-weight: 600;
}

#new-schedule-form #timing-part-timezone select {
  width: 100% !important;
}

#schedule-owner-field .hint {
  font-weight: 400;
  color: var(--muted);
}

/* --- Dateien -------------------------------------------------------------- */

.artifacts-pane > .skills-count {
  font-size: var(--fs-small);
}

.artifacts-pane .files-tabs {
  padding: 0 1.15rem 0.75rem;
}

.artifacts-pane .files-tabs .skills-subtab {
  min-height: var(--target-min);
}

#files-scope-output > .hint,
#files-scope-input > .hint,
#files-scope-output > .skills-count,
#files-scope-input > .skills-count,
.files-capped {
  margin: 0 1.15rem 0.55rem;
  font-size: var(--fs-small);
  line-height: 1.45;
}

#files-scope-output > .error,
#files-scope-input > .empty {
  margin: 0.25rem 1.15rem 0.5rem;
}

.artifacts-pane .skills-tools {
  padding: 0 1.15rem 0.6rem;
}

/* The search and «Alle Dateien zeigen» share one row. */
.files-tools {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.files-tools .skill-search-label {
  flex: 1 1 16rem;
  min-width: 0;
}

.artifact-groups {
  gap: 0.85rem;
}

.artifact-group {
  padding: 0.85rem 0.95rem 0.95rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-app-soft);
}

.artifact-group-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.65rem;
}

.artifact-group-copy {
  min-width: 0;
}

.artifact-group-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-body);
  font-weight: 700;
  line-height: 1.35;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.artifact-group-meta {
  margin: 0.15rem 0 0;
  color: var(--muted);
  font-size: var(--fs-small);
}

.artifact-group-head .btn {
  flex-shrink: 0;
  min-height: var(--target-min);
}

.artifact-group-files {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.artifact-group-files > li {
  min-width: 0;
}

.artifacts-pane .artifact-row {
  gap: 0.85rem;
  padding: 0.5rem 0.75rem 0.5rem 0.5rem;
  border-color: var(--line);
  background: var(--surface);
}

.artifacts-pane a.artifact-row:hover {
  border-color: var(--ink);
  background: var(--bright);
}

.artifacts-pane a.artifact-row:focus-visible {
  outline-offset: -2px;
}

.artifacts-pane .artifact-meta {
  margin-top: 0.1rem;
  font-size: var(--fs-small);
}

.artifact-download {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--muted);
}

a.artifact-row:hover .artifact-download {
  color: var(--ink);
}

.artifact-group-more {
  margin: 0.5rem 0 0;
  color: var(--muted);
  font-size: var(--fs-small);
}

/* A picture of the file, or its type's line icon. */
.file-visual {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-sm);
  background: var(--bright);
  color: var(--muted);
  overflow: hidden;
}

.file-visual svg {
  width: 1.4rem;
  height: 1.4rem;
}

.file-visual.has-thumb {
  border: 1px solid var(--line);
  background: var(--bg);
}

.file-visual .file-thumb {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.file-kind-pdf,
.file-kind-doc,
.file-kind-slides,
.file-kind-mail {
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
  color: var(--accent-text);
}

.file-kind-sheet,
.file-kind-data,
.file-kind-calendar {
  background: color-mix(in srgb, var(--ok) 10%, var(--surface));
  color: var(--ok);
}

.file-kind-image,
.file-kind-video,
.file-kind-audio,
.file-kind-model {
  background: color-mix(in srgb, var(--run) 10%, var(--surface));
  color: var(--run);
}

.files-empty {
  margin: 0.5rem 1.15rem 0;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
}

.files-empty p {
  margin: 0;
}

/* «Ihre Unterlagen für Atlas»: the upload starts on drop or select. */
.artifacts-pane .inputs-upload {
  padding: 0 1.15rem 0.75rem;
  gap: 0.6rem;
}

.artifacts-pane .inputs-scope {
  flex: 0 1 auto;
  max-width: 22rem;
}

.artifacts-pane .file-drop-sub {
  font-size: var(--fs-small);
}

.inputs-progress {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
}

.inputs-progress progress {
  width: 100%;
  height: 0.5rem;
  accent-color: var(--accent);
}

.inputs-progress-text {
  font-size: var(--fs-small);
  font-weight: 600;
  overflow-wrap: anywhere;
}

.artifacts-pane .inputs-saved {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--ok);
  font-weight: 600;
}

.artifacts-pane .inputs-upload > .error {
  margin: 0;
}

#inputs-list {
  gap: 0.4rem;
}

.artifacts-pane .input-row .artifact-actions .btn {
  min-height: var(--target-min);
}

@media (max-width: 820px) {
  .schedules-intro,
  .scheduler-notice,
  .scheduler-status,
  .schedules-empty,
  .files-empty {
    margin-left: 0.85rem;
    margin-right: 0.85rem;
  }

  .scheduler-notice {
    flex-wrap: wrap;
  }

  .scheduler-notice > .btn {
    width: 100%;
  }

  #schedule-list,
  .artifact-groups,
  #inputs-list {
    padding-left: 0.85rem;
    padding-right: 0.85rem;
  }

  /* The phone block above stacks the card; the switch and the actions go
     side by side under the text. */
  .schedule-side {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    width: 100%;
  }

  .schedule-switch {
    flex-direction: row;
  }

  .schedule-switch .switch-label {
    text-align: left;
  }

  .artifact-group-head {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
  }

  .artifact-group-head .btn {
    align-self: flex-start;
  }

  .artifacts-pane .files-tabs,
  .artifacts-pane .skills-tools,
  .artifacts-pane .inputs-upload {
    padding-left: 0.85rem;
    padding-right: 0.85rem;
  }

  #files-scope-output > .hint,
  #files-scope-input > .hint,
  #files-scope-output > .skills-count,
  #files-scope-input > .skills-count,
  .files-capped {
    margin-left: 0.85rem;
    margin-right: 0.85rem;
  }
}

/* === overhaul:knowledge-skills === */
/* Knowledge and Skills (SPEC 6.5, 6.6). */

/* The phone's back buttons stay on the phone. `.mobile-back` alone loses to
   `.btn`'s own display by source order (#314); two classes and the view id
   win wherever the rules sit. */
@media (min-width: 821px) {
  #view-memory .btn.mobile-back,
  #view-skills .btn.mobile-back {
    display: none;
  }
}

/* -- Wissen / Knowledge -------------------------------------------------- */

.memory-tools {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0 0.85rem 0.6rem;
  flex-shrink: 0;
}

.memory-type-chips {
  gap: 0.35rem;
}

.memory-type-chips .chip {
  font-weight: 600;
}

/* A row is titled by what the note says. Two lines at most, then the
   meta line; the stored name only shows in the Full view. */
.memory-row .memory-row-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.35;
  overflow-wrap: anywhere;
}

.memory-row .rmeta {
  align-items: center;
  margin: 0.3rem 0 0;
  font-size: var(--fs-small);
}

.memory-row-name {
  font-size: var(--fs-small);
}

.memory-row[aria-current="true"] {
  background: color-mix(in srgb, var(--accent) 10%, var(--bg-elevated));
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

/* The type badge said UPPERCASE store words in an undefined colour
   (--text-muted); it is a plain label now, readable at a glance. */
.memory-type-badge {
  font-size: var(--fs-small);
  letter-spacing: 0;
  text-transform: none;
  padding: 0.05rem 0.5rem;
  border-color: var(--line);
  color: var(--idle);
}

/* «Firma» / «Persönlich» beside it: the same plain label, in its own
   colour, instead of the Settings list's 0.68rem capitals. */
#view-memory .secret-scope-badge {
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  padding: 0.05rem 0.5rem;
}

.memory-search-empty {
  margin: 0.5rem 1rem;
  color: var(--muted);
}

/* Nothing learned yet: the invitation stands where the list would be. The
   list is the pane's flex-grow scroller, so an empty one would push the
   invitation to the bottom of the screen. */
.memory-list:empty {
  display: none;
}

.memory-empty {
  margin: 0.5rem 0.85rem 1rem;
  padding: 1.75rem 1.1rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: var(--bg-elevated);
}

.memory-empty-text {
  margin: 0;
  max-width: 28rem;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

.memory-empty-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
  margin-top: 0.4rem;
}

.memory-empty-actions .btn {
  min-height: var(--target-min);
}

/* The open note. */
.memory-detail-heading {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}

.memory-detail-heading h2 {
  overflow-wrap: anywhere;
}

.memory-detail-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.55rem;
  margin: 0;
  font-size: var(--fs-small);
}

#md-path {
  margin: 0;
  font-size: var(--fs-small);
  overflow-wrap: anywhere;
}

.memory-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 44rem;
}

.memory-field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin: 0;
}

.memory-field-label {
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--ink);
}

.memory-field-body .memory-field-label {
  font-family: var(--font-display);
  font-size: var(--fs-section-title);
  font-weight: 700;
}

.memory-field .hint {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-small);
  line-height: 1.4;
}

.memory-form input[type="text"],
.memory-form select,
.memory-form textarea {
  width: 100%;
  box-sizing: border-box;
  /* The face and weight of the page; the size stays with the 1rem floor. */
  font: inherit;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
}

/* What Atlas remembers is prose a person writes, not code: the body font,
   and room for a paragraph. The Full view keeps the store's own notation
   readable in the same face. */
.memory-form textarea {
  min-height: 10rem;
  font-family: var(--font-body);
  line-height: var(--lh-body);
  resize: vertical;
}

.memory-form input.mono {
  font-family: var(--font-mono);
}

.memory-form input[readonly] {
  opacity: 1;
  color: var(--muted);
  background: var(--bg-elevated);
}

.memory-company {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
}

.memory-company input[type="checkbox"] {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin: 0.1rem 0 0;
  accent-color: var(--accent);
}

.memory-company-text {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}

.memory-company-label {
  font-weight: 600;
  cursor: pointer;
}

.memory-company .hint {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-small);
  line-height: 1.4;
}

.memory-more .settings-advanced-body {
  padding: 0.35rem 1rem 1rem;
  gap: 1rem;
}

.memory-form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}

.memory-form-actions .btn {
  min-height: var(--target-min);
}

#btn-delete-memory {
  margin-left: auto;
}

#memory-form-error {
  margin: 0;
}

@media (max-width: 820px) {
  .memory-tools {
    padding: 0 0.75rem 0.6rem;
  }

  .memory-empty-actions {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }

  /* Clear of the floating «Neue Aufgabe» button. */
  .memory-empty {
    margin-bottom: 5rem;
  }

  .memory-detail-pane {
    padding: 0.85rem 1rem 1.25rem;
  }

  #view-memory .detail-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
  }
}

/* -- Skills ---------------------------------------------------------------- */

/* «In Verwendung» / «Weitere Skills»: two headed groups of the same cards. */
.skill-group {
  flex-shrink: 0;
  margin: 0.4rem 0 0;
  padding: 0.6rem 0 0;
}

.skill-group + .skill-group,
.skill-own-section + .skill-group,
.skill-group + .skill-own-section {
  margin-top: 1rem;
  border-top: 1px solid var(--line);
}

.skill-group-title,
.skill-own-section h3 {
  margin: 0 0.95rem 0.2rem;
  font-family: var(--font-display);
  font-size: var(--fs-pane-title);
  font-weight: 700;
}

.skill-group-hint,
.skill-own-section .hint {
  margin: 0 0.95rem 0.6rem;
}

.skill-own-section {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
}

/* Only a member reads who can add a skill; fails closed, so a page that has
   not learned the role yet says it rather than offering a button. */
html[data-role="admin"] .skill-member-note {
  display: none !important;
}

.skill-card {
  border-color: var(--line);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-app-soft);
}

.skill-card.is-active {
  border-color: var(--line);
}

.skill-card-title {
  font-family: var(--font-display);
  font-size: 1.02rem;
}

/* A word a first-time reader can read, not a 0.68rem uppercase tag. */
.skill-card-state {
  font-size: var(--fs-status-simple);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--idle);
}

.skill-card-state.on {
  color: var(--ok);
}

.skill-card-state.update {
  color: var(--accent-text);
}

.skill-card-summary {
  font-size: var(--fs-small);
  color: var(--ink);
  opacity: 0.88;
}

.skill-card-foot {
  gap: 0.5rem;
  min-height: 0;
}

.skill-card-foot .btn,
.skill-card-note .btn {
  min-height: var(--target-min);
}

.skill-card-member {
  margin: 0;
  color: var(--muted);
  font-size: var(--fs-small);
  line-height: 1.4;
}

.skill-card-note.ok {
  color: var(--ok);
}

/* «Was ist neu» before a card's update. */
.skill-whatsnew-dialog {
  max-width: 40rem;
}

.skill-whatsnew-lead {
  margin: 0 0 0.4rem;
}

#skill-whatsnew-versions,
#skill-whatsnew-english {
  margin: 0 0 0.5rem;
}

.skill-whatsnew-body {
  max-height: min(50vh, 26rem);
  overflow: auto;
  margin: 0 0 0.5rem;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-size: var(--fs-small);
}

.skill-whatsnew-body h2 {
  font-size: var(--fs-section-title);
  margin: 0.8rem 0 0.3rem;
}

.skill-whatsnew-body h2:first-child {
  margin-top: 0;
}

@media (max-width: 820px) {
  .skill-card-grid {
    grid-template-columns: minmax(0, 1fr);
    padding: 0.35rem 0.75rem 0.5rem;
  }

  .skill-card-foot .btn {
    flex: 1 1 auto;
  }
}

/* === overhaul:settings === */
/* Settings, sign-in, invite and the setup wizard (SPEC 6.7, 6.8). */

/* --- Settings hub: search (SPEC 6.7) -------------------------------------
   The field filters the tiles by title and description; a tile that does
   not match carries .settings-search-miss (its .hidden belongs to the card
   it mirrors, so the two never fight). */
.settings-search {
  position: relative;
  max-width: 28rem;
}

.settings-search::before {
  content: "";
  position: absolute;
  left: 0.8rem;
  top: 50%;
  width: 1.05rem;
  height: 1.05rem;
  transform: translateY(-50%);
  background: var(--muted);
  pointer-events: none;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Ccircle cx='8.5' cy='8.5' r='5.5'/%3E%3Cpath d='m13 13 4 4'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Ccircle cx='8.5' cy='8.5' r='5.5'/%3E%3Cpath d='m13 13 4 4'/%3E%3C/svg%3E") center / contain no-repeat;
}

.settings-search input {
  box-sizing: border-box;
  width: 100%;
  min-height: var(--target-min);
  padding: 0.6rem 0.85rem 0.6rem 2.4rem;
  font: inherit;
  font-size: var(--fs-control);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
}

.settings-search input::placeholder {
  color: var(--muted);
  opacity: 1;
}

.settings-search input::-webkit-search-cancel-button {
  cursor: pointer;
}

.settings-tile.settings-search-miss {
  display: none;
}

.settings-search-empty {
  margin: -0.4rem 0 0;
}

.settings-hub-group.tone-developer {
  --tile-hue: var(--queue);
}

/* An expired LinkedIn login is the most urgent status on the hub, so its
   dot must not fall back to the neutral grey. */
.settings-tile-status[data-tone="error"]::before,
.settings-tile-status[data-tone="fail"]::before {
  background: var(--fail);
}

/* --- Cards: labelled fields, one rhythm --------------------------------- */
.settings-detail .settings-language-label {
  padding: 0;
  margin: 0 0 1.1rem;
  max-width: 26rem;
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--ink);
}

.settings-detail .settings-language-label select,
.settings-detail .settings-language-label input {
  box-sizing: border-box;
  width: 100%;
  max-width: none;
  min-height: var(--target-min);
  font: inherit;
  font-size: var(--fs-control);
  font-weight: 400;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  padding: 0.55rem 0.75rem;
}

.settings-detail .settings-wide-label {
  max-width: 36rem;
}

.settings-subhead {
  margin: 1.5rem 0 0.9rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
  font-family: var(--font-display);
  font-size: var(--fs-section-title);
  font-weight: 700;
}

.settings-segmented {
  max-width: 32rem;
  margin: 0 0 0.45rem;
}

.settings-segmented legend {
  color: var(--ink);
}

.settings-mode-hint {
  margin: 0 0 1.25rem;
  max-width: 32rem;
}

#settings-display-note {
  margin-top: 0.5rem;
}

/* --- KI-Abo ---------------------------------------------------------------
   One row per AI: its name, this person's own status, and a way to sign in
   when it is not connected. */
.subscription-summary {
  margin: 0 0 0.9rem;
  font-size: var(--fs-body, 1rem);
}

.subscription-list .subscription-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.75rem;
  min-height: var(--row-min);
  padding-block: 0.5rem;
}

.subscription-list .subscription-row .secret-actions:empty {
  display: none;
}

.subscription-name {
  font-weight: 600;
}

.status-pill.subscription-pill {
  justify-self: end;
}

.status-pill.subscription-pill.is-ok { color: var(--ok); }
.status-pill.subscription-pill.is-missing { color: var(--ask); }
.status-pill.subscription-pill.is-unknown { color: var(--queue); }

.status-pill.subscription-pill::before {
  animation: none;
}

#btn-subscription-connect {
  min-height: var(--target-min);
}

/* «Einrichtung»: the company's own steps and where each one stands. */
.settings-setup-steps {
  list-style: none;
  margin: 0.8rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  max-width: 32rem;
}

.settings-setup-steps li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: var(--target-min);
  padding: 0.45rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bright);
}

.settings-setup-step-state {
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--ask);
}

.settings-setup-steps li.is-done .settings-setup-step-state {
  color: var(--ok);
}

/* The access key reads everybody's tasks: whose each one is. */
.run-owner {
  font-size: var(--fs-small);
  color: var(--muted);
}

/* --- The access-key notice (SPEC 6.8) ------------------------------------ */
.access-key-notice {
  flex: none;
  margin: 0.75rem 1rem 0;
  padding: 0.65rem 0.9rem;
  font-size: var(--fs-small);
  line-height: 1.45;
  color: var(--ink);
  border: 1px solid color-mix(in srgb, var(--signal) 40%, var(--line));
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--signal) 10%, var(--bg-elevated));
}

/* --- Sign-in and invite (SPEC 6.8) ---------------------------------------
   The dialog itself has no padding (only its forms do), so the wordmark,
   the expired-session notice and the access-key disclosure get their own
   and no longer sit flush against the border. */
.auth-dialog {
  max-width: 27rem;
}

.auth-brand {
  padding: 1.6rem 1.5rem 0;
  color: var(--ink);
}

.auth-wordmark {
  display: block;
  width: auto;
  height: 1.55rem;
}

.auth-dialog .auth-notice {
  margin: 1rem 1.5rem 0;
}

.auth-dialog > form {
  padding: 1.1rem 1.5rem 1.25rem;
}

.auth-dialog h2 {
  font-size: var(--fs-empty-title, 1.35rem);
}

.auth-dialog input[readonly] {
  background: var(--bright);
}

.auth-dialog .auth-show-password {
  min-height: var(--target-min);
  margin: -0.4rem 0 -0.2rem;
  font-size: var(--fs-small);
  font-weight: 500;
  cursor: pointer;
}

.auth-dialog .auth-show-password input {
  width: 1.15rem !important;
  height: 1.15rem;
  flex-shrink: 0;
  margin: 0;
  padding: 0;
  accent-color: var(--accent);
}

.auth-dialog .dialog-actions .btn.primary {
  flex: 1 1 auto;
  min-height: var(--target-min);
}

.auth-forgot {
  line-height: 1.45;
}

.auth-token-fallback {
  margin: 0 1.5rem 1.25rem;
  padding-top: 0.35rem;
  border-top: 1px solid var(--line);
}

.auth-token-fallback > summary {
  display: flex;
  align-items: center;
  min-height: var(--target-min);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
}

.auth-token-fallback[open] > summary {
  color: var(--ink);
}

.auth-token-fallback > form {
  padding: 0.35rem 0 0;
}

/* --- Setup wizard (SPEC 6.8) ---------------------------------------------
   Fits 375px: every grid track may shrink (minmax(0, 1fr)), and phones get
   «Schritt n von m» with a bar instead of the stepper. */
#view-setup.layout {
  grid-template-columns: minmax(0, 1fr);
}

.setup-wizard,
.setup-card,
.setup-step-panel {
  min-width: 0;
}

.setup-progress {
  display: none;
  flex: none;
  height: 0.4rem;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}

.setup-progress-bar {
  display: block;
  width: 0;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
  transition: width var(--dur, 0.3s) var(--ease);
}

.setup-stepper button {
  font-size: var(--fs-small);
  min-height: var(--target-min);
}

/* --bright is a light surface since the overhaul's tokens, so the stepper's
   old white-on-tint labels vanished in the light theme. Ink on the tint,
   and the tick in the done colour. */
.setup-stepper button[aria-current="step"] {
  color: var(--accent-on-tint);
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-elevated));
}

.setup-stepper button.is-done {
  color: var(--ink);
}

.setup-stepper button.is-done::before {
  color: var(--ok);
}

.setup-eyebrow {
  color: var(--accent-text);
}

.setup-card-head p {
  line-height: 1.5;
}

/* Which AI subscription: three labelled choices, each a large target. */
.setup-ai-choices {
  margin: 0 0 1.25rem;
  padding: 0;
  border: 0;
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  gap: 0.6rem;
}

.setup-ai-choices legend {
  margin-bottom: 0.6rem;
  padding: 0;
  font-weight: 700;
}

.setup-ai-choice {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-height: var(--row-min);
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
}

.setup-ai-choice input {
  width: 1.2rem;
  height: 1.2rem;
  margin: 0;
  flex-shrink: 0;
  accent-color: var(--accent);
}

.setup-ai-choice:has(input:checked) {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
}

.setup-ai-choice:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.setup-ai-choice-title {
  font-weight: 600;
}

.setup-ai-which-hint {
  grid-column: 1 / -1;
}

/* The numbered instructions. The number is drawn, the text is read. */
.setup-login-steps {
  list-style: none;
  counter-reset: setup-login;
  margin: 0 0 1.1rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.setup-login-step {
  counter-increment: setup-login;
  position: relative;
  min-width: 0;
  padding-left: 2.6rem;
}

.setup-login-step::before {
  content: counter(setup-login);
  position: absolute;
  left: 0;
  top: -0.1rem;
  display: grid;
  place-items: center;
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--accent-text);
  background: color-mix(in srgb, var(--accent) 12%, var(--bg-elevated));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}

.setup-login-step-text {
  display: block;
  line-height: 1.6;
}

.setup-login-open {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
  margin-top: 0.55rem;
}

.setup-login-open .btn {
  min-height: 3rem;
  padding-inline: 1.3rem;
  font-size: 1.05rem;
}

.setup-device-code {
  display: inline-block;
  margin: 0.55rem 0 0;
  padding: 0.5rem 0.9rem;
  font-size: 1.6rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  user-select: all;
}

.setup-code-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: 0.35rem 0.6rem;
  margin-top: 0.6rem;
  max-width: 34rem;
}

.setup-code-label {
  grid-column: 1 / -1;
  font-size: var(--fs-small);
  font-weight: 600;
}

.setup-code-input {
  box-sizing: border-box;
  width: 100%;
  min-height: 3.25rem;
  padding: 0.6rem 0.85rem;
  font-family: var(--font-mono);
  font-size: 1.25rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
}

.setup-code-row .btn {
  min-height: 3.25rem;
}

/* The raw output of the sign-in, for whoever needs it. */
.setup-terminal-details {
  border-top: 1px solid var(--line);
  padding-top: 0.35rem;
}

.setup-terminal-details > summary {
  display: flex;
  align-items: center;
  min-height: var(--target-min);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
}

.setup-terminal-details[open] > summary {
  color: var(--ink);
  margin-bottom: 0.4rem;
}

.setup-terminal-details .hint {
  margin-bottom: 0.55rem;
}

.setup-terminal-input-row {
  grid-template-columns: minmax(0, 1fr) auto;
}

.setup-card-foot .btn {
  min-height: var(--target-min);
}

@media (max-width: 820px) {
  .settings-search {
    max-width: none;
  }

  .settings-detail .settings-language-label,
  .settings-segmented,
  .settings-mode-hint,
  .settings-setup-steps {
    max-width: none;
  }

  .subscription-list .subscription-row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .subscription-list .subscription-row .secret-actions {
    grid-column: 1 / -1;
    justify-content: flex-start;
  }

  .auth-dialog {
    width: calc(100% - 1.5rem);
  }

  .auth-brand {
    padding: 1.3rem 1.2rem 0;
  }

  .auth-dialog .auth-notice {
    margin-inline: 1.2rem;
  }

  .auth-dialog > form {
    padding: 1rem 1.2rem 1.2rem;
  }

  .auth-token-fallback {
    margin: 0 1.2rem 1.2rem;
  }

  #view-setup.layout {
    padding: 0.9rem 1rem 2rem;
  }

  .setup-wizard {
    gap: 0.9rem;
  }

  .setup-wizard-head {
    flex-direction: column;
    gap: 0.6rem;
  }

  .setup-wizard-head h2 {
    font-size: 1.5rem;
  }

  .setup-stepper {
    display: none;
  }

  .setup-progress {
    display: block;
  }

  .setup-card {
    padding: 1rem;
    gap: 1rem;
  }

  .setup-login-step {
    padding-left: 2.35rem;
  }

  .setup-login-open,
  .setup-login-open .btn {
    width: 100%;
  }

  .setup-login-open .btn {
    justify-content: center;
  }

  .setup-code-row {
    grid-template-columns: minmax(0, 1fr);
    max-width: none;
  }

  .setup-code-row .btn {
    width: 100%;
  }

  /* The step's main action spans the width at the bottom; the ghost
     actions sit above it. */
  .setup-card-foot {
    flex-direction: column;
    align-items: stretch;
  }

  .setup-card-foot .btn {
    width: 100%;
    justify-content: center;
    order: 1;
  }

  .setup-card-foot .btn.primary {
    order: 2;
  }
}

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

/* === liquid-glass === */
/* Liquid Glass, Atlas Web's default look (ClaudeOS#1036, decided by Michael
   on 2026-10-01). Translucent, blurred material with a rim light; chrome
   that floats over the content; capsule controls; items tinted by state.

   «Klassisch» is everything above this line, untouched. Every rule here is
   scoped under html[data-ui-style="glass"], which the <head> pre-paint sets
   before the first frame and applyUiStyle() is the only writer of, so the
   classic look can never pick up a glass rule (tests/test_web_ui_style.py).

   Rules of the section:
   - Every colour is a token in the two glass token blocks directly below;
     component rules read tokens only (tests/test_web_css_lint.py names the
     two blocks in TOKEN_SELECTORS).
   - backdrop-filter only where content really moves behind a surface AND
     the blur reaches it, each with its -webkit- twin. A phone screen keeps
     to at most four blurred layers: the top edge and the tab bar (an open
     task's head, reply dock and chat arrows are opaque: their blur never
     reached the chat); with a sheet open, only the sheet and its scrim (the
     page under the scrim drops its blur).
   - Text reaches 4.5:1 against the rendered glass, measured on screenshots
     in both themes; the tints and alphas below are what that measurement
     settled on. Make glass more transparent only with a new measurement.
   - Three fallbacks at the end turn the material opaque: no backdrop-filter
     support, prefers-reduced-transparency and prefers-contrast: more. */

/* --- Tokens: light -------------------------------------------------------- */
html[data-ui-style="glass"] {
  /* Secondary text on a translucent material gets one step more weight
     (Apple's "vibrancy"): #5b6b7c needs a ground of L >= 0.81 for 4.5:1,
     this one needs L >= 0.56, which leaves the atmosphere room for colour
     (measured: #4f5e6e fell to 4.0:1 where a hint sits on the amber orb). */
  --glass-muted: #465462;
  --muted: var(--glass-muted);
  /* Secondary text on the open view's lifted lens (the phone menu's row). */
  --glass-muted-lens: var(--glass-muted);

  --glass-tint: var(--surface);
  --glass-tint-deep: var(--bg-elevated);
  --glass-alpha: 58%;          /* panes and cards */
  --glass-alpha-chrome: 62%;   /* sidebar, tab bar, floating bars */
  --glass-alpha-overlay: 76%;  /* dialogs, drawer, toasts */
  --glass-alpha-thin: 50%;     /* capsules that sit on glass */
  --glass-alpha-field: 74%;    /* where a person types */
  --glass-alpha-edge: 70%;     /* the phone's scroll edge at its top */
  --glass-alpha-dock: 82%;     /* a bar that text scrolls under */
  --glass-alpha-bar: 80%;      /* the phone's tab bar: labels over moving text */
  /* Opaque: nothing blurs behind these two, so any transparency only let
     the text behind them through as ghost letters (measured 1.06-1.10:1
     under a toast at 97 %, 1.05:1 under a band at 98 %). The sheen and the
     rim keep them glass. */
  --glass-alpha-toast: 100%;   /* a toast: the top layer does not blur for it */
  --glass-alpha-band: 100%;    /* a pinned band inside a dialog: no blur there either */
  /* An open task's head and reply dock, and the chat's scroll buttons: they
     sit inside the scrolling pane, and their blur never reached the
     conversation under them (measured: turning it off changed 0.5-7.9 % of
     their pixels, the phone's top edge 28.5 %), so the chat showed through
     as sharp ghost letters. Opaque, they hide it and cost no blur pass. */
  --glass-alpha-pinned: 100%;

  --glass-bg: color-mix(in srgb, var(--glass-tint) var(--glass-alpha), transparent);
  --glass-bg-chrome: color-mix(in srgb, var(--glass-tint) var(--glass-alpha-chrome), transparent);
  --glass-bg-overlay: color-mix(in srgb, var(--glass-tint-deep) var(--glass-alpha-overlay), transparent);
  --glass-bg-thin: color-mix(in srgb, var(--glass-tint) var(--glass-alpha-thin), transparent);
  --glass-bg-field: color-mix(in srgb, var(--glass-tint) var(--glass-alpha-field), transparent);
  --glass-bg-edge: color-mix(in srgb, var(--glass-tint-deep) var(--glass-alpha-edge), transparent);
  --glass-bg-dock: color-mix(in srgb, var(--glass-tint-deep) var(--glass-alpha-dock), transparent);
  --glass-bg-bar: color-mix(in srgb, var(--glass-tint) var(--glass-alpha-bar), transparent);
  --glass-bg-toast: color-mix(in srgb, var(--glass-tint-deep) var(--glass-alpha-toast), transparent);
  --glass-bg-band: color-mix(in srgb, var(--glass-tint-deep) var(--glass-alpha-band), transparent);
  --glass-bg-pinned: color-mix(in srgb, var(--glass-tint-deep) var(--glass-alpha-pinned), transparent);

  /* The material: blur, a strong saturation lift (the colour behind glows
     through, as it does in real glass), a little extra light. */
  --glass-filter: blur(22px) saturate(210%) brightness(1.06);
  --glass-filter-chrome: blur(28px) saturate(220%) brightness(1.08);
  --glass-filter-overlay: blur(38px) saturate(190%) brightness(1.1);
  --glass-filter-edge: blur(14px) saturate(190%) brightness(1.06);
  --glass-filter-scrim: blur(10px) saturate(140%);

  /* Sheen: light pools at the top, a thinner band gathers at the bottom
     edge where the curved surface bends it back (the refraction look). */
  --glass-sheen-top: rgba(255, 255, 255, 0.62);
  --glass-sheen-clear: rgba(255, 255, 255, 0);
  --glass-sheen-low: rgba(255, 255, 255, 0.3);
  --glass-sheen: linear-gradient(180deg, var(--glass-sheen-top) 0%, var(--glass-sheen-clear) 38%, var(--glass-sheen-clear) 82%, var(--glass-sheen-low) 100%);
  --glass-sheen-ctl: linear-gradient(180deg, var(--glass-sheen-top) 0%, var(--glass-sheen-clear) 55%, var(--glass-sheen-clear) 85%, var(--glass-sheen-low) 100%);
  /* On an accent fill the sheen stays faint: the label's contrast. */
  --glass-sheen-accent: linear-gradient(180deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 40%, rgba(255, 255, 255, 0) 84%, rgba(255, 255, 255, 0.08) 100%);

  /* The inner light: a specular line at the top, a softer one at the
     bottom, an inner glow that lifts the edges. */
  --glass-spec: rgba(255, 255, 255, 1);
  --glass-spec-low: rgba(255, 255, 255, 0.6);
  --glass-glow: rgba(255, 255, 255, 0.75);
  --glass-inner: inset 0 1px 0 0 var(--glass-spec), inset 0 -1px 0 0 var(--glass-spec-low), inset 0 0 24px -6px var(--glass-glow);
  --glass-inner-ctl: inset 0 1px 0 0 var(--glass-spec), inset 0 -1px 0 0 var(--glass-spec-low), inset 0 0 12px -4px var(--glass-glow);
  --glass-inner-accent: inset 0 1px 0 0 rgba(255, 255, 255, 0.42), inset 0 -1px 0 0 rgba(60, 0, 25, 0.22), inset 0 0 14px -4px rgba(255, 255, 255, 0.28);

  /* The rim, drawn by a masked pseudo-element on boxes that do not scroll:
     lit top left, shaded at the far side, lit again bottom right. */
  --glass-rim-hi: rgba(255, 255, 255, 1);
  --glass-rim-mid: rgba(255, 255, 255, 0.6);
  --glass-rim-lo: rgba(255, 255, 255, 0.18);
  --glass-rim-shade: rgba(31, 22, 52, 0.16);
  --glass-rim: linear-gradient(135deg, var(--glass-rim-hi) 0%, var(--glass-rim-mid) 14%, var(--glass-rim-lo) 38%, var(--glass-rim-shade) 66%, var(--glass-rim-mid) 92%, var(--glass-rim-hi) 100%);
  /* The chrome's rim splits the light: amber where it is lit, violet on the
     far edge -- the brand ramp, at a whisper. */
  --glass-disp-a: rgba(255, 194, 74, 0.75);
  --glass-disp-b: rgba(168, 85, 247, 0.5);
  --glass-rim-spectral: linear-gradient(135deg, var(--glass-rim-hi) 0%, var(--glass-disp-a) 16%, var(--glass-rim-lo) 40%, var(--glass-rim-shade) 64%, var(--glass-disp-b) 88%, var(--glass-rim-hi) 100%);

  /* Edges on boxes that scroll (border colours, no pseudo-element). */
  --glass-line-top: rgba(255, 255, 255, 0.95);
  --glass-line-side: rgba(255, 255, 255, 0.5);
  --glass-line-bottom: rgba(31, 22, 52, 0.12);
  /* Capsule controls: bevelled, and the bottom edge keeps the outline a
     person can find (control-line, nearly full strength). */
  --glass-ctl-top: rgba(255, 255, 255, 0.95);
  --glass-ctl-side: color-mix(in srgb, var(--control-line) 42%, transparent);
  --glass-ctl-bottom: var(--control-line);
  /* Where a person types or chooses (the Home field, the task search, the
     segmented controls) the edge is the same on all four sides and holds
     3:1 against what is around it. The bevel's white top and faint sides
     vanished into a light page (1.03-1.86:1 measured), and --control-line
     itself fell to 2.75:1 where the orbs darken the atmosphere. */
  --glass-ctl-edge: #536272;

  /* Focus: one two-tone ring on every glass control. A dark ring with a
     white halo between it and the control; whatever the backdrop (light
     glass, an accent glow, the ink lens of a pressed segment), one of the
     two holds 3:1 against it -- with white at L 1.0 the ring has to stay
     under L 0.067 for that, and this one is L 0.055. */
  --glass-focus-ring: #7d1a41;
  --glass-focus-halo: var(--surface);

  /* A live microphone: the danger edge and a tint, as in «Klassisch». */
  --glass-rec-tint: color-mix(in srgb, var(--danger) 12%, transparent);

  /* Depth. The drop shadow carries a trace of violet ink, not grey. */
  --glass-outline: rgba(31, 22, 52, 0.08);
  --glass-drop: rgba(46, 22, 70, 0.24);
  --glass-drop-deep: rgba(46, 22, 70, 0.34);
  --glass-shadow: 0 0 0 1px var(--glass-outline), 0 1px 2px rgba(15, 23, 32, 0.06), 0 16px 36px -14px var(--glass-drop);
  --glass-shadow-float: 0 0 0 1px var(--glass-outline), 0 3px 8px rgba(15, 23, 32, 0.07), 0 28px 60px -18px var(--glass-drop-deep);
  --glass-shadow-ctl: 0 1px 2px rgba(15, 23, 32, 0.08), 0 6px 14px -8px var(--glass-drop);
  --glass-shadow-accent: 0 8px 22px -8px color-mix(in srgb, var(--accent) 70%, transparent), 0 2px 5px -1px color-mix(in srgb, var(--accent) 35%, transparent);

  /* States on glass. */
  --glass-hover: rgba(255, 255, 255, 0.55);
  --glass-lift: rgba(255, 255, 255, 0.92);
  --glass-lift-accent: color-mix(in srgb, var(--accent-bright) 15%, var(--glass-lift));
  --glass-pressed: color-mix(in srgb, var(--ink) 90%, transparent);
  --glass-pressed-ink: var(--bg-elevated);
  --glass-scrim: rgba(24, 16, 40, 0.22);

  /* State-tinted glass. Amber: something waits on the person. Coral: it
     went wrong. Always next to the words that say so. */
  --glass-ask-bg: color-mix(in srgb, var(--spectrum-1) 20%, var(--glass-bg));
  --glass-ask-thin: color-mix(in srgb, var(--spectrum-1) 16%, var(--glass-bg-thin));
  --glass-ask-edge: color-mix(in srgb, var(--ask) 70%, transparent);
  --glass-ask-glow: rgba(255, 194, 74, 0.55);
  --glass-fail-bg: color-mix(in srgb, var(--spectrum-2) 15%, var(--glass-bg));
  --glass-fail-thin: color-mix(in srgb, var(--spectrum-2) 12%, var(--glass-bg-thin));
  --glass-fail-edge: color-mix(in srgb, var(--fail) 70%, transparent);
  --glass-run-thin: color-mix(in srgb, var(--signal) 8%, var(--glass-bg-thin));
  --glass-ok-edge: color-mix(in srgb, var(--ok) 70%, transparent);
  --glass-run-edge: color-mix(in srgb, var(--run) 70%, transparent);
  --glass-user-tint: 14%;

  /* Shape: concentric. A row inside a panel is the panel's radius minus its
     inset; every control is a capsule. */
  --glass-gap: 0.75rem;
  --glass-r-shell: 28px;
  --glass-r-card: 24px;
  --glass-r-inner: 16px;
  --glass-r-field: 22px;
  --glass-r-sheet: 32px;
  --glass-r-pill: 999px;

  /* Atmosphere: the whole spectrum ramp. Kept to light, high-luminance
     tints where text sits directly on it (the top of the content column). */
  /* The orbs stop short of what the muted text on them can take (measured:
     a hint on the strongest pink and amber fell to 3.9:1 at 0.32 / 0.5). */
  --orb-a: rgba(242, 84, 125, 0.26);
  --orb-b: rgba(255, 194, 74, 0.42);
  --glass-orb-c: rgba(168, 85, 247, 0.3);
  --glass-orb-d: rgba(255, 122, 89, 0.3);
  --glass-orb-e: rgba(242, 84, 125, 0.22);
  --atmosphere-accent: rgba(242, 84, 125, 0.16);
  --atmosphere-warm: rgba(255, 194, 74, 0.26);
  --atmosphere-top: #f6f3fb;
  --atmosphere-bottom: #e6e1f2;
  --glass-grid-opacity: 0.45;
}

/* --- Tokens: dark --------------------------------------------------------- */
html[data-ui-style="glass"][data-theme="dark"] {
  --glass-muted: #adb9c7;
  --muted: var(--glass-muted);
  /* Halfway to --ink: the open row's description in the phone menu sat at
     4.2:1 on the lens's accent-tinted lift with --glass-muted. */
  --glass-muted-lens: #cbd4de;
  /* Dark glass is a little lighter than the classic surfaces where an orb
     glows through it: accent links fell to 4.1:1 at #F2547D. */
  --glass-accent-text: #ff7aa0;
  --accent-text: var(--glass-accent-text);

  --glass-tint: var(--bg-elevated);
  --glass-tint-deep: var(--bg-elevated);
  --glass-alpha: 56%;
  --glass-alpha-chrome: 68%;
  --glass-alpha-overlay: 74%;
  --glass-alpha-thin: 46%;
  --glass-alpha-field: 70%;
  --glass-alpha-edge: 72%;
  --glass-alpha-dock: 84%;
  --glass-alpha-bar: 82%;
  --glass-alpha-toast: 100%;
  --glass-alpha-band: 100%;

  --glass-filter: blur(22px) saturate(180%) brightness(0.86);
  --glass-filter-chrome: blur(28px) saturate(190%) brightness(0.84);
  --glass-filter-overlay: blur(38px) saturate(160%) brightness(0.78);
  --glass-filter-edge: blur(14px) saturate(170%) brightness(0.85);
  --glass-filter-scrim: blur(10px) saturate(120%) brightness(0.9);

  --glass-sheen-top: rgba(255, 255, 255, 0.09);
  --glass-sheen-clear: rgba(255, 255, 255, 0);
  --glass-sheen-low: rgba(255, 255, 255, 0.05);
  --glass-sheen-accent: linear-gradient(180deg, rgba(255, 255, 255, 0.22) 0%, rgba(255, 255, 255, 0) 50%, rgba(255, 255, 255, 0) 84%, rgba(255, 255, 255, 0.1) 100%);

  --glass-spec: rgba(255, 255, 255, 0.2);
  --glass-spec-low: rgba(255, 255, 255, 0.06);
  --glass-glow: rgba(255, 255, 255, 0.07);
  --glass-inner-accent: inset 0 1px 0 0 rgba(255, 255, 255, 0.45), inset 0 -1px 0 0 rgba(40, 0, 18, 0.3), inset 0 0 14px -4px rgba(255, 255, 255, 0.25);

  --glass-rim-hi: rgba(255, 255, 255, 0.42);
  --glass-rim-mid: rgba(255, 255, 255, 0.16);
  --glass-rim-lo: rgba(255, 255, 255, 0.05);
  --glass-rim-shade: rgba(0, 0, 0, 0.3);
  --glass-disp-a: rgba(255, 194, 74, 0.42);
  --glass-disp-b: rgba(168, 85, 247, 0.5);

  --glass-line-top: rgba(255, 255, 255, 0.2);
  --glass-line-side: rgba(255, 255, 255, 0.08);
  --glass-line-bottom: rgba(0, 0, 0, 0.35);
  --glass-ctl-top: rgba(255, 255, 255, 0.24);
  --glass-ctl-side: color-mix(in srgb, var(--control-line) 45%, transparent);
  --glass-ctl-bottom: color-mix(in srgb, var(--control-line) 85%, transparent);
  --glass-ctl-edge: #8394ab;

  /* The light ring with a dark halo: against the near-black halo the ring
     needs L >= 0.44 for one of the two to hold 3:1 on any backdrop (the
     lit rim of a card, the amber of a waiting row, the light lens of a
     pressed segment); this pink is L 0.52. */
  --glass-focus-ring: #ffa6c1;
  --glass-focus-halo: var(--bg);
  --glass-rec-tint: color-mix(in srgb, var(--danger) 14%, transparent);

  --glass-outline: rgba(0, 0, 0, 0.4);
  --glass-drop: rgba(0, 0, 0, 0.55);
  --glass-drop-deep: rgba(0, 0, 0, 0.7);
  --glass-shadow: 0 0 0 1px var(--glass-outline), 0 1px 2px rgba(0, 0, 0, 0.3), 0 18px 40px -14px var(--glass-drop);
  --glass-shadow-float: 0 0 0 1px var(--glass-outline), 0 3px 10px rgba(0, 0, 0, 0.35), 0 30px 64px -18px var(--glass-drop-deep);
  --glass-shadow-ctl: 0 1px 2px rgba(0, 0, 0, 0.35), 0 6px 14px -8px var(--glass-drop);
  --glass-shadow-accent: 0 8px 24px -8px color-mix(in srgb, var(--accent-bright) 55%, transparent), 0 2px 5px -1px rgba(0, 0, 0, 0.4);

  /* Kept dim: a selected row's secondary text has to hold 4.5:1 on it, and
     the accent bar beside it says "selected" (measured 2.7:1 at 13%). */
  --glass-hover: rgba(255, 255, 255, 0.05);
  --glass-lift: rgba(255, 255, 255, 0.08);
  --glass-lift-accent: color-mix(in srgb, var(--accent-bright) 8%, var(--glass-lift));
  --glass-pressed: color-mix(in srgb, var(--ink) 92%, transparent);
  --glass-pressed-ink: var(--bg);
  --glass-scrim: rgba(0, 0, 0, 0.45);

  /* Amber kept low: accent links in a question card fell to 3.7:1 at 12%. */
  --glass-ask-bg: color-mix(in srgb, var(--spectrum-1) 8%, var(--glass-bg));
  --glass-ask-thin: color-mix(in srgb, var(--spectrum-1) 7%, var(--glass-bg-thin));
  --glass-ask-edge: color-mix(in srgb, var(--ask) 60%, transparent);
  --glass-ask-glow: rgba(255, 194, 74, 0.12);
  --glass-fail-bg: color-mix(in srgb, var(--spectrum-2) 12%, var(--glass-bg));
  --glass-fail-thin: color-mix(in srgb, var(--spectrum-2) 10%, var(--glass-bg-thin));
  --glass-fail-edge: color-mix(in srgb, var(--fail) 60%, transparent);
  --glass-run-thin: color-mix(in srgb, var(--signal) 8%, var(--glass-bg-thin));
  --glass-ok-edge: color-mix(in srgb, var(--ok) 60%, transparent);
  --glass-run-edge: color-mix(in srgb, var(--run) 60%, transparent);
  --glass-user-tint: 6%;

  --orb-a: rgba(242, 84, 125, 0.34);
  --orb-b: rgba(255, 194, 74, 0.16);
  --glass-orb-c: rgba(168, 85, 247, 0.36);
  --glass-orb-d: rgba(255, 122, 89, 0.18);
  --glass-orb-e: rgba(242, 84, 125, 0.2);
  --atmosphere-accent: rgba(242, 84, 125, 0.18);
  --atmosphere-warm: rgba(255, 194, 74, 0.1);
  --atmosphere-top: #120f1c;
  --atmosphere-bottom: #07080c;
  --glass-grid-opacity: 0.5;
}

/* --- Atmosphere ------------------------------------------------------------
   Full-bleed: the sidebar and the bars are glass and float over it. */
html[data-ui-style="glass"] body {
  background: transparent;
}

html[data-ui-style="glass"] .atmosphere {
  inset: 0;
}

html[data-ui-style="glass"] .atmosphere .grid {
  opacity: var(--glass-grid-opacity);
}

html[data-ui-style="glass"] .atmosphere .orb {
  opacity: 0.9;
  filter: blur(46px);
}

html[data-ui-style="glass"] .orb-a {
  width: 54vw;
  height: 54vw;
  max-width: 720px;
  max-height: 720px;
}

html[data-ui-style="glass"] .orb-b {
  width: 48vw;
  height: 48vw;
  max-width: 640px;
  max-height: 640px;
}

html[data-ui-style="glass"] .atmosphere::before,
html[data-ui-style="glass"] .atmosphere::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  filter: blur(54px);
  animation: drift 26s var(--ease) infinite alternate;
}

/* Violet low on the left, coral on the right: the ramp's other two stops,
   so a card lower down the page still has colour behind it. */
html[data-ui-style="glass"] .atmosphere::before {
  width: 64vw;
  height: 64vw;
  max-width: 860px;
  max-height: 860px;
  left: -12%;
  bottom: -30%;
  background: radial-gradient(circle at 40% 40%, var(--glass-orb-c), transparent 62%);
}

html[data-ui-style="glass"] .atmosphere::after {
  width: 52vw;
  height: 52vw;
  max-width: 700px;
  max-height: 700px;
  right: -14%;
  top: 34%;
  background:
    radial-gradient(circle at 50% 50%, var(--glass-orb-d), transparent 60%),
    radial-gradient(circle at 10% 90%, var(--glass-orb-e), transparent 55%);
  animation-delay: -9s;
  animation-duration: 30s;
}

/* --- The rim, shared -------------------------------------------------------
   A gradient edge from a masked pseudo-element (border-image cannot follow
   a radius). Only on boxes that do not scroll themselves and have no
   ::after of their own. */
html[data-ui-style="glass"] .home-section,
html[data-ui-style="glass"] .home-pane .dash-setup,
html[data-ui-style="glass"] .settings-tile,
html[data-ui-style="glass"] .detail-pane .detail-head,
html[data-ui-style="glass"] .tabbar {
  border-color: transparent;
}

html[data-ui-style="glass"] .home-section,
html[data-ui-style="glass"] .home-pane .dash-setup,
html[data-ui-style="glass"] .settings-tile {
  position: relative;
}

html[data-ui-style="glass"] .home-section::after,
html[data-ui-style="glass"] .home-pane .dash-setup::after,
html[data-ui-style="glass"] .settings-tile::after,
html[data-ui-style="glass"] .detail-pane .detail-head::after,
html[data-ui-style="glass"] .tabbar::after {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: 0;
  padding: 1px;
  border-radius: inherit;
  background: var(--glass-rim);
  -webkit-mask: linear-gradient(var(--mask-solid) 0 0) content-box, linear-gradient(var(--mask-solid) 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(var(--mask-solid) 0 0) content-box exclude, linear-gradient(var(--mask-solid) 0 0);
  pointer-events: none;
}

/* The chrome splits the light. */
html[data-ui-style="glass"] .tabbar::after,
html[data-ui-style="glass"] .detail-pane .detail-head::after {
  background: var(--glass-rim-spectral);
}

/* --- Controls: glass capsules ---------------------------------------------- */
html[data-ui-style="glass"] .btn {
  border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
  border-radius: var(--glass-r-pill);
  background: var(--glass-sheen-ctl), var(--glass-bg-thin);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .btn:hover {
  border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
  background: var(--glass-sheen-ctl), var(--glass-lift);
}

/* Ghost: clear glass -- the bevel and the lit edge, no body, no drop. */
html[data-ui-style="glass"] .btn.ghost {
  border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
  background: var(--glass-sheen-ctl), transparent;
  box-shadow: var(--glass-inner-ctl);
}

html[data-ui-style="glass"] .btn.ghost:hover {
  background: var(--glass-sheen-ctl), var(--glass-hover);
}

html[data-ui-style="glass"] .btn.icon,
html[data-ui-style="glass"] .dash-setup-dismiss {
  border-radius: 50%;
}

/* Primary: a tinted glass drop. The fill stays solid accent under a faint
   sheen (the label's 4.5:1 depends on it); the rim and the glow do the rest. */
html[data-ui-style="glass"] .btn.primary,
html[data-ui-style="glass"] .btn.primary:hover {
  border-color: transparent;
  background: var(--glass-sheen-accent), var(--accent);
  box-shadow: var(--glass-inner-accent), var(--glass-shadow-accent);
}

html[data-ui-style="glass"] .btn.primary:hover {
  filter: brightness(1.06) saturate(1.08);
}

html[data-ui-style="glass"] .btn.danger {
  background: var(--glass-sheen-ctl), var(--glass-fail-thin);
}

html[data-ui-style="glass"] .chip {
  border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
  background: var(--glass-sheen-ctl), var(--glass-bg-thin);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .chip:hover {
  border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
  background: var(--glass-sheen-ctl), var(--glass-lift);
}

html[data-ui-style="glass"] .home-example-routine {
  border-style: dashed;
  border-color: var(--glass-ctl-bottom);
}

/* Pressed: smoked glass -- dark in light, light in dark -- plus the shape,
   never the colour alone (aria-pressed carries it too). */
html[data-ui-style="glass"] .chip[aria-pressed="true"],
html[data-ui-style="glass"] .chip[aria-pressed="true"]:hover {
  border-color: transparent;
  background: var(--glass-sheen-accent), var(--glass-pressed);
  color: var(--glass-pressed-ink);
  box-shadow: var(--glass-inner-accent), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .status-pill {
  border-radius: var(--glass-r-pill);
  padding: 0.2rem 0.6rem;
}

/* Segmented controls (List / Board, the view, the text size, «Stil»): one
   glass capsule, the chosen segment a lifted lens inside it. */
html[data-ui-style="glass"] .segmented-options {
  gap: 2px;
  padding: 3px;
  border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
  border-radius: var(--glass-r-pill);
  background: var(--glass-sheen-ctl), var(--glass-bg-thin);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .segmented-options label {
  border-radius: var(--glass-r-pill);
}

html[data-ui-style="glass"] .segmented-options label + label {
  border-left-color: transparent;
}

html[data-ui-style="glass"] .segmented-options label:hover {
  background: var(--glass-hover);
}

html[data-ui-style="glass"] .segmented-options label:has(input:checked) {
  background: var(--glass-sheen-accent), var(--glass-pressed);
  color: var(--glass-pressed-ink);
  box-shadow: var(--glass-inner-accent), var(--glass-shadow-ctl);
}

/* Fields: glass, still outlined in --control-line (3:1). */
html[data-ui-style="glass"] .runs-search,
html[data-ui-style="glass"] dialog input:not([type="checkbox"]):not([type="radio"]),
html[data-ui-style="glass"] dialog select,
html[data-ui-style="glass"] dialog textarea,
html[data-ui-style="glass"] .send-row textarea,
html[data-ui-style="glass"] .nav-drawer-field select {
  border-radius: var(--glass-r-inner);
  background: var(--glass-bg-field);
  box-shadow: inset 0 1px 2px var(--glass-outline);
}

html[data-ui-style="glass"] .runs-search {
  border-radius: var(--glass-r-pill);
  padding-left: 1.1rem;
}

/* The fields and segmented controls a person types into or chooses from:
   one edge, the same on all four sides (the bevel above is for buttons). */
html[data-ui-style="glass"] .runs-search,
html[data-ui-style="glass"] .home-field textarea,
html[data-ui-style="glass"] .segmented-options {
  border-color: var(--glass-ctl-edge);
}

/* --- Focus: one two-tone ring ----------------------------------------------
   The classic accent ring sat on whatever the glass let through: the pink
   glow under a primary, the dark ink lens of a pressed segment, a card's
   lit rim in dark (1.6-2.9:1 measured). In glass every focus ring is the
   ring token with a halo of the opposite lightness right inside it, in
   the gap the 2px offset leaves; one of the two holds 3:1 against any
   backdrop (see the tokens). The halo replaces the control's own shadows
   while it has focus -- the accent glow included, which was what lightened
   the backdrop -- so it is !important against every glass box-shadow rule
   that names the control more specifically. The textarea of a reply field
   is excluded: its frame (.send-field) shows the ring for it. */
html[data-ui-style="glass"] :focus-visible:not(.send-row textarea),
html[data-ui-style="glass"] .send-field:focus-within,
html[data-ui-style="glass"] .file-drop > input:focus-visible + .file-drop-zone,
html[data-ui-style="glass"] .setup-ai-choice:has(input:focus-visible) {
  outline-color: var(--glass-focus-ring);
  box-shadow: 0 0 0 2px var(--glass-focus-halo) !important;
}

/* Rings the classic rules draw inside the edge (a scroller would clip
   them): the halo goes inside the ring as well, as far in as the ring's
   offset plus its own 2px. */
html[data-ui-style="glass"] :is(.tab, .run-list li button, .skill-select, .issue-select, .project-select, .nav-drawer .view-tab, .app-content, .run-card, .schedule-menu [role="menuitem"], .artifacts-pane a.artifact-row):focus-visible {
  box-shadow: inset 0 0 0 4px var(--glass-focus-halo) !important;
}

html[data-ui-style="glass"] .tabbar-item:focus-visible {
  box-shadow: inset 0 0 0 5px var(--glass-focus-halo) !important;
}

html[data-ui-style="glass"] .segmented-options label:has(input:focus-visible) {
  outline-color: var(--glass-focus-ring);
  box-shadow: inset 0 0 0 6px var(--glass-focus-halo) !important;
}

/* A row tabbed into at the foot of a view stopped flush with the edge, and
   the 4px ring under it was cut off. (Phones need far more: see below.) */
html[data-ui-style="glass"] .layout {
  scroll-padding-block: 0.75rem;
}

/* The sidebar's rows are 2px apart and the ring reaches 4px out: the next
   row's lens, painted later, covered the bottom of the ring above it. */
html[data-ui-style="glass"] .view-tab:focus-visible {
  z-index: 1;
}

/* A live microphone keeps its look on glass: the clear capsule's white
   bevel had replaced the danger edge, so only the icon's colour said it. */
html[data-ui-style="glass"] .btn.dictate-btn[data-state="recording"],
html[data-ui-style="glass"] .btn.dictate-btn[data-state="recording"]:hover {
  border-color: var(--danger);
  background: var(--glass-sheen-ctl), var(--glass-rec-tint);
}

/* --- Desktop shell: the sidebar floats ------------------------------------ */
@media (min-width: 821px) {
  html[data-ui-style="glass"] .top {
    inset: var(--glass-gap) auto var(--glass-gap) var(--glass-gap);
    width: calc(var(--shell-inset) - var(--glass-gap));
    padding: 1.2rem 0.75rem 0.75rem;
    border: 1px solid;
    border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
    border-radius: var(--glass-r-shell);
    background: var(--glass-sheen), var(--glass-bg-chrome);
    -webkit-backdrop-filter: var(--glass-filter-chrome);
    backdrop-filter: var(--glass-filter-chrome);
    box-shadow: var(--glass-inner), var(--glass-shadow-float);
  }

  /* Rows inside a 28px panel inset by 12px: 16px corners. */
  html[data-ui-style="glass"] .top .view-nav .view-tab,
  html[data-ui-style="glass"] .top .top-actions > .btn {
    border-radius: var(--glass-r-inner);
  }

  html[data-ui-style="glass"] .top .shell-new {
    border-radius: var(--glass-r-pill);
  }

  html[data-ui-style="glass"] .top .top-actions > .btn {
    border-color: transparent;
    background: transparent;
    box-shadow: none;
  }

  html[data-ui-style="glass"] .top .top-actions > .btn:hover {
    background: var(--glass-hover);
  }

  html[data-ui-style="glass"] .top .account-btn {
    border: 1px solid;
    border-color: var(--glass-ctl-top) var(--glass-line-side) var(--glass-line-bottom);
    border-radius: var(--glass-r-inner);
    background: var(--glass-sheen-ctl), var(--glass-bg-thin);
    box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
  }

  html[data-ui-style="glass"] .top .account-btn:hover {
    background: var(--glass-sheen-ctl), var(--glass-lift);
  }

  html[data-ui-style="glass"] .view-nav-foot {
    border-top-color: var(--glass-line-side);
  }

  /* The account menu opens beside the floating sidebar's foot. */
  html[data-ui-style="glass"] .account-menu {
    inset: auto auto var(--glass-gap) calc(var(--shell-inset) + var(--glass-gap));
  }

  /* Tasks: the list is a glass pane, the open task floats beside it. */
  html[data-ui-style="glass"] #view-runs.layout {
    gap: var(--glass-gap);
    padding: var(--glass-gap) var(--glass-gap) 0 var(--glass-gap);
  }

  html[data-ui-style="glass"] #view-memory.layout,
  html[data-ui-style="glass"] #view-skills.layout:not(.simple) {
    gap: var(--glass-gap);
    padding: var(--glass-gap) var(--glass-gap) 0 var(--glass-gap);
  }

  html[data-ui-style="glass"] .runs-pane,
  html[data-ui-style="glass"] .memory-pane,
  html[data-ui-style="glass"] .skills-pane {
    height: calc(100% - var(--glass-gap));
    border: 0;
    border-radius: var(--glass-r-shell);
    background: var(--glass-sheen), var(--glass-bg);
    -webkit-backdrop-filter: var(--glass-filter);
    backdrop-filter: var(--glass-filter);
    box-shadow: var(--glass-inner), var(--glass-shadow);
  }

  /* The conversation scrolls under a floating header and a floating reply
     dock. No mask on the pane (the prototype faded its edges with one): a
     mask makes the pane the backdrop root of both bars, and their blur then
     reaches nothing, so the conversation showed through them sharp. (Even
     without one it did not reach the chat; the bars are opaque now, see
     «The open task».) */
  html[data-ui-style="glass"] .detail-pane {
    padding: 0 0.6rem 0;
    scroll-padding-top: 8rem;
  }

  html[data-ui-style="glass"] .detail-pane .detail-head {
    top: 0;
    margin: 0 0 0.25rem;
    padding: 0.95rem 1.25rem 0.85rem;
    border: 1px solid transparent;
    border-radius: var(--glass-r-card);
  }

  html[data-ui-style="glass"] .detail-pane .reply-dock {
    bottom: var(--glass-gap);
    margin: auto 0 var(--glass-gap);
    padding: 0.8rem 1.1rem 0.9rem;
    border-radius: var(--glass-r-card);
  }

  /* Home cards are frosted on desktop: the orbs drift behind them. */
  html[data-ui-style="glass"] .home-section,
  html[data-ui-style="glass"] .home-pane .dash-setup,
  html[data-ui-style="glass"] .home-field textarea {
    -webkit-backdrop-filter: var(--glass-filter);
    backdrop-filter: var(--glass-filter);
  }
}

/* The sidebar's open view: a lifted glass lens with the accent bar inside. */
html[data-ui-style="glass"] .view-tab:hover {
  background: var(--glass-hover);
}

html[data-ui-style="glass"] .view-tab.active {
  background: var(--glass-sheen-ctl), var(--glass-lift-accent);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .view-tab.active .view-tab-desc {
  color: var(--glass-muted-lens);
}

html[data-ui-style="glass"] .view-tab.active::before {
  left: 0.35rem;
  top: 0.8rem;
  bottom: 0.8rem;
  width: 4px;
  border-radius: 4px;
  background: linear-gradient(180deg, var(--spectrum-1), var(--spectrum-3) 55%, var(--spectrum-4));
}

/* In the rail the label is centred under the icon and as wide as the
   button: a bar at the left ran under its first letters (2.9:1 measured
   over the amber end). There it lies along the top, above the icon. */
@media (min-width: 821px) and (max-width: 1180px) {
  /* The rail keeps its own geometry inside the capsule: the wide sidebar's
     0.75rem sides left each label about 50px, so «Übersicht» and
     «Einstellungen» broke onto two lines and pushed the account button
     below a 700px window (measured at 1024x700). */
  html[data-ui-style="glass"] .top {
    inset: 0.4rem auto 0.4rem 0.4rem;
    width: calc(var(--shell-inset) - 0.4rem);
    padding: 0.6rem 0.2rem 0.4rem;
  }

  html[data-ui-style="glass"] .top .view-nav .view-tab,
  html[data-ui-style="glass"] .top .top-actions > .btn {
    padding-inline: 0;
  }

  html[data-ui-style="glass"] .top .view-tab.active::before {
    left: 32%;
    right: 32%;
    top: 0.2rem;
    bottom: auto;
    width: auto;
    height: 3px;
    background: linear-gradient(90deg, var(--spectrum-1), var(--spectrum-3) 55%, var(--spectrum-4));
  }
}

/* --- Cards: frosted layers -------------------------------------------------- */
html[data-ui-style="glass"] .home-section,
html[data-ui-style="glass"] .home-pane .dash-setup {
  border-radius: var(--glass-r-card);
  background: var(--glass-sheen), var(--glass-bg);
  box-shadow: var(--glass-inner), var(--glass-shadow);
}

html[data-ui-style="glass"] .home-pane .dash-setup.blocking {
  background: var(--glass-sheen), var(--glass-ask-bg);
}

/* «Wartet auf Sie»: amber glass, with the ask colour as a bar inside the
   rounded edge and a warm glow -- the heading says what it is. */
html[data-ui-style="glass"] .home-section.home-waiting {
  border-left-width: 1px;
  background: var(--glass-sheen), var(--glass-ask-bg);
  box-shadow: inset 5px 0 0 var(--glass-ask-edge), var(--glass-inner), inset 0 0 40px -14px var(--glass-ask-glow), var(--glass-shadow);
}

html[data-ui-style="glass"] .home-waiting-row {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-inner);
  background: var(--glass-sheen-ctl), var(--glass-ask-thin);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .home-recent-row {
  border-radius: var(--glass-r-inner);
}

html[data-ui-style="glass"] .home-recent-row:hover {
  border-color: var(--glass-line-side);
  background: var(--glass-hover);
}

html[data-ui-style="glass"] .home-estimate {
  border-color: var(--glass-line-side);
  background: var(--glass-bg-thin);
}

html[data-ui-style="glass"] .settings-advanced {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-card);
  background: var(--glass-sheen), var(--glass-bg);
  box-shadow: var(--glass-inner), var(--glass-shadow);
}

html[data-ui-style="glass"] .settings-advanced-summary {
  border-radius: var(--glass-r-card);
}

/* The composer: a glass well, still outlined in --control-line (3:1). */
html[data-ui-style="glass"] .home-field,
html[data-ui-style="glass"] .home-field textarea {
  border-radius: var(--glass-r-field);
}

html[data-ui-style="glass"] .home-field textarea {
  background: var(--glass-sheen), var(--glass-bg-field);
  box-shadow: var(--glass-inner), var(--glass-shadow);
}

html[data-ui-style="glass"] .home-recurrence {
  border-radius: var(--glass-r-card);
}

/* Settings tiles and cards: the same material without the blur. */
html[data-ui-style="glass"] .settings-tile {
  border-radius: var(--glass-r-card);
  background: var(--glass-sheen), var(--glass-bg);
  box-shadow: var(--glass-inner), var(--glass-shadow);
}

html[data-ui-style="glass"] .settings-tile:hover {
  box-shadow: var(--glass-inner), var(--glass-shadow-float), 0 0 0 1px color-mix(in srgb, var(--tile-hue) 25%, transparent);
}

html[data-ui-style="glass"] .settings-tile-icon {
  border-radius: var(--glass-r-inner);
}

html[data-ui-style="glass"] .card,
html[data-ui-style="glass"] .settings-card {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-card);
  background: var(--glass-sheen), var(--glass-bg);
  box-shadow: var(--glass-inner), var(--glass-shadow);
}

/* --- Tasks: the list ------------------------------------------------------ */
html[data-ui-style="glass"] .run-list li button {
  border-radius: var(--glass-r-inner);
}

html[data-ui-style="glass"] .run-list li button:hover {
  background: var(--glass-hover);
  border-color: var(--glass-line-side);
}

/* State-tinted rows: amber glass waits on the person, coral glass went
   wrong. The pill under the title still says which. */
html[data-ui-style="glass"] .run-list li:has(.status-pill.needs_input) > button {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  background: var(--glass-sheen-ctl), var(--glass-ask-thin);
  box-shadow: inset 3px 0 0 var(--glass-ask-edge), var(--glass-inner-ctl);
}

html[data-ui-style="glass"] .run-list li:has(.status-pill.failed, .status-pill.stalled) > button {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  background: var(--glass-sheen-ctl), var(--glass-fail-thin);
  box-shadow: inset 3px 0 0 var(--glass-fail-edge), var(--glass-inner-ctl);
}

html[data-ui-style="glass"] .run-list li button.active,
html[data-ui-style="glass"] .run-list li:has(.status-pill) > button.active {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  background: var(--glass-sheen-ctl), var(--glass-lift);
  box-shadow: inset 4px 0 0 var(--accent-bright), var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .runs-filters {
  gap: 0.35rem;
}

/* --- Tasks: the board ----------------------------------------------------- *
   Columns are thin glass inside the pane's frosted glass (no second blur:
   nothing moves behind a column but the pane). The state's colour stays a
   strip along the top, drawn inside the rounded edge; the word says it too. */
html[data-ui-style="glass"] .run-board-col {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-card);
  background: var(--glass-sheen), var(--glass-bg-thin);
  box-shadow: inset 0 3px 0 var(--glass-run-edge), var(--glass-inner);
}

html[data-ui-style="glass"] .run-board-col[data-col="waiting"] {
  background: var(--glass-sheen), var(--glass-ask-thin);
  box-shadow: inset 0 3px 0 var(--glass-ask-edge), var(--glass-inner), inset 0 0 40px -18px var(--glass-ask-glow);
}

html[data-ui-style="glass"] .run-board-col[data-col="done"] {
  box-shadow: inset 0 3px 0 var(--glass-ok-edge), var(--glass-inner);
}

html[data-ui-style="glass"] .run-board-col[data-col="problems"] {
  background: var(--glass-sheen), var(--glass-fail-thin);
  box-shadow: inset 0 3px 0 var(--glass-fail-edge), var(--glass-inner);
}

html[data-ui-style="glass"] .run-board-head {
  padding: 0.8rem 1rem 0.5rem;
}

/* Cards inside a 24px column inset by 8px: 16px corners. */
html[data-ui-style="glass"] .run-card {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-inner);
  background: var(--glass-sheen-ctl), var(--glass-bg-field);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .run-card:hover {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-ctl-bottom);
  background: var(--glass-sheen-ctl), var(--glass-lift);
}

html[data-ui-style="glass"] .run-card.active {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  background: var(--glass-sheen-ctl), linear-gradient(var(--glass-lift-accent) 0 0), var(--glass-bg-field);
  box-shadow: inset 4px 0 0 var(--accent-bright), var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .run-board-list li .run-continue {
  border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
  border-radius: var(--glass-r-pill);
  background: var(--glass-sheen-ctl), var(--glass-run-thin);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

/* --- Routines, files, knowledge, skills ------------------------------------
   The cards of these views are the same material as Home's, without the
   blur: they scroll, and the atmosphere behind them is smooth. A card that
   carries its state as a left strip keeps it (the strip says something). */
html[data-ui-style="glass"] .schedule-card,
html[data-ui-style="glass"] .scheduler-status,
html[data-ui-style="glass"] .schedules-empty,
html[data-ui-style="glass"] .artifact-group,
html[data-ui-style="glass"] .files-empty,
html[data-ui-style="glass"] .inputs-progress,
html[data-ui-style="glass"] .memory-empty,
html[data-ui-style="glass"] .memory-company,
html[data-ui-style="glass"] .skill-card {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-card);
  background: var(--glass-sheen), var(--glass-bg);
  box-shadow: var(--glass-inner), var(--glass-shadow);
}

html[data-ui-style="glass"] .scheduler-notice {
  border-top-color: var(--glass-line-top);
  border-right-color: var(--glass-line-side);
  border-bottom-color: var(--glass-line-bottom);
  border-radius: var(--glass-r-card);
  background: var(--glass-sheen), var(--glass-bg);
  box-shadow: var(--glass-inner), var(--glass-shadow);
}

html[data-ui-style="glass"] .skill-card.is-active {
  border-color: var(--glass-line-top) var(--glass-line-side) color-mix(in srgb, var(--accent) 45%, transparent);
}

/* A row inside a 24px card inset by 8px: 16px corners, a field-like well. */
html[data-ui-style="glass"] .artifacts-pane .artifact-row {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-inner);
  background: var(--glass-sheen-ctl), var(--glass-bg-field);
  box-shadow: var(--glass-inner-ctl);
}

html[data-ui-style="glass"] .artifacts-pane a.artifact-row:hover {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-ctl-bottom);
  background: var(--glass-sheen-ctl), var(--glass-lift);
}

html[data-ui-style="glass"] .file-visual {
  border-radius: 12px;
}

/* «Von Atlas erstellt» / «Ihre Unterlagen»: two capsules, the open one the
   smoked lens the chips use. */
html[data-ui-style="glass"] .skills-subtab {
  border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
  border-radius: var(--glass-r-pill);
  background: var(--glass-sheen-ctl), var(--glass-bg-thin);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
  color: var(--ink);
}

html[data-ui-style="glass"] .skills-subtab:hover {
  background: var(--glass-sheen-ctl), var(--glass-lift);
}

html[data-ui-style="glass"] .skills-subtab.active {
  border-color: transparent;
  background: var(--glass-sheen-accent), var(--glass-pressed);
  color: var(--glass-pressed-ink);
  box-shadow: var(--glass-inner-accent), var(--glass-shadow-ctl);
}

/* Rows in the knowledge and skill lists: the task list's rows. */
html[data-ui-style="glass"] .skill-select {
  border-radius: var(--glass-r-inner);
}

html[data-ui-style="glass"] .skill-select:hover {
  border-color: var(--glass-line-side);
  background: var(--glass-hover);
}

html[data-ui-style="glass"] .skill-select.active,
html[data-ui-style="glass"] .memory-row[aria-current="true"] {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  background: var(--glass-sheen-ctl), var(--glass-lift);
  box-shadow: inset 4px 0 0 var(--accent-bright), var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

/* A routine's «…» menu floats over the cards: overlay glass. */
html[data-ui-style="glass"] .schedule-menu {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-inner);
  background: var(--glass-sheen), var(--glass-bg-overlay);
  -webkit-backdrop-filter: var(--glass-filter-overlay);
  backdrop-filter: var(--glass-filter-overlay);
  box-shadow: var(--glass-inner), var(--glass-shadow-float);
}

html[data-ui-style="glass"] .schedule-menu [role="menuitem"] {
  border-radius: 10px;
}

/* Search fields, selects and form fields in the views: glass wells, still
   outlined in --control-line (3:1). Every page field the classic look draws
   with its own corner is listed, so none keeps the square white box inside
   a glass card (the settings forms, the knowledge and skill editors, the
   repo search and filters, the invite and pairing fields). */
html[data-ui-style="glass"] .skill-search-label input,
html[data-ui-style="glass"] .project-search-label input,
html[data-ui-style="glass"] .inline-filter select,
html[data-ui-style="glass"] .settings-language-label select,
html[data-ui-style="glass"] .settings-detail .settings-language-label input,
html[data-ui-style="glass"] .settings-search input,
html[data-ui-style="glass"] .settings-pref-form input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
html[data-ui-style="glass"] .custom-skill-form input:not([type="checkbox"]):not([type="radio"]),
html[data-ui-style="glass"] .custom-skill-form textarea,
html[data-ui-style="glass"] .memory-form input[type="text"],
html[data-ui-style="glass"] .memory-form select,
html[data-ui-style="glass"] .memory-form textarea,
html[data-ui-style="glass"] .invite-link-row input,
html[data-ui-style="glass"] .embed-form input,
html[data-ui-style="glass"] .setup-code-input,
html[data-ui-style="glass"] .account-field select {
  border-radius: var(--glass-r-inner);
  background-color: var(--glass-bg-field);
  box-shadow: inset 0 1px 2px var(--glass-outline);
}

/* --- Setup wizard ------------------------------------------------------------
   The atmosphere behind it, washed with thin glass so the head's accent
   eyebrow and hints keep 4.5:1 over the orbs (4.3:1 without); the card the
   material without the blur. */
html[data-ui-style="glass"] #view-setup.layout {
  background: var(--glass-bg-thin);
}

html[data-ui-style="glass"] .setup-card {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-sheet);
  background: var(--glass-sheen), var(--glass-bg-overlay);
  box-shadow: var(--glass-inner), var(--glass-shadow-float);
}

/* «Fertig» and the locked-path hint: concentric inside the sheet. */
html[data-ui-style="glass"] .setup-callout {
  border-radius: var(--glass-r-inner);
}

html[data-ui-style="glass"] .setup-stepper button {
  border-radius: var(--glass-r-pill);
}

html[data-ui-style="glass"] .setup-ai-choice {
  border-radius: var(--glass-r-inner);
  background: var(--glass-sheen-ctl), var(--glass-bg-field);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .setup-progress {
  border-radius: var(--glass-r-pill);
}

/* No task open: the start form is a glass card, not text on the
   atmosphere (its accent link fell to 3.9:1 over the pink orb). The same
   for the other views' «pick one» notes. */
html[data-ui-style="glass"] .detail-empty {
  /* The padding takes room the form used to have: a track that may shrink
     below the textarea's intrinsic width keeps the form inside the card at
     the largest text (it ran out of the card at 1440px / 150 %). */
  grid-template-columns: minmax(0, 1fr);
  padding: 1.6rem 1.6rem 1.4rem;
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-sheet);
  background: var(--glass-sheen), var(--glass-bg);
  box-shadow: var(--glass-inner), var(--glass-shadow);
}

/* --- The open task ---------------------------------------------------------- */
/* The head and the reply dock are pinned inside the scrolling pane, and a
   backdrop-filter there did not blur the conversation under them (Chrome
   154: the chat stayed sharp through 25 % glass). So they are the pane's
   glass made opaque, like the toasts and the dialog bands: the sheen, the
   rim and the float keep them glass, and the chat no longer shows through
   as ghost letters. The head casts only a short shadow: the chat scrolls
   out from under it, and the float's 70px of shade dimmed «Sie» on the
   phone to 4.2:1 just below its edge. */
html[data-ui-style="glass"] .detail-pane .detail-head {
  background: var(--glass-sheen), var(--glass-bg-pinned);
  box-shadow: var(--glass-inner), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .detail-pane .detail-head > * {
  position: relative;
  z-index: 1;
}

html[data-ui-style="glass"] .detail-pane .reply-dock {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  background: var(--glass-sheen), var(--glass-bg-pinned);
  box-shadow: var(--glass-inner), var(--glass-shadow-float);
}

html[data-ui-style="glass"] .reply-dock .file-drop-compact .file-drop-zone {
  border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
  background: var(--glass-sheen-ctl), var(--glass-bg-thin);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

/* Cards in the conversation: the material without the blur. */
html[data-ui-style="glass"] .attachments-card,
html[data-ui-style="glass"] .meta,
html[data-ui-style="glass"] .prompt-card,
html[data-ui-style="glass"] .tldr-card,
html[data-ui-style="glass"] .run-unmerged,
html[data-ui-style="glass"] .ask-card,
html[data-ui-style="glass"] .run-problem {
  border-radius: var(--glass-r-card);
  box-shadow: var(--glass-inner), var(--glass-shadow);
}

html[data-ui-style="glass"] .attachments-card,
html[data-ui-style="glass"] .meta,
html[data-ui-style="glass"] .prompt-card,
html[data-ui-style="glass"] .run-unmerged {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  background: var(--glass-sheen), var(--glass-bg);
}

html[data-ui-style="glass"] .ask-card {
  border-color: var(--glass-ask-edge);
  background: var(--glass-sheen), var(--glass-ask-bg);
  box-shadow: var(--glass-inner), inset 0 0 40px -14px var(--glass-ask-glow), var(--glass-shadow);
}

html[data-ui-style="glass"] .run-problem {
  border-color: var(--glass-fail-edge);
  background: var(--glass-sheen), var(--glass-fail-bg);
}

html[data-ui-style="glass"] .chat-bubble {
  border-radius: 20px;
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .chat-bubble.assistant {
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  background: var(--glass-sheen), var(--glass-bg);
  border-bottom-left-radius: 8px;
}

html[data-ui-style="glass"] .chat-bubble.user {
  border-color: color-mix(in srgb, var(--accent) 30%, var(--glass-line-side));
  background: var(--glass-sheen), color-mix(in srgb, var(--accent-bright) var(--glass-user-tint), var(--glass-bg));
  border-bottom-right-radius: 8px;
}

/* The answers a question offers: capsule wells inside the amber glass,
   their amber edge kept (it says these belong to the question). */
html[data-ui-style="glass"] .ask-option {
  border-radius: var(--glass-r-inner);
  background: var(--glass-sheen-ctl), var(--glass-bg-field);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .link-card,
html[data-ui-style="glass"] .stat-tile,
html[data-ui-style="glass"] .access-key-notice {
  border-radius: var(--glass-r-inner);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .link-card,
html[data-ui-style="glass"] .stat-tile {
  background: var(--glass-sheen-ctl), var(--glass-bg);
}

/* Where a person types an answer: a glass well, outlined in
   --control-line (3:1). */
html[data-ui-style="glass"] .send-field,
html[data-ui-style="glass"] .empty-run-form textarea,
html[data-ui-style="glass"] .empty-run-field select {
  border-radius: var(--glass-r-inner);
  background-color: var(--glass-bg-field);
}

/* «Zur neuesten Antwort» floats over the conversation: an opaque capsule.
   With a blur of its own, a phone task scrolled into its middle had five
   blurred layers (the top edge, the head, both arrows, the dock) against
   the budget of four. The arrows are .btn.icon.ghost: the selector has to
   outrank the glass .btn.ghost rules, hover included, or the capsule stays
   see-through. */
html[data-ui-style="glass"] .btn.icon.chat-scroll-btn,
html[data-ui-style="glass"] .btn.icon.chat-scroll-btn:hover {
  border-radius: var(--glass-r-pill);
  background: var(--glass-sheen-ctl), var(--glass-bg-pinned);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-float);
}

/* The panel tabs: a segmented glass capsule with a lifted lens. */
html[data-ui-style="glass"] .tabs {
  align-self: flex-start;
  max-width: 100%;
  gap: 0.15rem;
  padding: 0.25rem;
  border: 1px solid;
  border-color: var(--glass-ctl-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-pill);
  background: var(--glass-sheen-ctl), var(--glass-bg-thin);
  box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .tab {
  margin: 0;
  padding: 0.45rem 1.05rem;
  border-bottom: 0;
  border-radius: var(--glass-r-pill);
}

html[data-ui-style="glass"] .tab:hover {
  background: var(--glass-hover);
}

/* The open tab is the smoked lens the chips and segments use: a lifted
   white lens on white glass did not say which tab was open (1.3:1). */
html[data-ui-style="glass"] .tab.active {
  background: var(--glass-sheen-accent), var(--glass-pressed);
  color: var(--glass-pressed-ink);
  box-shadow: var(--glass-inner-accent), var(--glass-shadow-ctl);
}

html[data-ui-style="glass"] .run-file-chip {
  border-radius: var(--glass-r-pill);
  background: var(--glass-bg-thin);
}

/* --- Overlays ------------------------------------------------------------- */
html[data-ui-style="glass"] dialog {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-sheet);
  background: var(--glass-sheen), var(--glass-bg-overlay);
  -webkit-backdrop-filter: var(--glass-filter-overlay);
  backdrop-filter: var(--glass-filter-overlay);
  box-shadow: var(--glass-inner), var(--glass-shadow-float);
}

/* The call panel and the restart card float over the page like a sheet. */
html[data-ui-style="glass"] .voice-panel,
html[data-ui-style="glass"] .restart-card {
  border: 1px solid;
  border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
  border-radius: var(--glass-r-card);
  background: var(--glass-sheen), var(--glass-bg-dock);
  -webkit-backdrop-filter: var(--glass-filter-overlay);
  backdrop-filter: var(--glass-filter-overlay);
  box-shadow: var(--glass-inner), var(--glass-shadow-float);
}

/* The help sheet's pinned head and foot: the sheet's glass, opaque.
   Measured: a backdrop-filter on a band inside a modal dialog does
   not blur the text scrolling under it (the dialog in the top layer is its
   backdrop root), so the band itself has to hide that text. */
html[data-ui-style="glass"] .help-sheet-head,
html[data-ui-style="glass"] .help-sheet-actions {
  border-color: var(--glass-line-side);
  background: var(--glass-bg-band);
}

html[data-ui-style="glass"] dialog::backdrop {
  background: var(--glass-scrim);
  -webkit-backdrop-filter: var(--glass-filter-scrim);
  backdrop-filter: var(--glass-filter-scrim);
}

/* While a sheet is open, the page under its scrim keeps its tint and drops
   its own blur: the scrim's blur already softens everything there, and the
   blurred layers are then the sheet and its scrim. A task on a phone with
   the account menu open had five (the top edge, the head, the reply dock,
   the menu and its scrim) against a budget of four, before the head and
   the dock became opaque. */
html[data-ui-style="glass"]:has(dialog:modal) :is(.top, .tabbar, .runs-pane, .memory-pane, .skills-pane, .home-section, .home-pane .dash-setup, .home-field textarea, .schedule-menu, .voice-panel, .restart-card) {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

html[data-ui-style="glass"] dialog form {
  padding: 1.7rem 1.7rem 1.4rem;
}

html[data-ui-style="glass"] .file-drop-zone {
  border-radius: var(--glass-r-inner);
  background: var(--glass-bg-thin);
}

html[data-ui-style="glass"] .toast {
  border-top-color: var(--glass-line-top);
  border-right-color: var(--glass-line-side);
  border-bottom-color: var(--glass-line-bottom);
  border-radius: var(--glass-r-card);
  background: var(--glass-sheen), var(--glass-bg-toast);
  box-shadow: var(--glass-inner), var(--glass-shadow-float);
}

html[data-ui-style="glass"] .account-menu {
  border-radius: var(--glass-r-card);
}

/* The drawer's head (sticky since #1032): an opaque band of the sheet's own
   glass across its full width, so rows passing under it do not show through
   (a blur inside a modal dialog does not reach them, see the help sheet),
   fading out at its foot. The classic rule gives it the negative margins
   that carry the band to the sheet's edges. */
html[data-ui-style="glass"] .nav-drawer-head {
  border-radius: calc(var(--glass-r-sheet) - 1px) calc(var(--glass-r-sheet) - 1px) 0 0;
  background: var(--glass-bg-band);
  -webkit-mask-image: linear-gradient(180deg, var(--mask-solid) calc(100% - 0.5rem), transparent);
  mask-image: linear-gradient(180deg, var(--mask-solid) calc(100% - 0.5rem), transparent);
}

/* A row's lens needs air around its two lines: the label touched the lens
   edge and its highlight line. */
html[data-ui-style="glass"] .nav-drawer .view-tab {
  padding-top: 0.7rem;
  padding-bottom: 0.7rem;
  border-radius: var(--glass-r-inner);
}

html[data-ui-style="glass"] .nav-drawer-account {
  border-top-color: var(--glass-line-side);
}

/* --- Phone: chrome over the content ----------------------------------------- */
@media (max-width: 820px) {
  html[data-ui-style="glass"] {
    --glass-edge-h: 1.4rem;
    /* Room at the end of everything that scrolls, so its last row clears
       the floating tab bar and the button above it. */
    --glass-end-space: calc(4.25rem + var(--tabbar-h) + var(--glass-gap) * 2 + env(safe-area-inset-bottom, 0px));
  }

  /* The top bar floats: a scroll edge, not a box. Its own blur fades out
     downwards over --glass-edge-h, so the content scrolling under it
     dissolves rather than being cut. The fade starts below the 0.35rem
     under the buttons, never across them: over 1.4 x the edge it reached
     the focus ring of «Einrichten» at the largest text (2.83:1 in dark). Out of flow now, so every view takes
     its height as top padding (below). Taps pass through the fade. */
  html[data-ui-style="glass"] .top {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: var(--z-shell);
    padding: calc(0.35rem + env(safe-area-inset-top, 0px)) max(0.6rem, env(safe-area-inset-right, 0px)) calc(0.35rem + var(--glass-edge-h)) max(1rem, env(safe-area-inset-left, 0px));
    border-bottom: 0;
    background: linear-gradient(180deg, var(--glass-bg-edge) 0%, var(--glass-bg-edge) 50%, transparent 100%);
    -webkit-backdrop-filter: var(--glass-filter-edge);
    backdrop-filter: var(--glass-filter-edge);
    -webkit-mask-image: linear-gradient(180deg, var(--mask-solid) calc(100% - var(--glass-edge-h)), transparent 100%);
    mask-image: linear-gradient(180deg, var(--mask-solid) calc(100% - var(--glass-edge-h)), transparent 100%);
    pointer-events: none;
  }

  html[data-ui-style="glass"] .top > * {
    pointer-events: auto;
  }

  html[data-ui-style="glass"] main.layout {
    padding-top: calc(var(--topbar-h) + env(safe-area-inset-top, 0px));
  }

  /* Tab into something under the floating chrome and the scroller brings
     it out from under it (WCAG 2.4.11). Without this, «Datei anhängen»
     and «Starten» on Home were 100 % under the tab bar and the rows of
     «Wartet auf Sie» 40 % under «Neue Aufgabe»: they sat inside the
     scrollport, so nothing scrolled. The views scroll in main.layout; the
     panes and lists that scroll on their own reach the bottom of the
     screen, under the bar and the button, and start under the top bar's
     fade at most (at «Maximal» the whole task pane scrolls, not its list:
     its rows sat 84 % under the tab bar). */
  html[data-ui-style="glass"] .layout {
    scroll-padding-top: calc(4.5rem + env(safe-area-inset-top, 0px));
    scroll-padding-bottom: var(--glass-end-space);
  }

  html[data-ui-style="glass"] :is(.runs-pane, .memory-pane, .skills-pane, .run-list, .issue-list, .project-list, .skill-list, .memory-list, .skill-catalog-simple, .dashboard-pane, .schedules-pane, .artifacts-pane, .settings-pane) {
    scroll-padding-top: calc(var(--glass-edge-h) + 0.5rem);
    scroll-padding-bottom: var(--glass-end-space);
  }

  /* The wizard has a padding of its own, set by id. */
  html[data-ui-style="glass"] #view-setup.layout {
    padding-top: calc(0.9rem + var(--topbar-h) + env(safe-area-inset-top, 0px));
  }

  html[data-ui-style="glass"] .top .voice-btn,
  html[data-ui-style="glass"] .top .account-btn {
    border: 1px solid;
    border-color: var(--glass-ctl-top) var(--glass-ctl-side) var(--glass-ctl-bottom);
    background: var(--glass-sheen-ctl), var(--glass-bg-thin);
    box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
  }

  html[data-ui-style="glass"] .top .voice-btn {
    border-radius: var(--glass-r-pill);
  }

  html[data-ui-style="glass"] .top .account-btn {
    border-radius: 50%;
  }

  /* The tab bar floats: a capsule inset from the edges and above the safe
     area, the content scrolling underneath it. */
  html[data-ui-style="glass"] .tabbar {
    position: fixed;
    left: max(var(--glass-gap), env(safe-area-inset-left, 0px));
    right: max(var(--glass-gap), env(safe-area-inset-right, 0px));
    bottom: calc(var(--glass-gap) + env(safe-area-inset-bottom, 0px));
    height: var(--tabbar-h);
    padding: 0.3rem;
    gap: 0.1rem;
    border: 1px solid transparent;
    border-radius: var(--glass-r-pill);
    background: var(--glass-sheen), var(--glass-bg-bar);
    -webkit-backdrop-filter: var(--glass-filter-chrome);
    backdrop-filter: var(--glass-filter-chrome);
    box-shadow: var(--glass-inner), var(--glass-shadow-float);
  }

  html[data-ui-style="glass"] .tabbar-item {
    z-index: 1;
    min-height: 0;
    padding: 0.2rem 0.15rem;
    border-radius: var(--glass-r-pill);
  }

  /* At the two largest text sizes the capsule's inset and padding stop
     growing with the text (they are rem above): the four labels get the
     room the classic bar gives them. «Übersicht» and «Aufgaben» were cut
     at 390px / Maximal and 320px / Sehr gross. */
  html[data-ui-style="glass"]:is([data-text-size="larger"], [data-text-size="largest"]) .tabbar {
    left: max(6px, env(safe-area-inset-left, 0px));
    right: max(6px, env(safe-area-inset-right, 0px));
    padding: 3px;
    gap: 0;
  }

  html[data-ui-style="glass"]:is([data-text-size="larger"], [data-text-size="largest"]) .tabbar-item {
    padding-inline: 2px;
  }

  /* The open view: a lens of lifted glass behind the item, the label in
     ink and bold (the shape and the weight say it, not only the colour). */
  html[data-ui-style="glass"] .tabbar-item[aria-current="page"] {
    color: var(--ink);
  }

  html[data-ui-style="glass"] .tabbar-item[aria-current="page"] .tabbar-icon {
    color: var(--accent-text);
  }

  html[data-ui-style="glass"] .tabbar-item[aria-current="page"]::before {
    inset: 0;
    z-index: -1;
    height: auto;
    border-radius: var(--glass-r-pill);
    background: var(--glass-sheen-ctl), var(--glass-lift-accent);
    box-shadow: var(--glass-inner-ctl), var(--glass-shadow-ctl);
  }

  html[data-ui-style="glass"] .tabbar .nav-badge {
    top: -0.3rem;
  }

  html[data-ui-style="glass"] .btn.atlas-fab {
    bottom: calc(var(--tabbar-h) + var(--glass-gap) * 2 + env(safe-area-inset-bottom, 0px));
    right: max(var(--glass-gap), env(safe-area-inset-right, 0px));
  }

  html[data-ui-style="glass"] .btn.atlas-fab,
  html[data-ui-style="glass"] .btn.atlas-fab:hover {
    background: var(--glass-sheen-accent), var(--accent);
    box-shadow: var(--glass-inner-accent), var(--glass-shadow-accent), var(--glass-shadow-float);
  }

  html[data-ui-style="glass"] .run-list::after,
  html[data-ui-style="glass"] .run-board-list::after,
  html[data-ui-style="glass"] .issue-list::after,
  html[data-ui-style="glass"] .project-list::after,
  html[data-ui-style="glass"] .skill-list::after,
  html[data-ui-style="glass"] .memory-list::after,
  html[data-ui-style="glass"] .skill-catalog-simple::after,
  html[data-ui-style="glass"] .dashboard-pane::after,
  html[data-ui-style="glass"] .schedules-pane::after,
  html[data-ui-style="glass"] .artifacts-pane::after,
  html[data-ui-style="glass"] .settings-pane::after {
    height: var(--glass-end-space);
  }

  html[data-ui-style="glass"] .toast-region {
    inset: auto var(--glass-gap) calc(var(--tabbar-h) + var(--glass-gap) * 2 + env(safe-area-inset-bottom, 0px)) var(--glass-gap);
  }

  /* Cards on a phone: the material without the blur (it is invisible over
     the smooth atmosphere and costs frames). */
  html[data-ui-style="glass"] .home-section,
  html[data-ui-style="glass"] .home-pane .dash-setup {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  html[data-ui-style="glass"] .runs-pane {
    background: transparent;
  }

  /* The board: each column runs down under the floating tab bar, square at
   the bottom like a sheet that continues off screen; its list ends in the
   spacer above. «Ältere laden» under the board only has to clear the bar:
   the classic grid keeps it level beside «Neue Aufgabe», which floats the
   same distance above the bar. */
  html[data-ui-style="glass"] .run-board-col {
    border-bottom: 0;
    border-radius: var(--glass-r-card) var(--glass-r-card) 0 0;
  }

  html[data-ui-style="glass"] .run-board-older {
    padding-bottom: calc(var(--tabbar-h) + var(--glass-gap) * 2 + env(safe-area-inset-bottom, 0px));
  }

  html[data-ui-style="glass"] .detail-pane .detail-head {
    border-radius: 0 0 var(--glass-r-card) var(--glass-r-card);
  }

  html[data-ui-style="glass"] .detail-pane .reply-dock {
    border-radius: var(--glass-r-card) var(--glass-r-card) 0 0;
  }

  /* The drawer is a floating glass sheet, inset from three edges. */
  html[data-ui-style="glass"] .nav-drawer {
    margin: var(--glass-gap) var(--glass-gap) var(--glass-gap) auto;
    height: calc(100% - var(--glass-gap) * 2);
    max-height: calc(100% - var(--glass-gap) * 2);
    border: 1px solid;
    border-color: var(--glass-line-top) var(--glass-line-side) var(--glass-line-bottom);
    border-radius: var(--glass-r-sheet);
  }
}

/* A small phone at the two largest text sizes: the classic rule centres
   «Ältere laden» and clears «Neue Aufgabe» from below; here it clears the
   button and the floating bar under it. (0,3,1) outranks the classic (0,2,1). */
@media (max-width: 340px) {
  html[data-ui-style="glass"][data-text-size="larger"] .run-board-older {
    padding-bottom: var(--glass-end-space);
  }
}

@media (max-width: 380px) {
  html[data-ui-style="glass"][data-text-size="largest"] .run-board-older {
    padding-bottom: var(--glass-end-space);
  }
}

/* A dialog's pinned actions on a phone (the classic rule pins them): the
   sheet's glass, opaque over the form scrolling under it (no blur
   reaches it inside a dialog, see the help sheet), and reaching the glass
   form's wider padding. */
@media (max-width: 820px) and (min-height: 501px) {
  html[data-ui-style="glass"] dialog form > .dialog-actions:last-child {
    margin: 0.2rem -1.7rem -1.4rem;
    padding: 0.75rem 1.7rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
    border-top-color: var(--glass-line-side);
    border-radius: 0 0 calc(var(--glass-r-sheet) - 1px) calc(var(--glass-r-sheet) - 1px);
    background: var(--glass-bg-band);
  }
}

/* --- Fallbacks: opaque surfaces --------------------------------------------
   `html[data-ui-style="glass"]:root` outranks both glass token blocks (the
   dark one by source order). The layout (floating chrome, capsules, radii)
   stays; the material turns solid and the blur goes -- the pressed and the
   lifted lens included (ink at 90 % and white at 92 % before), so every
   glass surface is opaque, literally. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  html[data-ui-style="glass"]:root {
    --glass-alpha: 100%;
    --glass-alpha-chrome: 100%;
    --glass-alpha-overlay: 100%;
    --glass-alpha-thin: 100%;
    --glass-alpha-field: 100%;
    --glass-alpha-edge: 100%;
    --glass-alpha-dock: 100%;
    --glass-alpha-bar: 100%;
    --glass-alpha-toast: 100%;
    --glass-alpha-band: 100%;
    --glass-tint: var(--bg-elevated);
    --glass-pressed: var(--ink);
    --glass-lift: var(--surface);
  }

  html[data-ui-style="glass"][data-theme="dark"]:root {
    --glass-lift: color-mix(in srgb, var(--ink) 8%, var(--bg-elevated));
  }
}

@media (prefers-reduced-transparency: reduce) {
  html[data-ui-style="glass"]:root {
    --glass-alpha: 100%;
    --glass-alpha-chrome: 100%;
    --glass-alpha-overlay: 100%;
    --glass-alpha-thin: 100%;
    --glass-alpha-field: 100%;
    --glass-alpha-edge: 100%;
    --glass-alpha-dock: 100%;
    --glass-alpha-bar: 100%;
    --glass-alpha-toast: 100%;
    --glass-alpha-band: 100%;
    --glass-tint: var(--bg-elevated);
    --glass-pressed: var(--ink);
    --glass-lift: var(--surface);
    --glass-filter: none;
    --glass-filter-chrome: none;
    --glass-filter-overlay: none;
    --glass-filter-edge: none;
    --glass-filter-scrim: none;
  }

  html[data-ui-style="glass"][data-theme="dark"]:root {
    --glass-lift: color-mix(in srgb, var(--ink) 8%, var(--bg-elevated));
  }
}

@media (prefers-contrast: more) {
  html[data-ui-style="glass"]:root {
    --glass-alpha: 100%;
    --glass-alpha-chrome: 100%;
    --glass-alpha-overlay: 100%;
    --glass-alpha-thin: 100%;
    --glass-alpha-field: 100%;
    --glass-alpha-edge: 100%;
    --glass-alpha-dock: 100%;
    --glass-alpha-bar: 100%;
    --glass-alpha-toast: 100%;
    --glass-alpha-band: 100%;
    --glass-tint: var(--bg-elevated);
    --glass-pressed: var(--ink);
    --glass-lift: var(--surface);
    --glass-filter: none;
    --glass-filter-chrome: none;
    --glass-filter-overlay: none;
    --glass-filter-edge: none;
    --glass-filter-scrim: none;
    --glass-sheen-top: transparent;
    --glass-sheen-low: transparent;
    --glass-rim: linear-gradient(var(--control-line), var(--control-line));
    --glass-rim-spectral: linear-gradient(var(--control-line), var(--control-line));
    --glass-line-top: var(--control-line);
    --glass-line-side: var(--control-line);
    --glass-line-bottom: var(--control-line);
    --glass-ctl-top: var(--control-line);
    --glass-ctl-side: var(--control-line);
    --glass-ctl-bottom: var(--control-line);
    --glass-glow: transparent;
  }

  html[data-ui-style="glass"][data-theme="dark"]:root {
    --glass-lift: color-mix(in srgb, var(--ink) 8%, var(--bg-elevated));
  }
}
