/* Atlas — base styles: tokens, app shell, typography, buttons, cards, forms,
   badges, dialogs, toasts. View-specific rules live in views.css. */

:root {
  /* This palette is light by construction and several controls force a white
     background. Without declaring the scheme, a browser in dark mode resolves
     the UA default colour for form controls to near-white, and a textarea that
     does not set its own colour renders invisible against that white. Declaring
     it keeps those defaults light. Not a token — a statement about the page. */
  color-scheme: light;

  --scrollbar-thumb: #cfc4b2;
  --scrollbar-track: #efe9e0;
}

/* Scrollbars, said out loud rather than left to the browser.
   `color-scheme: light` above fixes the UA defaults for form CONTROLS, and the
   owner still met a black scrollbar beside the questionnaire and beside the
   drawer (2026-08-12) — a browser in dark mode paints scrollbar furniture from
   its own preference, and a page that never names a colour gets whatever that
   preference is. Naming both halves settles it in every engine: `scrollbar-color`
   for Firefox and Chromium 121+, the `::-webkit-*` rules for everything older,
   including Safari. Applied to the whole document rather than to the two panes
   that were noticed, because the next scrolling pane would have the same bug and
   nobody would connect it to this. */
:root {
  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
  scrollbar-width: thin;
}

*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: var(--scrollbar-track); }
*::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border-radius: 5px;
  border: 2px solid var(--scrollbar-track);
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-soft); }

:root {

  --bg: #f6f3ee;
  --surface: #fffdfa;
  --surface-2: #efe9e0;
  --text: #33302b;
  --text-soft: #746c60;
  --border: #e2dacc;
  --border-strong: #cfc4b2;
  --accent: #5e8272;
  --accent-strong: #49695c;
  --accent-soft: #e3ece7;
  --focus-ring: 2px solid rgba(94, 130, 114, 0.4);
  --danger: #b25a50;
  --danger-strong: #97463d;
  --danger-soft: #f7e6e3;
  --warn-bg: #fbf1dd;
  --warn-border: #eddbb4;
  --warn-text: #7c5f21;
  /* "Out of date, and yours to decide about" — a translation that no longer
     matches what it was made from (board task 122). Its own three tokens rather
     than the warn ones because it has to be told apart AT A GLANCE from the
     field beside it that is merely untranslated, and orange rather than
     `--danger` because a stale translation is work to do, not a fault. */
  --stale-bg: #fdeadb;
  --stale-border: #f0c39a;
  --stale-text: #8f4e1a;
  --radius: 10px;
  --radius-sm: 7px;
  --shadow: 0 1px 2px rgba(51, 46, 38, 0.05), 0 6px 20px rgba(51, 46, 38, 0.06);
  --font-body: -apple-system, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: Georgia, "Times New Roman", serif;
  --font-mono: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
}

* { box-sizing: border-box; }

/* Author display rules (flex etc.) must never beat the hidden attribute. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  color: var(--text);
  background:
    radial-gradient(1100px 380px at 12% -8%, rgba(94, 130, 114, 0.08), transparent 60%),
    var(--bg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* --- App shell ----------------------------------------------------------- */

.app-header {
  flex: none;
  display: flex;
  align-items: center;
  gap: 28px;
  padding: 0 32px;
  height: 60px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.brand {
  font-family: var(--font-display);
  font-size: 22px;
  letter-spacing: 0.02em;
  color: var(--accent-strong);
  text-decoration: none;
}

/* Name and version travel together as ONE flex item. The header's `gap` changes
   twice between a desktop and a phone (28 -> 16 -> 14), so a version spaced by
   that gap needed a negative margin per breakpoint - and at 390px it landed on
   top of the word "Atlas". Its own container has its own gap and no width
   depends on anything. */
.brand-block { flex: none; display: flex; align-items: baseline; gap: 6px; min-width: 0; }

/* Deliberately small and quiet: it is a fact you go looking for (which machine
   is this? did the update land?) rather than one the page should spend
   attention on. */
.app-version {
  flex: none;
  font-size: 11px;
  font-family: var(--font-mono);
  color: var(--text-soft);
  opacity: 0.75;
}

.app-nav { display: flex; gap: 18px; }

.app-nav a {
  color: var(--text-soft);
  text-decoration: none;
  font-weight: 500;
  font-size: 14px;
  padding: 17px 2px 15px;
  border-bottom: 2px solid transparent;
}

.app-nav a:hover { color: var(--text); }

.app-nav a[aria-current] {
  color: var(--accent-strong);
  border-bottom-color: var(--accent);
}

.header-right { margin-left: auto; display: flex; align-items: center; gap: 14px; }

.user-name { color: var(--text-soft); font-size: 14px; }

.app-header.is-anon .app-nav,
.app-header.is-anon .user-name,
.app-header.is-anon #logout-btn { display: none; }

/* A PUBLIC page (a published questionnaire, an invite) is anonymous whoever is
   signed in: the brand and the language switch, nothing else. The version goes
   too - it is a fact about the software, and a stranger was given a form. */
.app-header.is-public .app-nav,
.app-header.is-public .user-name,
.app-header.is-public #logout-btn,
.app-header.is-public .app-version { display: none; }

.lang-toggle {
  display: inline-flex;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  overflow: hidden;
}

.lang-toggle button {
  border: 0;
  background: transparent;
  padding: 4px 12px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-soft);
  cursor: pointer;
}

.lang-toggle button.active { background: var(--accent); color: #fff; }

/* The scrolling element of the whole app: `body` is `overflow: hidden` so the
   header stays put and this pane moves under it.

   **It is full-bleed, and the content is centred with PADDING rather than by
   capping this box.** That is the whole reason the rule looks like this. It used
   to be `max-width: 1160px; margin: 0 auto`, which centres the scroller itself —
   so its scrollbar was drawn at the column's right edge, floating
   `(viewport - 1160) / 2` in from the side of the window. At 1920px that is
   380px, and the owner reported it as "a scroll bar inside the page... about
   twenty five percent in from the right hand side" (2026-08-21). A scrollbar
   belongs at the edge of the window; anywhere else reads as a broken frame.

   `--main-max` rather than a literal so a view can widen the column (Fred's
   workspace does) without going back to capping the box and reintroducing this.
   The `max()` keeps the old 32px gutter once the window is narrower than the
   column, and the media queries below override the padding outright from there. */
.app-main {
  --main-max: 1160px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  width: 100%;
  padding: 28px max(32px, calc((100% - var(--main-max)) / 2)) 48px;
}

/* --- Buttons -------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  padding: 8px 16px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.btn:hover:not(:disabled) { border-color: var(--accent); box-shadow: 0 1px 5px rgba(73, 105, 92, 0.15); }
.btn:focus-visible { outline: var(--focus-ring); outline-offset: 1px; }
.btn:disabled { opacity: 0.45; cursor: default; }
.btn[hidden] { display: none; }

.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-primary:hover:not(:disabled) { background: var(--accent-strong); border-color: var(--accent-strong); }

.btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-danger:hover:not(:disabled) { background: var(--danger-strong); border-color: var(--danger-strong); }

.btn-ghost { background: transparent; border-color: transparent; color: var(--text-soft); }
.btn-ghost:hover:not(:disabled) { color: var(--text); border-color: var(--border-strong); box-shadow: none; }

.btn-sm { padding: 5px 12px; font-size: 13px; }
.btn-block { width: 100%; }

/* --- Cards, inputs, badges ------------------------------------------------ */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.input {
  width: 100%;
  font: inherit;
  color: var(--text);
  background: #fff;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
}

.input:focus { outline: var(--focus-ring); outline-offset: 1px; border-color: var(--accent); }

.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
}

.badge-accent { background: var(--accent-soft); color: var(--accent-strong); }
.badge-muted { background: var(--surface-2); color: var(--text-soft); }
.badge-warn { background: var(--warn-bg); color: var(--warn-text); border: 1px solid var(--warn-border); }
.badge-live { background: #e5efdd; color: #4c6b33; }
.badge-plain { background: transparent; border: 1px solid var(--border); color: var(--text-soft); }
.badge[hidden] { display: none; }

.chip, .chip-control {
  background: var(--surface-2);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 12.5px;
  color: var(--text-soft);
  white-space: nowrap;
}

/* A chip you can OPERATE — the same token, as a control. Chat's mode switch is
   the first of them (board task 25). Deliberately not ALSO `.chip`: a locator
   asking a header for its chips wants the labels, not a form control. */
.chip-control {
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
}
.chip-control:hover { border-color: var(--accent); }
.chip-control:focus-visible { outline: var(--focus-ring); outline-offset: 1px; }

/* A chip that GOES somewhere. Chat's client chip is the first (board task 16):
   it stays a `.chip` because it is still a label of this session, and it is an
   anchor because the record behind it is the point of the session having a
   client at all. */
.chip-link { text-decoration: none; color: var(--accent-strong); }
.chip-link:hover { background: var(--accent-soft); }
.chip-link:focus-visible { outline: var(--focus-ring); outline-offset: 1px; }

.chip-row { display: flex; gap: 8px; flex-wrap: wrap; }

/* --- The authoring model switch (js/models.js) --------------------------------
   Haiku · Sonnet · Opus, as one segmented control rather than three buttons or a
   dropdown: the three are being compared with each other, and a closed <select>
   hides the two you did not pick. It lives here rather than in a view's sheet
   because two views draw it — the authoring assistant and a document's study
   notes — and they must not drift apart.

   Sized off the same tokens as `.btn-sm` so it sits level with the buttons
   beside it, and it wraps, because every other control row in Atlas learned to
   (footgun 34).

   `ai-switch`, NOT `model-switch`: chat already has a `.mode-switch` (which of
   FRED'S modes you are testing, board task 25). Two classes one letter apart,
   both called "switch", both in the same app, is a bug waiting for whoever
   greps next. */
.ai-switch {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 2px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
}

.ai-switch[hidden] { display: none; }

.ai-switch-choice {
  border: 0;
  border-radius: 999px;
  padding: 2px 11px;
  background: transparent;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.7;
  color: var(--text-soft);
  cursor: pointer;
  white-space: nowrap;
}

.ai-switch-choice:hover { color: var(--text); }
.ai-switch-choice:focus-visible { outline: var(--focus-ring); outline-offset: 1px; }

/* Which model each choice IS, and which is chosen, is colour + ring + weight —
   set in workspace.css beside the panel tints the colours must match (owner,
   2026-07-31: the buttons reflect the thread's theming). Here, only the bones. */

.muted { color: var(--text-soft); }
.small { font-size: 13px; }
.empty-note { color: var(--text-soft); font-size: 14px; margin: 8px 0; }
.loading { color: var(--text-soft); padding: 26px 0; text-align: center; }

.error-box {
  background: var(--danger-soft);
  color: #7c3a32;
  border: 1px solid #ecccc7;
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  margin: 8px 0;
  font-size: 14px;
}

.form-field { display: block; margin-bottom: 16px; }
.form-label { display: block; font-size: 13px; font-weight: 600; color: var(--text-soft); margin-bottom: 5px; }

.form-error {
  background: var(--danger-soft);
  color: #7c3a32;
  border: 1px solid #ecccc7;
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  margin-bottom: 14px;
  font-size: 13.5px;
}

/* --- Page scaffolding shared by views -------------------------------------- */

.page-title { font-family: var(--font-display); font-weight: 500; font-size: 26px; margin: 0; }
.page-head { margin-bottom: 18px; }

.back-link { color: var(--text-soft); text-decoration: none; font-size: 13.5px; }
.back-link:hover { color: var(--accent-strong); }

/* --- Modal ------------------------------------------------------------------ */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(48, 42, 34, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 60;
}

.modal {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
  padding: 22px 24px;
  width: 100%;
  max-width: 460px;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
}

.modal-wide { max-width: 780px; }
.modal-message { margin: 0 0 14px; white-space: pre-line; }
/* A refusal that names every field it refused on can run to a couple of thousand
   characters (the publish gate did exactly that). It scrolls inside the dialog
   rather than growing one that runs off the bottom of the screen with its own
   Close button below the fold. `overflow-wrap` because the list is
   comma-separated with no spaces to break at on a narrow phone. */
.modal-scroll { max-height: min(50vh, 340px); overflow-y: auto; overflow-wrap: anywhere; }
.modal-title + .modal-message { margin-top: 10px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.modal-title { margin: 0; font-size: 16px; }

.modal-close {
  border: 0;
  background: transparent;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  color: var(--text-soft);
  padding: 2px 8px;
  border-radius: 6px;
}

.modal-close:hover { background: var(--surface-2); color: var(--text); }
.modal-body { overflow: auto; min-height: 0; }

/* --- Toasts ------------------------------------------------------------------ */

.toast-stack {
  position: fixed;
  right: 20px;
  bottom: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 80;
  max-width: 360px;
  /* **A toast may never eat a click** (owner, 2026-08-24). The stack is pinned
     to the bottom-right corner, and above 1100px the authoring assistant is a
     360px column pinned to that same corner — so every toast lands squarely on
     its Send button and, for the 4.55s it lives, swallowed the press. The owner
     met it in the worst possible place: the toast sitting there was the spend-
     limit refusal, so the one press it blocked was the retry.

     A toast is an announcement. Nothing in it is clickable, nothing listens for
     hover, and it is gone in four seconds whatever the reader does - so it has
     no business in the hit-testing at all, in this corner or any other. Set on
     the STACK rather than on each toast so it also covers the gaps between
     them, and so a toast added later cannot forget. */
  pointer-events: none;
}

.toast {
  background: #35312a;
  color: #f7f4ee;
  padding: 11px 16px;
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.22);
  font-size: 14px;
  border-left: 3px solid var(--accent);
  animation: toast-in 0.25s ease;
}

.toast-success { border-left-color: #7fa96b; }
.toast-error { border-left-color: #d18077; }
.toast-out { opacity: 0; transform: translateY(6px); transition: opacity 0.3s ease, transform 0.3s ease; }

/* A confirmation nobody asked to see (owner, 2026-09-06): announced to
   assistive technology through the live region, present in the DOM for the
   harness, and drawn nowhere. The standard visually-hidden recipe rather than
   `display: none`, which would silence the announcement too. */
.toast-quiet {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  animation: none;
}

/* On a phone the corner IS the page. One slim bar along the bottom, full
   width, and never more than one (ui.js replaces rather than stacks). */
@media (max-width: 700px) {
  .toast-stack { left: 12px; right: 12px; bottom: 12px; max-width: none; }
  .toast { font-size: 13.5px; padding: 9px 12px; }
}

/* The bell: whether confirmations are shown to THIS person. Off by default,
   which is why it is drawn quiet; on, it takes the accent. Hidden on a public
   page and when signed out - it is a signed-in preference. */
.confirm-toggle { padding: 4px 8px; color: var(--text-soft); }
.confirm-toggle svg { display: block; }
.confirm-toggle .confirm-slash { stroke: currentColor; }
.confirm-toggle.is-on { color: var(--accent-strong); }
.confirm-toggle.is-on .confirm-slash { display: none; }
.app-header.is-anon .confirm-toggle,
.app-header.is-public .confirm-toggle { display: none; }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
}

/* --- "The server is behind the code on disk" (app/staleness.py) --------------
   A BANNER, not a toast: a toast is for something that went well and needs no
   action, and this needs one and stays true until somebody takes it. Above the
   view rather than inside it, because it is true of the whole app and has to be
   able to appear before any view has rendered. Never drawn on a deployed Atlas,
   whose code cannot change under it. */
.restart-banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 16px;
  background: var(--warn-bg);
  border-bottom: 1px solid var(--warn-border);
  color: var(--warn-text);
  font-size: 13.5px;
  line-height: 1.45;
}

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

.restart-banner-mark {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 1px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--warn-text);
  color: var(--warn-bg);
  font-size: 12px;
  font-weight: 700;
}
