/* ============================================================
   zenpAI Arbeitsbereich — shell layout: the 68px header, the transcript/rail
   grid, the rail's tab chrome, the identity popover and the <1100px off-canvas
   drawer. Companion to assets/zpa-app.css, which owns every colour, radius,
   hairline and type scale used below; this file adds no token of its own.
   No CDN, no web font, no external reference of any kind.
   ============================================================ */

/* The app is a fixed viewport with exactly one scroll container per column;
   the document itself never scrolls. `height:100%` has to be stated on both
   html and body, because a percentage height resolves against the parent's
   *used* height and the initial containing block is the only ancestor with
   one.

   Leptos mounts into <body> (`mount_to_body`), so body IS the shell's root
   element and carries the column layout: header, optional degradation banner,
   then the grid taking whatever is left. A flex column rather than
   `calc(100% - 68px)` on the grid, because the banner's height is not a
   constant — its German sentence wraps to three lines on a 390px phone — and a
   layout that hardcodes it pushes the composer off the bottom of exactly the
   screen where that matters most. */
html, body { height: 100%; }
body { overflow: hidden; display: flex; flex-direction: column; }

/* Hairline, in-palette scrollbars. The browser default reads as chrome from
   another product on a surface this quiet. */
.transcript, .rail-panes, .popover, .md pre, .md .table-wrap, .report-dialog-content {
  scrollbar-width: thin;
  scrollbar-color: var(--linie) transparent;
}

/* ── Header (68px) ────────────────────────────────────────────────── */
/* `min-height` + `flex-wrap`, not a fixed `height`: at 68px the header is one
   row at every width the product is designed for, and on a 320px phone it
   becomes two rather than pushing its last control past the right edge — where
   `body{overflow:hidden}` would make it invisible *and* unreachable. Flex
   decides when that happens; there is no second breakpoint to keep in sync. */
.app-header {
  min-height: var(--app-header-h); flex-shrink: 0;
  border-bottom: 1px solid var(--linie);
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 24px;
  padding: 0 22px;
  position: relative; z-index: 60;
  background: var(--kohle);
  transition: background 380ms var(--ease), border-color 380ms var(--ease);
}
.header-brand { display: flex; align-items: center; gap: inherit; min-width: 0 }
.header-controls { display: flex; align-items: center; gap: 12px; margin-left: auto }
.crumb { border-left: 1px solid var(--linie); padding-left: 18px; margin-left: 2px; white-space: nowrap }
.crumb .sep { color: var(--linie); margin: 0 7px }

/* DE | EN. Two buttons and a hairline separator — not a select: the choice is
   binary and permanently visible. */
.lang-toggle { display: flex; align-items: center; gap: 2px }
.lang-toggle button {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em;
  color: var(--schiefer); padding: 6px 5px;
}
.lang-toggle button.on { color: var(--graphit) }
.lang-toggle .sep { color: var(--linie) }

.theme-toggle { font-size: 15px; line-height: 1; padding: 8px 9px }

/* Muted, non-blocking degradation banner: `policy_error` from /api/bootstrap.
   Muted on purpose — the workspace works, only the "which alias resolves
   local" annotation is missing, and a red bar would claim otherwise. */
.degraded {
  display: flex; align-items: baseline; gap: 6px 14px; flex-wrap: wrap;
  flex-shrink: 0;
  padding: 9px 22px;
  border-bottom: 1px solid var(--linie);
  background: var(--kohle-card);
}
.degraded .service-de { font-size: 13px; color: var(--schiefer); line-height: 1.45 }

/* ── Shell grid (minmax(0,1fr) 400px at >=1100px) ─────────────────── */
.app-main {
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--rail-w);
  overflow: hidden;
  /* The containing block for the rail once it becomes a drawer: the drawer
     covers the content area and never the header, whatever the header and
     banner happen to add up to. */
  position: relative;
}

/* min-height:0 / min-width:0 — a flex or grid item refuses to shrink below its
   content, so without these the composer is pushed off the bottom of the
   viewport and the transcript never scrolls. */
.app-col {
  display: flex; flex-direction: column;
  min-width: 0; min-height: 0;
  border-right: 1px solid var(--linie);
  position: relative;
}
.transcript {
  flex: 1; min-height: 0;
  overflow-y: auto;
  /* overflow-anchor:none — the browser's own scroll anchoring fights the
     transcript autoscroll. scroll-behavior stays `auto` here; the one smooth
     scroll in the product is the pill's, set per call. */
  overflow-anchor: none;
  scroll-behavior: auto;
  padding: 34px 40px 26px;
  /* Own paint, not a transparent hole onto <body>: the data-theme flip at
     mount is a real value change on the already-painted <body>, so body's own
     `transition: background 380ms` runs, and an element without a background
     of its own shows that transition through as a flat grey rectangle for up
     to 380ms. Freshly inserted elements (.app-header/.rail/.composer-bar) have
     no prior value to transition from and paint their final colour at once,
     which is why only this one needs a background. */
  background: var(--kohle);
  transition: background 380ms var(--ease);
}
.column-inner { max-width: 820px; margin: 0 auto }
.composer-bar {
  position: relative;
  border-top: 1px solid var(--linie);
  padding: 16px 40px 18px;
  background: var(--kohle);
}

/* ── Composer meta line ───────────────────────────────────────────── */
/* Composition of existing primitives, no token or colour of its own: the two
   <select>s are the mono label shape with the browser's chrome removed, so the
   row reads as one sentence of live truth rather than as a form. */
.composer-meta { display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: center; margin-top: 11px }
.composer-meta .sep { color: var(--linie) }
.composer-meta .hint { color: var(--mittelgrau) }
.composer-meta select {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--schiefer);
  background: none; border: 0; padding: 0; cursor: pointer;
  transition: color 200ms var(--ease);
}
.composer-meta select:hover, .composer-meta select:focus-visible { color: var(--blau) }
.composer-meta select:disabled { cursor: not-allowed; opacity: 0.55 }
/* The option list is drawn by the platform, which does not inherit a
   transparent background — name the ground so a light-on-light dropdown is
   not possible in either theme. */
.composer-meta option { background: var(--kohle-card); color: var(--graphit); text-transform: none }
.composer-bar label[for="ask"] { display: block; margin-bottom: 6px }

/* ── Transcript body ──────────────────────────────────────────────── */
/* One exchange = one <article>. The hairline between exchanges belongs to
   `.qa-answer` in zpa-app.css, so this wrapper is structure only. */
.qa { display: block }
/* A wide table scrolls INSIDE its block instead of widening the column.
   `.md .table-wrap` exists, but pulldown-cmark emits a bare <table> and
   nothing may wrap it after the fact — a settled block's HTML is never
   rewritten. `:has()` puts the overflow on the block that contains a table and
   nowhere else. */
.md-block:has(> table) { overflow-x: auto }

/* The refusal line: knowledge's own sentence under the footer's mono row.
   Full width and sentence case — it is a statement, not a label, and the
   uppercase mono of the row above would make an audit fact look like a chip. */
.answer-footer .note {
  flex-basis: 100%;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.02em;
  text-transform: none; color: var(--schiefer); line-height: 1.5;
}

/* ── Rail ─────────────────────────────────────────────────────────── */
.rail {
  display: flex; flex-direction: column;
  min-width: 0; min-height: 0;
  background: var(--kohle);
  transition: background 380ms var(--ease), transform 300ms var(--ease);
}
.rail-tabs { display: flex; border-bottom: 1px solid var(--linie); flex-shrink: 0 }
.rail-tabs button {
  flex: 1; padding: 19px 6px 15px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--schiefer);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: color 200ms var(--ease), border-color 200ms var(--ease);
}
.rail-tabs button:hover { color: var(--graphit) }
.rail-tabs button[aria-selected="true"] { color: var(--blau); border-bottom-color: var(--blau) }
.rail-panes { flex: 1; min-height: 0; overflow-y: auto; padding: 22px 22px 34px }
/* An empty tab states what will appear and when. A pane that is blank until
   something happens reads as broken. */
.empty { padding: 30px 2px; line-height: 1.55; color: var(--schiefer); font-size: 14px }

/* ── Search rail tab ──────────────────────────────────────────────── */
/* Reuses the composer's `.field-group`/`.composer-meta` shapes verbatim, so the
   search form reads as the same sentence of live truth. All this panel adds is
   the vertical rhythm a form sitting above a list of result cards needs. */
.search-panel .composer-meta { margin-bottom: 4px }
.search-results { margin-top: 22px }
.search-results .empty { padding-top: 8px }

/* ── Identity menu ────────────────────────────────────────────────── */
.who { position: relative }
.who > button {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 10px;
  border: 1px solid var(--linie); border-radius: var(--radius);
  transition: border-color 200ms var(--ease);
}
.who > button:hover, .who > button[aria-expanded="true"] { border-color: var(--blau) }
.who .initials {
  width: 26px; height: 26px; border-radius: 3px; border: 1px solid var(--blau);
  display: grid; place-items: center;
  font-family: var(--mono); font-size: 10px; font-weight: 700; color: var(--blau);
  flex-shrink: 0;
}
.who .name { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.15 }
.who .name b { font-size: 13px; font-weight: 600 }
.who .caret { color: var(--schiefer); font-size: 10px }

.popover {
  position: absolute; top: calc(100% + 8px); right: 0; width: 322px;
  background: var(--kohle-card);
  border: 1px solid var(--linie); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 8px; z-index: 70;
  max-height: min(70vh, 560px); overflow-y: auto;
}
.popover .popover-head { padding: 8px 10px 10px; border-bottom: 1px solid var(--linie); margin-bottom: 6px }
.popover .popover-foot { padding: 10px; border-top: 1px solid var(--linie); margin-top: 6px; line-height: 1.45 }
/* One session, one identity: no row to pick, just the facts the directory
   returned — the same mono/uppercase/muted label convention `.ledger dt`
   uses for a request row's own field names. */
.popover .identity-facts {
  padding: 4px 10px 8px;
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 6px 14px;
  align-items: baseline;
}
.popover .identity-facts dt {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.06em;
  color: var(--schiefer); text-transform: uppercase;
}
.popover .identity-facts dd { font-size: 13px; overflow-wrap: anywhere }
.popover-foot .btn--ghost.outline { width: 100% }

/* ── BootstrapGate skeleton ───────────────────────────────────────── */
/* Hairlines, not a spinner: the anonymous fetch is a fast local check and this
   is visible for one frame on a warm cache. A spinner would imply waiting on
   something slow, which would be a lie about where the time goes. */
.skeleton { padding: 60px 40px; display: flex; flex-direction: column; gap: 14px; max-width: 620px }
.skeleton .bar { height: 1px; background: var(--linie) }
.skeleton .bar:nth-child(2) { width: 72% }
.skeleton .bar:nth-child(3) { width: 45% }

/* ── Login screen ─────────────────────────────────────────────────── */
/* Centred card, the homepage form idiom the composer already uses (bottom
   border under each field, coral on focus) rather than a boxed dialog — this
   IS the page while nobody has signed in, not an overlay on top of one. */
.login-screen {
  height: 100%; display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.login-card { width: min(360px, 100%); display: flex; flex-direction: column; gap: 4px }
.login-card .logo { margin-bottom: 18px }
.login-card h1 { font-size: 22px; font-weight: 700; letter-spacing: -0.015em; margin: 0 0 2px }
.login-card > .mono-label.fg { margin-bottom: 4px }
.login-card > .empty { margin-bottom: 22px }
.login-card form { display: flex; flex-direction: column; gap: 18px }
.login-card .field + .field { margin-top: -4px }
.login-card button[type="submit"] { margin-top: 6px }

/* ── Responsive (rail becomes an off-canvas drawer <1100px) ───────── */
@media (max-width: 1100px) {
  .app-main { grid-template-columns: minmax(0, 1fr) }
  .rail {
    position: absolute; top: 0; right: 0; bottom: 0;
    width: min(var(--rail-w), 92vw);
    border-left: 1px solid var(--linie);
    transform: translateX(101%);
    z-index: 80;
    box-shadow: var(--shadow-soft);
  }
  body.rail-open .rail { transform: none }
  .app-col { border-right: 0 }
  .transcript { padding: 26px 22px 20px }
  .composer-bar { padding: 14px 22px 16px }
}
@media (min-width: 1101px) {
  /* The drawer button is the only way into a rail that is always visible
     above this width, so it is not merely hidden — it is absent. */
  .rail-drawer-btn { display: none !important }
}
@media (max-width: 720px) {
  /* Everything in the header earns its width again at this size. The crumb and
     the wordmark go — the mark alone is the brand, and the identity square
     without its name is still the identity — so that the four *controls*
     (drawer, identity, DE|EN, theme) all stay reachable rather than being
     clipped by `body{overflow:hidden}` and silently unreachable. */
  .app-header { gap: 8px 10px; padding: 9px 12px }
  .header-controls { gap: 8px }
  .crumb, .who .name, .logo-word { display: none }
  .app-header .chip { padding: 4px 9px; font-size: 9.5px; letter-spacing: 0.07em }
  /* DE|EN collapses to the language you are *not* in. The pair is a binary
     toggle, so the inactive half carries the whole meaning, and 31px of a
     360px header is worth more elsewhere. */
  .lang-toggle .sep, .lang-toggle button[aria-pressed="true"] { display: none }
  .degraded { padding: 9px 14px }
  .transcript { padding: 22px 16px 16px }
  .composer-bar { padding: 12px 16px 14px }
  .popover { width: min(322px, calc(100vw - 28px)); right: -6px }
}
@media (max-width: 600px) {
  .report-btn { display: none }
}
