/* VxAI Web -- the same dark-Nord palette as ai_evolve's Textual "nord"
   theme (interface/theme.tcss + vxai/theme.tcss), ported to CSS. Exact hex
   values pulled from the installed Textual theme object, not approximated:
   primary/secondary/accent/foreground/background/surface/panel/success/
   warning/error below match that theme field-for-field. */

:root {
  --bg: #2E3440;
  --surface: #3B4252;
  --panel: #434C5E;
  --muted: #4C566A;
  --fg: #D8DEE9;
  --fg-bright: #ECEFF4;
  --primary: #88C0D0;
  --secondary: #81A1C1;
  --accent: #B48EAD;
  --success: #A3BE8C;
  --warning: #EBCB8B;
  --error: #BF616A;
  --btn-fg: #2E3440; /* Textual's button-color-foreground: dark text on a primary button */
  --font-mono: "Cascadia Code", "JetBrains Mono", "Fira Code", ui-monospace,
    "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  /* Scrolling within a child (#transcript, #conv-list, ...) shouldn't
     rubber-band the whole page or trigger the browser's pull-to-refresh --
     each scrollable region gets its own overscroll-behavior below too, but
     this stops the bounce from ever reaching the page itself as a fallback. */
  overscroll-behavior-y: contain;
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-mono);
  font-size: 15px;
  line-height: 1.5;
}

/* Buttons/links respond the instant a tap lifts instead of waiting out the
   double-tap-zoom detection window, and can't be zoomed by a stray
   double-tap -- meaningful with min-height:44px hit targets this small. */
button, .header-link, .conv-item, #sidebar-toggle, #sidebar-close {
  touch-action: manipulation;
}

#app {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  max-width: 760px;
  margin: 0 auto;
  /* At desktop width, #app is a flex:1 item inside body's row layout (see
     the min-width:960px block) -- flex items default to min-width:auto,
     meaning their floor is their content's own min-content width, not 0.
     Long unbroken text deep inside (a news card summary, in particular)
     can then force #app itself wider than the viewport, and everything
     else stretches to match -- text that would otherwise wrap just runs
     off the edge instead. min-width:0 lets #app actually shrink to the
     space it's given, which is what makes its descendants wrap normally. */
  min-width: 0;
}

/* ---------- header: one flat toolbar row, retro terminal chrome ----------
   design.png's own title strip is just a status dot + plain text on one
   line -- the two-row split tried here before (a title strip plus a
   separate button row) looked exactly like what it was: two mostly-empty
   rows stacked. One row, icon-weight actions on the right, is both closer
   to the reference and closer to how ChatGPT's own header reads. */

#header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.5rem 1rem;
  /* viewport-fit=cover (see index.html) lets these env() vars resolve to
     the actual notch/Dynamic-Island/status-bar inset on iOS instead of 0 --
     max() so it never gets smaller than the normal padding on a phone
     without a notch, or on desktop where the env() vars don't exist. */
  padding-top: max(0.5rem, env(safe-area-inset-top));
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  border-bottom: 1px solid var(--panel);
  background: var(--surface);
}

.header-left {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* status LED -- neutral while /api/health hasn't answered yet; chat.js
   toggles .online / .offline once it has (same info the status line at
   the bottom of the page spells out in words, for anyone who taps it). */
.dot {
  width: 8px;
  height: 8px;
  flex-shrink: 0;
  background: var(--muted);
}
.dot.online { background: var(--success); }
.dot.offline { background: var(--error); }

.brand-name {
  color: var(--fg-bright);
  font-size: 14px;
  font-weight: bold;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.header-actions {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}

/* Icon-weight action buttons -- one glyph each (💾 ✚ ⚙ ⏻), equal square
   footprint, tooltip + aria-label carrying the word instead of spelling it
   out inline. Four spelled-out labels crowded the row at phone widths;
   four small squares don't, and read as a toolbar rather than a wall of
   text -- closer to the reference's flat icon-button language. */
.header-actions button,
.header-link {
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--warning);
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: inherit;
  font-size: 14px;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}

.header-actions button:hover,
.header-link:hover { color: var(--fg); border-color: var(--secondary); }
#logout:hover { color: var(--error); border-color: var(--error); }
#sidebar-toggle:hover { color: var(--fg); border-color: var(--secondary); }

/* Text link button -- the sidebar's "settings" link and the settings
   page's "← back to chat" link both carry a word, not a single glyph, so
   they get the old auto-width/padded treatment instead of the 32px icon
   square above (that's .header-link, reserved for the single-glyph ⚙
   settings icon in the main chat header -- same underlying style, just a
   fixed footprint that only works for one character). */
.link-btn {
  display: inline-flex;
  align-items: center;
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--warning);
  padding: 6px 10px;
  min-height: 32px;
  font-family: inherit;
  font-size: 12px;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.link-btn:hover { color: var(--fg); border-color: var(--secondary); }

/* ---------- tab bar (Chat / News) ---------- */

#tabbar {
  display: flex;
  gap: 0.25rem;
  padding: 0 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  border-bottom: 1px solid var(--panel);
  background: var(--surface);
  flex-shrink: 0;
}

.tab {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--warning);
  padding: 8px 4px;
  margin-bottom: -1px; /* sits on top of #tabbar's own border-bottom */
  font-family: inherit;
  font-size: 13px;
  font-weight: bold;
  cursor: pointer;
}
.tab:hover { color: var(--fg); }
.tab.active { color: var(--primary); border-bottom-color: var(--primary); }

/* Each top-level view fills the space below the tab bar; only .active
   actually renders, so switching tabs is just a display toggle -- the
   chat's SSE connection and DOM state stay alive underneath, unaffected. */
.view {
  display: none;
  flex: 1;
  min-height: 0;
  flex-direction: column;
}
.view.active { display: flex; }

/* ---------- transcript ---------- */

#transcript {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  padding: 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

/* Thin flat scrollbar, every scrollable region in the app -- was only
   ever applied to #transcript, so #news-results/#settings-main/
   #model-picker/#conv-list were all falling back to the browser's chunky
   default (light, rounded, platform-styled), which stands out against
   the flat dark theme everywhere else. */
#transcript, #news-results, #settings-main, #model-picker, #conv-list {
  scrollbar-width: thin;              /* Firefox */
  scrollbar-color: var(--panel) transparent;
}
#transcript::-webkit-scrollbar,
#news-results::-webkit-scrollbar,
#settings-main::-webkit-scrollbar,
#model-picker::-webkit-scrollbar,
#conv-list::-webkit-scrollbar { width: 8px; }
#transcript::-webkit-scrollbar-thumb,
#news-results::-webkit-scrollbar-thumb,
#settings-main::-webkit-scrollbar-thumb,
#model-picker::-webkit-scrollbar-thumb,
#conv-list::-webkit-scrollbar-thumb { background: var(--panel); border-radius: 0; }
#transcript::-webkit-scrollbar-track,
#news-results::-webkit-scrollbar-track,
#settings-main::-webkit-scrollbar-track,
#model-picker::-webkit-scrollbar-track,
#conv-list::-webkit-scrollbar-track { background: transparent; }

.msg {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-width: 100%;
}

.msg-role {
  font-size: 12px;
  font-weight: bold;
  letter-spacing: 0.02em;
}

.msg-user .msg-role { color: var(--secondary); }
.msg-assistant .msg-role { color: var(--primary); }

/* .msg-body: a plain, single-segment message (user turns, greetings).
   .msg-turn: an assistant turn that can hold several segments in order
   (thinking / text / a tool call / its result / more text, ...) -- same
   outer card look as .msg-body, so from a distance it still reads as one
   reply, with segments flowing inside instead of separate boxes. */
.msg-body,
.msg-turn {
  background: var(--surface);
  border: 1px solid var(--panel);
  border-radius: 0;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.msg-user .msg-body { border-left: 3px solid var(--secondary); }
.msg-assistant .msg-body,
.msg-assistant .msg-turn { border-left: 3px solid var(--primary); }

.seg-text {
  white-space: pre-wrap;
  word-break: break-word;
}

.chat-img {
  display: block;
  max-width: 100%;
  max-height: 420px;
  height: auto;
  border-radius: 0;
  margin: 6px 0;
  border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
  background: repeating-conic-gradient(#e8e8e8 0% 25%, #ffffff 0% 50%) 50% / 16px 16px;
}

/* A [text](url) link in chat text -- e.g. VxAI standing up a local dev
   server for something you asked it to build and handing you the address.
   Bold + underlined + a real tap target, not just colored text, so it
   reads as a button on a phone, not a formatting accident. */
.chat-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--primary);
  font-weight: bold;
  text-decoration: underline;
  text-underline-offset: 2px;
  word-break: break-all;
}
.chat-link:hover { color: var(--secondary); }
.chat-link::after { content: "\2197"; text-decoration: none; } /* small ↗ -- signals "opens elsewhere" */

.seg-thinking {
  color: var(--warning);
  font-style: italic;
  font-size: 12.5px;
  white-space: pre-wrap;
  word-break: break-word;
}

.seg-error {
  color: var(--error);
}

.seg-stopped {
  color: var(--warning);
  font-style: italic;
}

/* Coarse progress markers with no per-character content of their own
   (currently just the news "write article" flow's research_start/
   research_done, before the actual writer turn's own thinking/text
   segments begin) -- deliberately plainer than .seg-tool, since there's
   nothing to expand/collapse, just a status line. */
.seg-status {
  color: var(--warning);
  font-size: 12.5px;
  padding: 4px 0;
}

/* Shown only for a live turn (chat.js's addAssistantTurn(true) /
   streamIntoTurn's clearLoading) -- the animated logo filling the gap
   between sending and the model's first visible token, instead of a
   generic spinner. Removed the moment real content starts arriving. */
.seg-loading {
  display: flex;
  align-items: center;
  padding: 4px 0;
}
.seg-loading img { display: block; }

/* a tool call: visually distinct from prose -- its own dark block, an
   accent-colored header line naming what's running, monospace output */
.seg-tool {
  background: var(--bg);
  border: 1px solid var(--panel);
  border-radius: 0;
  overflow: hidden;
}

.seg-tool-header {
  color: var(--accent);
  font-size: 12.5px;
  font-weight: bold;
  padding: 6px 10px;
  border-bottom: 1px solid var(--panel);
  white-space: pre-wrap;
  word-break: break-word;
}

.seg-tool-body {
  padding: 8px 10px;
  font-size: 13px;
  color: var(--fg);
}

.seg-tool-body pre {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: inherit;
}

.seg-tool-body summary {
  cursor: pointer;
  color: var(--warning);
  font-size: 12px;
}

.seg-tool-body details[open] summary { margin-bottom: 6px; }

.seg-tool-error .seg-tool-header { color: var(--error); }
.seg-tool-error { border-color: var(--error); }

.cursor {
  display: inline-block;
  margin-left: 1px;
  color: var(--primary);
  animation: blink 1s steps(2, jump-none) infinite;
}

@keyframes blink { 50% { opacity: 0; } }

/* ---------- composer ---------- */

#composer {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 0.75rem 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  border-top: 1px solid var(--panel);
  background: var(--surface);
}

#input {
  flex: 1;
  resize: none;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--panel);
  border-radius: 0;
  padding: 10px 12px;
  font-family: inherit;
  font-size: 16px; /* >=16px stops iOS Safari auto-zoom on focus */
  max-height: 200px;
}

#input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}

#input:disabled { opacity: 0.6; }

#attach {
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--warning);
  border-radius: 0;
  min-height: 44px;
  min-width: 44px;
  font-size: 18px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
}

#attach:hover:not(:disabled) { color: var(--fg); border-color: var(--secondary); }
#attach:disabled { opacity: 0.5; cursor: default; }

#send {
  background: var(--primary);
  color: var(--btn-fg);
  border: none;
  border-radius: 0;
  padding: 0 18px;
  min-height: 44px;
  font-family: inherit;
  font-weight: bold;
  font-size: 14px;
  cursor: pointer;
}

#send:hover:not(:disabled) { background: var(--secondary); }
#send:disabled { opacity: 0.5; cursor: default; }

/* Swaps in for #send while a turn is streaming (see setBusy) -- same
   footprint, error-colored so it visibly reads as "cancel" rather than
   a second send action. */
#stop {
  background: var(--error);
  color: var(--fg-bright);
  border: none;
  border-radius: 0;
  padding: 0 18px;
  min-height: 44px;
  font-family: inherit;
  font-weight: bold;
  font-size: 14px;
  cursor: pointer;
}
#stop:hover { background: #a5484f; } /* --error, darkened -- no darker token defined in the palette */
#stop[hidden] { display: none; }

/* The hidden attribute is otherwise defeated by the display:flex/inline-
   flex rules above (header icon buttons, #attach, #sidebar-toggle) -- a
   guest session hides those via JS, and without this the buttons would
   stay visible despite [hidden]. !important is needed to beat the more
   specific selectors. */
[hidden] { display: none !important; }

/* ---------- status line ---------- */

#status {
  padding: 4px 1rem 10px;
  /* #status is the last thing in #app -- it's the element actually
     touching the physical bottom edge (home-indicator strip on iPhone),
     so the bottom safe-area inset belongs here, not on #composer above it. */
  padding-bottom: max(10px, env(safe-area-inset-bottom));
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  font-size: 11px;
  color: var(--warning);
  background: var(--surface);
}

/* ---------- news view ---------- */

#news-categories-bar {
  padding: 0.75rem 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  border-bottom: 1px solid var(--panel);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex-shrink: 0;
}
/* #news-categories-bar's own `display: flex` above would otherwise beat
   the browser's default [hidden] { display: none } -- an ID selector
   outranks a bare attribute selector -- so chat.js's
   newsCategoriesBar.hidden = true wouldn't actually hide it. This rule's
   ID+attribute specificity beats the plain-ID rule above and restores
   the expected behavior. */
#news-categories-bar[hidden] { display: none; }

#category-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

#category-chips:empty::after {
  content: "No categories yet -- add one below to start following it.";
  color: var(--warning);
  font-size: 12px;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg);
  border: 1px solid var(--panel);
  color: var(--fg);
  padding: 4px 4px 4px 10px;
  font-size: 12.5px;
}

.chip button {
  background: transparent;
  border: none;
  color: var(--warning);
  font-size: 12px;
  line-height: 1;
  padding: 4px 6px;
  cursor: pointer;
}
.chip button:hover { color: var(--error); }

#category-form {
  display: flex;
  gap: 8px;
}

#category-input {
  flex: 1;
  min-width: 0;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--panel);
  padding: 8px 10px;
  font-family: inherit;
  font-size: 16px; /* stops iOS Safari auto-zoom on focus */
}
#category-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }

#category-form button {
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--fg);
  padding: 0 14px;
  font-family: inherit;
  font-size: 13px;
  font-weight: bold;
  cursor: pointer;
}
#category-form button:hover { border-color: var(--secondary); color: var(--fg-bright); }

/* A separate row inside #news-categories-bar (not folded into
   #category-form) -- pasting a link is a different kind of action
   (adding one specific article) from picking topics, but shares that
   parent's padding/background/border via its flex-column layout. */
#pin-form {
  display: flex;
  gap: 8px;
}

#pin-input {
  flex: 1;
  min-width: 0;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--panel);
  padding: 8px 10px;
  font-family: inherit;
  font-size: 16px; /* stops iOS Safari auto-zoom on focus */
}
#pin-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }

#pin-form button {
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--fg);
  padding: 0 14px;
  font-family: inherit;
  font-size: 13px;
  font-weight: bold;
  white-space: nowrap;
  cursor: pointer;
}
#pin-form button:hover:not(:disabled) { border-color: var(--secondary); color: var(--fg-bright); }
#pin-form button:disabled { opacity: 0.5; cursor: default; }

#news-toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  border-bottom: 1px solid var(--panel);
  flex-shrink: 0;
}

/* Opens/closes #news-categories-bar below -- collapsed by default (see
   chat.js's setNewsCategoriesOpen) so chips + the add-category/pin-a-link
   rows don't eat a phone screen's worth of height before any actual news
   shows. Same small-button language as #news-refresh next to it. */
#news-categories-toggle {
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--fg);
  padding: 6px 10px;
  font-family: inherit;
  font-size: 12px;
  font-weight: bold;
  white-space: nowrap;
  cursor: pointer;
  flex-shrink: 0;
}
#news-categories-toggle:hover,
#news-categories-toggle.open { border-color: var(--secondary); color: var(--fg-bright); }

#news-updated {
  flex: 1;
  min-width: 0;
  color: var(--warning);
  font-size: 11px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#news-refresh {
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--fg);
  padding: 6px 12px;
  font-family: inherit;
  font-size: 12px;
  font-weight: bold;
  cursor: pointer;
}
#news-refresh:hover:not(:disabled) { border-color: var(--secondary); color: var(--fg-bright); }
#news-refresh:disabled { opacity: 0.5; cursor: default; }
#news-refresh.loading { color: var(--warning); border-color: var(--warning); }

/* The Morning News button is the hero of the News tab -- it reads
   differently from the small chrome buttons around it on purpose. */
#morning-news-btn {
  background: transparent;
  border: 1px solid var(--accent);
  color: var(--accent);
  padding: 6px 12px;
  font-family: inherit;
  font-size: 12px;
  font-weight: bold;
  white-space: nowrap;
  cursor: pointer;
  flex-shrink: 0;
}
#morning-news-btn:hover:not(:disabled) { background: var(--accent); color: var(--bg); }
#morning-news-btn:disabled { opacity: 0.5; cursor: default; }
#morning-news-btn.busy { color: var(--warning); border-color: var(--warning); }

/* The morning read panel: takes over the whole news view (news results
   hide underneath) so the brief reads like an article, not a widget. */
#morning-panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#morning-panel[hidden] { display: none; }
/* Panel open = reading mode: the news feed (and the toolbar that drives
   it) tucks away underneath; the panel's own header/close takes over. */
#view-news.morning-open #news-toolbar,
#view-news.morning-open #news-categories-bar,
#view-news.morning-open #news-results { display: none; }

#morning-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.6rem 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  border-bottom: 1px solid var(--panel);
  flex-shrink: 0;
}
#morning-title {
  color: var(--accent);
  font-size: 13px;
  font-weight: bold;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
#morning-close {
  background: transparent;
  border: none;
  color: var(--fg);
  font-size: 14px;
  cursor: pointer;
  padding: 4px 8px;
}
#morning-close:hover { color: var(--fg-bright); }

#morning-status {
  padding: 0.5rem 1rem;
  color: var(--warning);
  font-size: 12px;
  flex-shrink: 0;
}
#morning-status.error { color: var(--error); }

#morning-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  padding: 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
}
#morning-body:empty::before {
  content: "Press ☕ Morning News to get today's read...";
  color: var(--warning);
  font-size: 13px;
}
#morning-body h2 {
  color: var(--fg-bright);
  font-size: 16px;
  margin: 1.4rem 0 0.5rem;
  padding-bottom: 4px;
  border-bottom: 2px solid var(--accent);
}
#morning-body h3 { color: var(--fg-bright); font-size: 14px; margin: 1rem 0 0.4rem; }
#morning-body h4 { color: var(--fg-bright); font-size: 13px; margin: 0.8rem 0 0.3rem; }
#morning-body p { margin: 0 0 0.8rem; }
#morning-body ul, #morning-body ol { margin: 0 0 0.8rem; padding-left: 1.4rem; }
#morning-body li { margin-bottom: 0.25rem; }
#morning-body blockquote {
  margin: 0 0 0.8rem;
  padding-left: 0.9rem;
  border-left: 3px solid var(--secondary);
  color: var(--fg);
  opacity: 0.85;
}
#morning-body code {
  background: var(--panel);
  padding: 1px 4px;
  border-radius: 3px;
  font-size: 0.92em;
}
#morning-body a.chat-link { color: var(--secondary); }

#morning-comment {
  flex-shrink: 0;
  border-top: 1px solid var(--panel);
  padding: 0.8rem 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  background: var(--surface);
}
.morning-comment-title {
  font-size: 12px;
  font-weight: bold;
  color: var(--fg-bright);
  margin-bottom: 0.4rem;
}
#morning-comment-input {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--panel);
  color: var(--fg);
  font-family: inherit;
  font-size: 13px;
  padding: 8px;
  resize: vertical;
}
#morning-comment-input:focus { outline: none; border-color: var(--secondary); }
#morning-comment-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.5rem;
}
#morning-comment-send {
  background: var(--accent);
  border: none;
  color: var(--btn-fg);
  padding: 7px 12px;
  font-family: inherit;
  font-size: 12px;
  font-weight: bold;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
}
#morning-comment-send:disabled { opacity: 0.5; cursor: default; }
#morning-comment-feedback {
  color: var(--success);
  font-size: 11.5px;
  min-width: 0;
  word-break: break-word;
}
#morning-comment-feedback.error { color: var(--error); }

#morning-lens {
  flex-shrink: 0;
  border-top: 1px solid var(--panel);
  padding: 0.6rem 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
}
#morning-lens summary {
  color: var(--muted);
  font-size: 11.5px;
  cursor: pointer;
  user-select: none;
}
#morning-lens summary:hover { color: var(--fg-bright); }
#morning-lens-learned { margin-top: 0.5rem; display: flex; flex-direction: column; gap: 0.35rem; }
.morning-learned-note {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  font-size: 12px;
  color: var(--fg);
}
.morning-learned-note .morning-learned-del {
  background: transparent;
  border: none;
  color: var(--error);
  cursor: pointer;
  font-size: 12px;
  padding: 0 2px;
  flex-shrink: 0;
}
.morning-learned-note .morning-learned-del:hover { color: var(--fg-bright); }
#morning-lens-seed { margin-top: 0.5rem; }
#morning-lens-seed summary {
  color: var(--muted);
  font-size: 11.5px;
  cursor: pointer;
  user-select: none;
}
#morning-lens-seed summary:hover { color: var(--fg-bright); }
#morning-lens-seed-text {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--panel);
  color: var(--fg);
  font-family: inherit;
  font-size: 12px;
  padding: 8px;
  margin-top: 0.4rem;
  resize: vertical;
}
#morning-lens-seed-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.4rem;
}
#morning-lens-seed-save {
  background: var(--secondary);
  border: none;
  color: var(--btn-fg);
  padding: 5px 10px;
  font-family: inherit;
  font-size: 11.5px;
  font-weight: bold;
  cursor: pointer;
}
#morning-lens-seed-feedback { color: var(--success); font-size: 11.5px; }

#news-results {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  padding: 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  display: flex;
  flex-direction: column;
  gap: 1.75rem; /* space between SECTIONS now -- cards within a section get their own, tighter gap below */
}

#news-results:empty::before {
  content: "No news fetched yet -- add a category above and hit Refresh.";
  color: var(--warning);
  font-size: 13px;
}

/* A real front page instead of one flat list in fetch order -- top
   stories (highest relevance across every category) get the featured
   treatment up top; recent/popular are dense grids below, the way an
   actual news site's homepage is laid out. Whatever's freshest or
   highest-relevance always lands in one of these, on top, regardless of
   where it fell in the array the last fetch happened to return. */
.news-section-heading {
  display: flex;
  align-items: baseline;
  gap: 8px;
  color: var(--fg-bright);
  font-size: 13px;
  font-weight: bold;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding-bottom: 6px;
  margin-bottom: 10px;
  border-bottom: 2px solid var(--primary);
}

.news-section-note {
  color: var(--warning);
  font-size: 10.5px;
  font-weight: normal;
  letter-spacing: normal;
  text-transform: none;
}

.news-section-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.news-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 10px;
  align-items: start;
}

.news-card {
  background: var(--surface);
  border: 1px solid var(--panel);
  border-left: 3px solid var(--accent);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Top-stories treatment: a heavier accent edge and a bigger headline so
   the lead items actually read as "featured," not just first-in-list. */
.news-card.featured {
  border-left-width: 4px;
}
.news-card.featured .news-card-title {
  font-size: 17px;
}

/* User-pinned cards get their own accent (primary, not the AI-picked
   cards' --accent) so "I added this" reads as visually distinct from
   "the model ranked this," at a glance, in a section that survives
   every Refresh instead of being replaced by it. */
.news-card.pinned {
  border-left-color: var(--primary);
}
.news-card.pinned .news-card-category {
  color: var(--primary);
}

.news-card-time {
  color: var(--warning);
  flex-shrink: 0;
  white-space: nowrap;
}

.news-card-category {
  color: var(--accent);
  font-size: 10.5px;
  font-weight: bold;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.news-card-title {
  color: var(--fg-bright);
  font-size: 14.5px;
  font-weight: bold;
  line-height: 1.35;
}

.news-card-summary {
  color: var(--fg);
  font-size: 13px;
  line-height: 1.45;
}

.news-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 2px;
  font-size: 11.5px;
}

.news-card-source {
  color: var(--warning);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.news-card-relevance {
  color: var(--warning);
  flex-shrink: 0;
  letter-spacing: 1px;
}

.news-card-link {
  color: var(--primary);
  font-weight: bold;
  text-decoration: underline;
  text-underline-offset: 2px;
  flex-shrink: 0;
}
.news-card-link:hover { color: var(--secondary); }

.news-error {
  color: var(--error);
  font-size: 13px;
  border: 1px solid var(--error);
  padding: 10px 12px;
}

.news-card-write {
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--primary);
  font-family: inherit;
  font-weight: bold;
  font-size: 11px;
  padding: 3px 8px;
  cursor: pointer;
  flex-shrink: 0;
}
.news-card-write:hover { color: var(--fg-bright); border-color: var(--secondary); }

.news-card-unpin {
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--warning);
  font-family: inherit;
  font-weight: bold;
  font-size: 11px;
  padding: 3px 8px;
  cursor: pointer;
  flex-shrink: 0;
}
.news-card-unpin:hover { color: var(--error); border-color: var(--error); }

/* ---------- news "write article" modal ---------- */
/* Same overlay pattern as #backdrop, just above everything (sidebar
   included -- this can be opened from the News tab regardless of
   whether the sidebar happens to be open on a narrow screen). */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
/* display:flex above (needed to center the panel while it IS shown)
   otherwise beats the browser's built-in [hidden]{display:none} rule --
   same specificity, but this stylesheet loads after the UA one, so
   without this override the modal showed at every page load regardless
   of the hidden attribute. No other [hidden]-toggled element here sets
   its own display, which is why this never came up before .modal-overlay. */
.modal-overlay[hidden] { display: none; }

.modal-panel {
  background: var(--surface);
  border: 1px solid var(--panel);
  width: min(92vw, 480px);
  max-height: 85vh;
  display: flex;
  flex-direction: column;
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  border-bottom: 1px solid var(--panel);
  color: var(--fg-bright);
  font-weight: bold;
  font-size: 13.5px;
  flex-shrink: 0;
}

.modal-head button {
  background: transparent;
  border: none;
  color: var(--fg);
  font-size: 14px;
  line-height: 1;
  padding: 4px;
  cursor: pointer;
}
.modal-head button:hover { color: var(--error); }

#article-modal-body {
  padding: 14px;
  overflow-y: auto;
  overscroll-behavior-y: contain;
}

.modal-label {
  display: block;
  color: var(--warning);
  font-size: 11px;
  font-weight: bold;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin: 12px 0 6px;
}
.modal-label:first-child { margin-top: 0; }

#article-model,
#article-instructions {
  width: 100%;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--panel);
  padding: 8px 10px;
  font-family: inherit;
  font-size: 13px;
}
#article-model:focus,
#article-instructions:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}
#article-instructions { resize: vertical; line-height: 1.4; }

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;
}


/* ---------- settings page ---------- */

#settings-main {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  padding: 1rem;
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  padding-bottom: max(1rem, env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.settings-note {
  margin: 0;
  color: var(--warning);
  font-size: 11px;
}

.settings-card {
  background: var(--surface);
  border: 1px solid var(--panel);
  border-radius: 0;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.settings-card h2 {
  margin: 0;
  color: var(--primary);
  font-size: 14px;
}

.settings-help {
  margin: 0;
  color: var(--warning);
  font-size: 12.5px;
  line-height: 1.5;
}

.settings-card select,
.settings-card input[type="text"],
.settings-card input[type="password"] {
  width: 100%;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--panel);
  border-radius: 0;
  padding: 10px 12px;
  font-family: inherit;
  font-size: 16px; /* stops iOS Safari auto-zoom on focus */
}

.settings-card select:focus,
.settings-card input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}

.settings-card button {
  align-self: flex-start;
  background: var(--primary);
  color: var(--btn-fg);
  border: none;
  border-radius: 0;
  padding: 0 18px;
  min-height: 40px;
  font-family: inherit;
  font-weight: bold;
  font-size: 13px;
  cursor: pointer;
}

.settings-card button:hover:not(:disabled) { background: var(--secondary); }
.settings-card button:disabled { opacity: 0.5; cursor: default; }

.settings-status {
  margin: 0;
  font-size: 12.5px;
}

.settings-status.status-ok { color: var(--success); }
.settings-status.status-error { color: var(--error); }

/* ---------- login page ---------- */

#login-wrap {
  height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  padding-top: max(1rem, env(safe-area-inset-top));
  padding-bottom: max(1rem, env(safe-area-inset-bottom));
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
}

.login-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  width: 100%;
  max-width: 360px;
  background: var(--surface);
  border: 1px solid var(--panel);
  border-radius: 0;
  padding: 40px 28px 32px;
}

.login-card #logo {
  width: min(280px, 100%);
  height: auto;
  border-radius: 0;
}

.login-sub {
  margin: 0;
  color: var(--warning);
  font-size: 12.5px;
  line-height: 1.5;
  text-align: center;
  max-width: 260px;
}

.login-guest-hint {
  margin: 0;
  color: var(--fg-muted, var(--fg));
  font-size: 12px;
  line-height: 1.5;
  text-align: center;
  max-width: 280px;
  opacity: 0.75;
}

.login-card #password {
  width: 100%;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--panel);
  border-radius: 0;
  padding: 10px 12px;
  font-family: inherit;
  font-size: 16px; /* stops iOS Safari auto-zoom on focus */
}

.login-card #password:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}

.login-card button[type="submit"] {
  width: 100%;
  background: var(--primary);
  color: var(--btn-fg);
  border: none;
  border-radius: 0;
  padding: 0 18px;
  min-height: 44px;
  font-family: inherit;
  font-weight: bold;
  font-size: 14px;
  cursor: pointer;
}

.login-card button[type="submit"]:hover:not(:disabled) { background: var(--secondary); }
.login-card button[type="submit"]:disabled { opacity: 0.5; cursor: default; }

#login-error {
  margin: 0;
  color: var(--error);
  font-size: 12.5px;
  text-align: center;
}

@media (max-width: 640px) {
  body { font-size: 14px; }
  #header { padding-left: 0.75rem; padding-right: 0.75rem; }
  .header-actions button,
  .header-link,
  #sidebar-toggle {
    width: 30px;
    height: 30px;
    font-size: 13px;
  }
}

/* ---------- conversation history sidebar (ChatGPT-style) ---------- */

#sidebar {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: 280px;
  max-width: 82vw;
  background: var(--surface);
  border-right: 1px solid var(--panel);
  display: flex;
  flex-direction: column;
  z-index: 60;
  transform: translateX(-100%);
  transition: transform 0.18s ease;
}

#sidebar.open { transform: translateX(0); }

.sidebar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.9rem 1rem 0.6rem;
  padding-top: max(0.9rem, env(safe-area-inset-top));
  padding-left: max(1rem, env(safe-area-inset-left));
}

.sidebar-title {
  color: var(--warning);
  font-size: 12px;
  font-weight: bold;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

#sidebar-close {
  background: transparent;
  border: none;
  color: var(--warning);
  font-size: 15px;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 0;
}
#sidebar-close:hover { color: var(--fg); background: var(--panel); }

/* Bordered, not filled -- Send is the only bright/filled action anywhere
   in the app (per design.png's hierarchy: one solid accent button per
   view, everything else is an outlined block), so this stays a neutral
   block like every other secondary action instead of competing with it. */
.new-chat-row {
  display: flex;
  gap: 6px;
  margin: 0 0.75rem 0.75rem;
}

.new-chat-btn {
  flex: 1;
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--panel);
  padding: 10px 12px;
  font-family: inherit;
  font-weight: bold;
  font-size: 13px;
  cursor: pointer;
}
.new-chat-btn:hover { color: var(--fg-bright); border-color: var(--secondary); background: var(--panel); }

.model-picker-toggle {
  flex-shrink: 0;
  width: 38px;
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--panel);
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
}
.model-picker-toggle:hover,
.model-picker-toggle.open { color: var(--fg-bright); border-color: var(--secondary); background: var(--panel); }

/* Model picker -- lists DeepSeek + whatever LM Studio has loaded/available
   right now, grouped. Picking a DeepSeek model starts a new chat in this
   tab (that's always been "+ new chat"'s job); picking an LM Studio model
   opens a NEW TAB pinned to it instead, since that's the actual point --
   several local models, each in their own tab, none of them able to step
   on each other (see conversations.py's per-conversation model field). */
#model-picker {
  margin: 0 0.75rem 0.75rem;
  border: 1px solid var(--panel);
  background: var(--bg);
  max-height: 260px;
  overflow-y: auto;
}

.model-picker-group-label {
  padding: 6px 10px 4px;
  color: var(--warning);
  font-size: 10.5px;
  font-weight: bold;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.model-picker-empty {
  padding: 4px 10px 8px;
  color: var(--warning);
  font-size: 11.5px;
}

.model-picker-item {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  background: transparent;
  border: none;
  border-top: 1px solid var(--panel);
  color: var(--fg);
  padding: 8px 10px;
  font-family: inherit;
  font-size: 12.5px;
  text-align: left;
  cursor: pointer;
}
.model-picker-item:hover { background: var(--panel); color: var(--fg-bright); }

.model-picker-item .dot {
  width: 6px;
  height: 6px;
}

.model-picker-item-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.model-picker-item-tag {
  flex-shrink: 0;
  color: var(--accent);
  font-size: 10.5px;
}

#conv-list {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 0 0.5rem 0.75rem;
}

#conv-list:empty::after {
  content: "no saved chats yet";
  color: var(--warning);
  font-size: 12px;
  padding: 0.75rem;
}

.conv-item {
  display: flex;
  align-items: stretch;
  gap: 2px;
  border: 1px solid transparent;
  border-radius: 0;
  background: transparent;
  cursor: pointer;
  transition: background 0.12s ease;
}

.conv-item:hover { background: var(--panel); }

.conv-item.active {
  background: var(--panel);
  border-color: var(--secondary);
}

.conv-main {
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.conv-title {
  color: var(--fg);
  font-size: 13px;
  font-weight: bold;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.conv-meta {
  color: var(--warning);
  font-size: 11px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.conv-actions {
  display: none;
  align-items: center;
  gap: 2px;
  padding-right: 4px;
}

.conv-item:hover .conv-actions { display: flex; }

.conv-actions button {
  background: transparent;
  border: none;
  color: var(--warning);
  font-size: 12px;
  cursor: pointer;
  padding: 4px 6px;
  border-radius: 0;
  line-height: 1;
}
.conv-actions button:hover { color: var(--fg); background: var(--bg); }
.conv-del:hover { color: var(--error); }

/* ---------- sidebar folders (Conversation.group -- a plain tag, not a
   separate entity, see conversations.py) ---------- */

.conv-group { display: flex; flex-direction: column; }

.conv-group-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px 4px;
  cursor: pointer;
  color: var(--warning);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.conv-group-header:hover { color: var(--fg); }

.conv-group-caret {
  display: inline-block;
  font-size: 9px;
  transition: transform 0.12s ease;
}
.conv-group.collapsed .conv-group-caret { transform: rotate(-90deg); }
.conv-group.collapsed .conv-group-items { display: none; }

.conv-group-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.conv-group-count { opacity: 0.7; }

.conv-group-actions { display: none; align-items: center; gap: 2px; }
.conv-group-header:hover .conv-group-actions { display: flex; }
.conv-group-actions button {
  background: transparent;
  border: none;
  color: var(--warning);
  font-size: 11px;
  cursor: pointer;
  padding: 2px 5px;
  line-height: 1;
}
.conv-group-actions button:hover { color: var(--fg); }

.conv-group-items {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

@media (hover: none) {
  .conv-group-actions { display: flex; }
}

.sidebar-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1rem;
  padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
  padding-left: max(1rem, env(safe-area-inset-left));
  border-top: 1px solid var(--panel);
}

#conv-total {
  color: var(--warning);
  font-size: 11px;
}

/* mobile touch devices don't have hover -- show actions always, but keep
   them compact so the title stays readable */
@media (hover: none) {
  .conv-actions { display: flex; }
}

#backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 55;
}

/* history toggle -- same 32px icon-square as the buttons in .header-actions
   (kept as its own rule since it sits in .header-left, not .header-actions);
   hidden entirely once the sidebar becomes a permanent column below. */
#sidebar-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--panel);
  color: var(--warning);
  width: 32px;
  height: 32px;
  padding: 0;
  font-family: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}

@media (min-width: 960px) {
  /* Scoped to the chat page specifically (the only page with #sidebar) --
     a bare "body" selector here was leaking this row-flex layout onto
     login.html and settings.html too, which don't have a #sidebar to
     lay out beside anything. On those pages #login-wrap/#settings-main's
     own centering counted on body being a plain block container; turning
     it into an unconstrained-width flex item broke that; login-wrap
     shrank to its content's width and hugged the left edge instead of
     centering. */
  body:has(#sidebar) {
    display: flex;
    height: 100dvh;
    overflow: hidden;
  }
  #sidebar {
    position: static;
    transform: none;
    width: 280px;
    flex-shrink: 0;
    height: 100dvh;
  }
  #sidebar-toggle, #sidebar-close, #backdrop { display: none !important; }
  #app {
    flex: 1;
    max-width: none;
  }
  /* Full-bleed, not a narrow centered column: every row shares the same
     horizontal padding instead of each carrying its own max-width +
     margin:auto (which centered them independently -- fine at 860px on a
     laptop, but on a wide monitor it left large dead gutters on both
     sides and made the header look like a floating strip instead of a
     proper toolbar spanning the app). #tabbar was missing from this
     block entirely before, so it silently stayed full-width while
     #header above it was centered and narrow -- their edges never lined
     up. One shared rule for every row fixes both at once. */
  #header, #tabbar,
  #transcript, #composer, #status,
  #news-categories-bar, #news-toolbar, #news-results {
    padding-left: 2rem;
    padding-right: 2rem;
  }
}
