Files
beardrive/internal/webapp/frontend/src/style.css
T
5fb834e3a4 fix(webapp): keep the provenance line on mobile, unclipped in History (BEA-70) (#121)
Below 900px the file view hid the who/when/how-hot line outright
(`#meta { display: none }`) and the History run header ellipsised its
note and author to `claude-…` / `Alice <ali…` — the line the product is
differentiated by, gone exactly on the surface people catch up from.

CSS-only, inside the existing `@media (max-width: 900px)` block:

- `#topbar` wraps (`height: auto; min-height: 52px`) and `#meta` takes its
  own full-width row, left-aligned and wrapping. `order: 1` is what keeps
  Search / Share / ⋯ on row 1 — meta precedes them in the DOM, so a bare
  flex-wrap would drag them down. `#meta:empty` keeps folder, dashboard
  and history routes from gaining a blank strip.
- `.hrun-head` wraps; the note loses its 46% cap and both spans stop
  ellipsising, with `.hrun-meta` on its own line under the note and the
  time still on row 1.

Desktop (≥901px) is untouched — every rule lives inside the breakpoint.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 16:38:00 +09:00

1065 lines
70 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* BearDrive viewer — near-black, hairline-crafted, honey accent.
Design language: Linear-grade restraint; the one accent (honey) is spent
only on active state, the live/brand mark, and the palette selection. */
:root {
/* The app is dark-only. Without this, anything the UA paints itself —
scrollbars, autofill, select/date popups, and any control that loses its
author `color` — resolves in the light scheme. That trapdoor is how
BEA-54 turned one dead selector into black-on-black text. */
color-scheme: dark;
--bg: #0a0b0d; /* main ground */
--bg-side: #0c0e10; /* sidebar / panels */
--bg-raise: #15171b; /* palette, modals, elevated cards */
--surface: rgba(255,255,255,.03); /* inputs, ghost buttons */
--hover: rgba(255,255,255,.06); /* row / control hover */
--border: rgba(255,255,255,.07);
--border-2: rgba(255,255,255,.11);
--code-bg: #0d0f12;
--text: #eef0f3;
--text-dim: #9aa0a9; /* AA on both grounds */
--text-faint: #868b93; /* ≥4.5:1 on near-black */
--text-ghost: #666b74; /* decorative icons only */
--accent: #f5a623; /* honey */
--accent-bright: #ffcf85; /* honey text / primary hover */
--accent-dim: #d3861a;
--accent-press: #e0951a;
--glow: rgba(245,166,35,.13);
--add: #4cc38a;
--del: #f26d6d;
--mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
--ui: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI", Roboto, sans-serif;
--r-ctl: 7px;
--r-card: 10px;
--r-over: 14px;
/* Page columns. Three widths for every route — see .page below. */
--page-read: 768px; /* rendered files only (markdown prose) — Tailwind md */
--page-app: 768px; /* structured views: guide, listings, history, insights, settings, admin — Tailwind md */
--page-wide: 1200px; /* data-dense: treemap, coverage matrix, big tables */
--hero-top: clamp(32px, 8vh, 88px); /* one vertical start for short centered states */
}
/* Jersey 10 — the logo face, and only the logo (a pixel face is a logo, not a
UI font). Self-hosted: Vite fingerprints it into static/assets/ alongside
the bundle, so the binary stays self-contained and no request leaves the
hub. SIL OFL; licence sits beside the file in src/assets/.
Deliberately NOT a token: tw.css's @theme is mirrored by the cloud landing's
tokens.css and a drift check fails the build if they diverge. */
@font-face {
font-family: "Jersey 10";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("./assets/jersey-10.woff2") format("woff2");
}
/* Same optical size for whichever face wins the load race. */
@font-face {
font-family: "Logo Fallback";
src: local("Helvetica Neue"), local("Arial"), local("Segoe UI"), local("Roboto");
size-adjust: 73%;
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { height: 100%; margin: 0; }
/* The React shell renders inside #root; make it transparent to layout so
body's flex still governs sidebar + main. */
#root { display: contents; }
body {
display: flex;
background: var(--bg);
color: var(--text);
font: 13px/1.5 var(--ui);
letter-spacing: -.006em;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
::selection { background: var(--glow); color: var(--accent-bright); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 5px; }
:focus-visible { outline-color: var(--accent); }
.admin input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.admin input[aria-invalid="true"]:focus-visible { outline-color: var(--del); }
[role="dialog"] input[aria-invalid="true"] { border-color: var(--del); }
[role="dialog"] input[aria-invalid="true"]:focus-visible { outline-color: var(--del); }
button:disabled, .btn:disabled { cursor: default; }
input[type="checkbox"] { accent-color: var(--accent); width: 20px; height: 20px; }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
.sprite { position: absolute; }
.ico {
width: 16px; height: 16px; flex: none;
stroke: currentColor; stroke-width: 1.6; fill: none;
stroke-linecap: round; stroke-linejoin: round;
}
button, input, a.btn { font-family: inherit; }
/* ---- sidebar ---- */
#sidebar {
width: 264px; min-width: 210px;
background: var(--bg-side);
border-right: 1px solid var(--border);
display: flex; flex-direction: column; overflow: hidden;
}
#vault {
display: flex; align-items: center; gap: 9px;
height: 52px; padding: 0 12px 0 14px;
border-bottom: 1px solid var(--border);
/* Own stacking context above the sections below, so the search tooltip
paints over their borders instead of sliding behind them. */
position: relative; z-index: 45;
}
/* Brand lockup: the mark in honey, the name in text colour, so the accent
lands once. Same mark and same face as the landing page and the docs — the
three surfaces are one identity. */
#vault-badge { flex: none; display: grid; place-items: center; color: var(--accent); }
#vault-name {
font-family: "Jersey 10", "Logo Fallback", var(--ui);
/* Jersey 10 is a condensed pixel face with a single weight: 18px here sets
to the same optical size as 13px of the UI face, and synthetic bold would
fill the gaps between pixels. */
font-size: 18px; font-weight: 400; font-synthesis: none;
letter-spacing: .01em; line-height: 1;
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vault-actions { display: flex; align-items: center; gap: 4px; }
#vault #signout, .icon-btn2 {
width: 28px; height: 28px; border-radius: 6px;
display: inline-flex; align-items: center; justify-content: center;
color: var(--text-ghost); background: transparent; border: none; cursor: pointer; text-decoration: none;
}
#vault #signout:hover, .icon-btn2:hover { color: var(--text); background: var(--hover); }
#vault #signout .ico, .icon-btn2 .ico { width: 16px; height: 16px; }
/* projects */
#projects { flex: none; max-height: 32%; overflow-y: auto; padding: 10px 8px 8px; border-bottom: 1px solid var(--border); }
.nav-head { display: flex; align-items: center; justify-content: space-between; padding: 6px 8px; font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .07em; color: var(--text-faint); }
.nav-add { display: grid; place-items: center; width: 18px; height: 18px; border: none; background: transparent; color: var(--text-ghost); cursor: pointer; border-radius: 5px; }
.nav-add .ico { width: 14px; height: 14px; }
.nav-add:hover { color: var(--text); background: var(--hover); }
#projects ul { list-style: none; margin: 0; padding: 0; }
#projects .row {
display: flex; align-items: center; gap: 9px;
height: 31px; padding: 0 8px; border-radius: 7px;
color: var(--text-dim); cursor: pointer; position: relative;
white-space: nowrap; overflow: hidden;
}
.proj-mark {
width: 17px; height: 17px; border-radius: 5px; flex: none;
display: grid; place-items: center; font-size: 10px; font-weight: 700;
color: #0a0b0d; letter-spacing: -.02em; text-transform: uppercase;
}
/* The project's lucide glyph sits inside the colored square. */
.proj-mark svg { width: 11px; height: 11px; }
/* Menu rows carry their own mark, so they need the same row layout the
trigger has. */
.proj-menu [data-slot="select-item"] { display: flex; align-items: center; gap: 8px; }
#projects .row .label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
#projects .row:hover { background: var(--hover); color: var(--text); }
#projects .row.active { background: var(--glow); color: var(--accent-bright); }
#projects .row.active::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 2px; border-radius: 2px; background: var(--accent); }
/* file tree */
#tree { flex: 1; overflow-y: auto; padding: 8px 8px 24px; }
.tguide { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--border); pointer-events: none; }
#tree .row {
display: flex; align-items: center; gap: 6px;
height: 28px; padding: 0 8px; border-radius: 7px;
color: var(--text-dim); cursor: pointer; position: relative;
white-space: nowrap; overflow: hidden;
}
#tree .row:hover { background: var(--hover); color: var(--text); }
#tree .row.active { background: var(--glow); color: var(--accent-bright); }
#tree .row.active::before { content: ""; position: absolute; left: 0; top: 5px; bottom: 5px; width: 2px; border-radius: 2px; background: var(--accent); }
#tree .chev { width: 14px; height: 14px; flex: none; color: var(--text-ghost); transition: transform .12s; display: flex; align-items: center; justify-content: center; }
#tree .chev .ico { width: 13px; height: 13px; }
#tree .ticon { flex: none; display: flex; color: var(--text-ghost); }
#tree .ticon .ico { width: 15px; height: 15px; }
#tree .row:hover .ticon, #tree .row:hover .chev { color: var(--text-faint); }
#tree .row.active .ticon, #tree .row.active .chev { color: var(--accent); }
#tree .label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
#tree .file .label { font-size: 12.5px; }
#tree .file .chev { visibility: hidden; }
#tree .row.collapsed .chev { transform: rotate(-90deg); }
/* org bar */
.field-err { color: var(--del); font-size: 12px; margin: 6px 2px 0; }
/* Radix/shadcn content utilities set the CSS `translate` property; house
overlays center with `transform`. Both compose — neutralize the utility
so house positioning owns these surfaces. */
.modal, #palette { translate: none; }
/* react-table admin tables ride the .admin-item/.ai-* vocabulary */
.admin-card-table { padding: 0; }
.admin-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.admin-table td .ai-main, .admin-table td a.ai-main, .admin-table td .ai-copy,
.admin-table td .ai-tag {
display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* table-layout: fixed takes its tracks from the first row, so the actions
column needs a real width — otherwise `width: 100%` on the first cell
starves it to nothing and the content of column two spills across it. */
/* table-layout: fixed takes its tracks from the FIRST ROW, which is the
header — sizing td alone did nothing. The last column holds the row's
controls (a role select plus a destructive button), so it gets a real
track and the text column takes the rest. */
.admin-table th:last-child, .admin-table td:last-child { width: 186px; text-align: right; }
.admin-table tr:last-child td { border-bottom: none; }
.admin-table th {
text-align: left; font-size: 11px; font-weight: 600; letter-spacing: .05em;
text-transform: uppercase; color: var(--text-faint); padding: 0;
border-bottom: 1px solid var(--border); user-select: none;
}
.admin-table td { padding: 0; border-bottom: 1px solid var(--border); overflow: hidden; text-overflow: ellipsis; }
.admin-table tr.admin-item { display: table-row; }
.admin-table tr.admin-item td { padding: 8px 10px; }
/* Narrow screens: the table keeps its own horizontal scroll so the page
never scrolls sideways under it. */
.admin-card-table { overflow-x: auto; }
/* The shares table's actions column holds one button, not a select plus one
like the members table. At 186px it stole the room the row's own facts
(who, when, whether it expires) needed, and they truncated to "no exp…". */
.shares-table .admin-table th:last-child, .shares-table .admin-table td:last-child { width: 110px; }
/* ---- file-page "this file is public" banner ---- */
.share-banner {
margin: 0 0 18px; padding: 12px 14px;
border: 1px solid var(--border); border-left: 3px solid var(--accent);
border-radius: var(--r-ctl); background: var(--surface);
}
.share-banner .sb-head {
display: flex; align-items: center; gap: 8px;
font-size: 13px; color: var(--text);
}
.share-banner .sb-head .ico, .share-banner .sb-head svg {
width: 15px; height: 15px; flex: none; color: var(--accent);
}
.share-banner .sb-count { color: var(--text-faint); font-size: 12px; }
.share-banner .sb-note {
margin: 6px 0 10px; font-size: 12.5px; line-height: 1.55;
color: var(--text-faint); max-width: 64ch;
}
.share-banner .sb-link {
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
padding-top: 8px; border-top: 1px solid var(--border);
}
.share-banner .sb-link + .sb-link { margin-top: 8px; }
.share-banner .sb-url {
flex: 1 1 260px; min-width: 0; font-size: 12px; color: var(--text);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.share-banner .sb-meta { font-size: 11.5px; color: var(--text-faint); }
.share-banner .sb-actions { display: flex; align-items: center; gap: 6px; margin-left: auto; }
/* ---- project menu ---- */
.nav-menu { list-style: none; margin: 6px 0 0; padding: 0; }
.nav-menu .row .ico { width: 15px; height: 15px; flex: none; color: var(--text-ghost); }
.nav-menu .row.active .ico { color: var(--accent-bright); }
/* ---- project switcher ---- */
.proj-row { display: flex; align-items: center; gap: 4px; padding: 0 10px 4px 12px; }
#project-select {
flex: 1; min-width: 0; height: 30px; padding: 0 9px;
display: inline-flex; align-items: center; gap: 8px;
border: 1px solid var(--border); border-radius: var(--r-ctl);
background: var(--surface); color: var(--text);
font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer;
white-space: nowrap; outline: none;
}
#project-select > span:last-of-type { overflow: hidden; text-overflow: ellipsis; }
#project-select:hover { background: var(--hover); border-color: var(--border-2); }
#project-select svg { color: var(--text-ghost); }
.proj-menu {
z-index: 80; min-width: var(--radix-select-trigger-width, 200px);
border: 1px solid var(--border-2); border-radius: 9px; padding: 4px;
background: var(--bg-raise); box-shadow: 0 10px 32px rgba(0, 0, 0, .35);
}
.proj-menu [role="option"] { font-size: 12.5px; color: var(--text-dim); border-radius: 6px; outline: none; }
.proj-menu [role="option"][data-highlighted] { background: var(--hover); color: var(--text); }
/* ---- account bar ---- */
#accountbar { position: relative; border-top: 1px solid var(--border); padding: 7px 10px; }
.gh-star {
display: flex; align-items: center; gap: 8px;
padding: 4px 8px; margin-bottom: 2px; border-radius: 7px;
color: var(--text-faint); font-size: 11px; text-decoration: none;
}
.gh-star:hover { background: var(--hover); color: var(--text); }
.gh-star .gh-mark { width: 12px; height: 12px; flex: none; }
.gh-star .ext { margin-left: auto; font-size: 9px; }
#account-btn {
width: 100%; display: flex; align-items: center; gap: 9px; text-align: left;
padding: 6px 8px; border: none; border-radius: 7px;
background: transparent; color: var(--text-dim); cursor: pointer; font: inherit;
}
#account-btn:hover { background: var(--hover); color: var(--text); }
#account-btn .avatar {
width: 26px; height: 26px; flex: none; border-radius: 50%;
display: inline-flex; align-items: center; justify-content: center;
color: #fff; font-size: 12px; font-weight: 700;
}
#account-btn .acct { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
#account-btn .acct b { font-size: 12.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#account-btn .acct small { font-size: 11px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#account-btn > .ico { width: 14px; height: 14px; color: var(--text-ghost); }
#account-menu {
min-width: var(--radix-dropdown-menu-trigger-width, 220px);
padding: 5px; border: 1px solid var(--border-2); border-radius: 9px;
background: var(--bg-raise); box-shadow: 0 10px 32px rgba(0, 0, 0, .35);
display: flex; flex-direction: column; z-index: 80; outline: none;
}
#account-menu [role="menuitem"] { outline: none; }
#account-menu [role="menuitem"][data-highlighted] { background: var(--hover); color: var(--text); }
#account-menu .menu-sec {
padding: 7px 9px 3px; font-size: 10.5px; font-weight: 600; letter-spacing: .04em;
text-transform: uppercase; color: var(--text-faint);
}
#account-menu [role="menuitem"] {
display: flex; align-items: center; gap: 8px; padding: 7px 9px;
border: none; border-radius: 6px; background: transparent; text-align: left;
color: var(--text-dim); font: inherit; font-size: 12.5px; cursor: pointer; text-decoration: none;
}
#account-menu [role="menuitem"]:hover { background: var(--hover); color: var(--text); }
#account-menu [role="menuitem"] b { font-weight: 600; }
#account-menu [role="menuitem"] .ico { width: 15px; height: 15px; }
#account-menu .plan-chip { margin-left: auto; color: var(--accent); border-color: var(--border-2); }
#account-menu #signout { color: var(--del); }
#account-menu #signout:hover { color: var(--del); background: var(--hover); }
/* ---- billing view (managed hubs) ---- */
#billing-view .plan-chip { color: var(--accent); }
.plan-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 700px) { .plan-grid { grid-template-columns: 1fr; } }
.usage-bar { background: var(--surface); border: 1px solid var(--border); border-radius: 4px; height: 6px; overflow: hidden; }
.usage-bar > div { background: var(--accent); height: 100%; }
.plan-price { font-size: 20px; font-weight: 700; margin: 0 0 10px; }
.plan-price small { font-size: 12px; color: var(--text-dim); font-weight: 500; }
.muted-note { color: var(--text-dim); font-size: 13px; }
/* ---- main pane ---- */
#main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
#topbar {
position: relative; display: flex; align-items: center; gap: 9px;
height: 52px; padding: 0 16px; border-bottom: 1px solid var(--border);
}
.icon-btn { display: none; width: 34px; height: 34px; border: none; background: transparent; color: var(--text-dim); cursor: pointer; border-radius: 7px; align-items: center; justify-content: center; }
.icon-btn:hover { color: var(--text); background: var(--hover); }
#crumb { font-size: 12.5px; color: var(--text); font-weight: 500; letter-spacing: -.01em; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#crumb .crumb-seg { color: var(--text-dim); cursor: pointer; }
#crumb .crumb-seg:hover { color: var(--accent-bright); }
#crumb .crumb-sep { color: var(--text-ghost); margin: 0 5px; }
#meta { flex: 1; min-width: 0; font-size: 12px; color: var(--text-faint); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.btn {
display: inline-flex; align-items: center; gap: 6px; flex: none;
height: 30px; padding: 0 11px; border-radius: var(--r-ctl);
border: 1px solid var(--border); background: var(--surface);
color: var(--text-dim); font-size: 12.5px; font-weight: 500; cursor: pointer; text-decoration: none;
}
.btn:hover { background: var(--hover); color: var(--text); border-color: var(--border-2); }
.btn .ico { width: 15px; height: 15px; }
.btn.ghost { color: var(--text-dim); }
/* Radix tooltip card (house style) */
.tipcard {
display: flex; align-items: center; gap: 7px; white-space: nowrap;
padding: 6px 9px; border-radius: 8px; border: 1px solid var(--border-2);
background: var(--surface-solid, var(--bg-raise)); color: var(--text); font-size: 12.5px; font-weight: 500;
box-shadow: 0 8px 24px rgba(0, 0, 0, .35); z-index: 80;
}
.tipcard kbd {
font: 11px var(--ui); color: var(--text-faint);
background: var(--hover); border: 1px solid var(--border-2); border-radius: 5px;
padding: 1px 5px;
}
#more-menu {
position: absolute; right: 12px; top: calc(100% - 4px); z-index: 80;
background: var(--bg-raise); border: 1px solid var(--border-2);
border-radius: var(--r-card); box-shadow: 0 18px 44px -14px rgba(0,0,0,.75);
padding: 6px; min-width: 168px;
}
.more-item { display: block; width: 100%; text-align: left; min-height: 40px; padding: 0 12px; background: transparent; border: none; cursor: pointer; color: var(--text); font: inherit; font-size: 13.5px; border-radius: var(--r-ctl); }
.more-item:hover { background: var(--hover); }
/* The scroll container owns scrolling and the page gutter — never a width.
Width and centering belong to .page (one per view), so every route shares
the same column edges and the gutter never eats into the reading measure. */
#content { flex: 1; overflow-y: auto; padding: 44px 40px 110px; scroll-behavior: smooth; }
.page { width: 100%; max-width: var(--page-app); margin-inline: auto; min-width: 0; }
.page.read { max-width: var(--page-read); }
.page.wide { max-width: var(--page-wide); }
.empty { color: var(--text-faint); text-align: center; margin-top: var(--hero-top); }
.empty-hint { display: block; margin-top: 6px; font-size: 12px; color: var(--text-faint); }
/* ---- onboarding ---- */
/* A hero card inside .page.read, not a page column of its own. */
.onboard { max-width: 560px; margin: var(--hero-top) auto 0; }
.onboard h1 { font-size: 25px; font-weight: 640; letter-spacing: -.02em; margin: 0 0 8px; color: #f4f6f9; }
.onboard > p { color: var(--text-dim); margin: 0 0 28px; font-size: 14px; }
.ob-card { background: var(--bg-side); border: 1px solid var(--border); border-radius: var(--r-card); padding: 20px 22px; margin-bottom: 14px; }
.ob-card h3 { margin: 0 0 6px; font-size: 14.5px; font-weight: 600; }
.ob-card p { margin: 0 0 14px; font-size: 13px; color: var(--text-dim); }
/* The create-here card leads; the agent-paste card follows it. Accent edge
marks which one is the primary path without a second button competing. */
.ob-card.ob-start { border-color: var(--border-2); box-shadow: inset 2px 0 0 var(--accent); }
.ob-card.ob-start .pbtn { margin-top: 2px; }
.ob-alt { margin: 12px 0 0; }
.ob-alt a { color: var(--text-faint); font-size: 12.5px; font-weight: 600; text-decoration: none; }
.ob-alt a:hover { color: var(--text); }
/* primary + secondary buttons */
.pbtn { display: inline-flex; align-items: center; gap: 6px; flex: none; height: 32px; padding: 0 14px; border-radius: var(--r-ctl); border: none; background: var(--accent); color: #241704; font-size: 13px; font-weight: 600; cursor: pointer; white-space: nowrap; text-decoration: none; }
.pbtn:hover { background: var(--accent-bright); }
.pbtn .ico { width: 15px; height: 15px; }
.danger-btn { display: inline-flex; align-items: center; height: 32px; padding: 0 14px; border-radius: var(--r-ctl); border: none; background: #b3382e; color: #fff; font-size: 13px; font-weight: 600; cursor: pointer; }
.danger-btn:hover { background: #c94336; }
/* Tailwind preflight is deliberately off (see tw.css), so copied shadcn
components miss two of its defaults: form controls inheriting the page font
and color (a bare <textarea> otherwise renders monospace, an <input> black),
and the `border` utility resolving to a themed color rather than
currentColor (a near-white hairline on every card). Supply both here, by
slot, so the primitives stay unedited. */
[data-slot="input"],
[data-slot="textarea"] { font: inherit; color: var(--text); }
/* An invalid field keeps a red ring while focused, the way .admin's do —
the accent ring otherwise contradicts the red message below it. */
[data-slot="input"][aria-invalid="true"]:focus-visible,
[data-slot="textarea"][aria-invalid="true"]:focus-visible { border-color: var(--del); }
[data-slot="card"],
[data-slot="dropdown-menu-content"] { border-color: var(--border); }
/* ---- project settings ---- */
/* Sectioned cards: General (editable), About (facts), People, Danger zone. */
.project-settings { display: flex; flex-direction: column; gap: 14px; }
.project-settings > h2 { font-size: 21px; font-weight: 640; letter-spacing: -.02em; margin: 0 0 4px; color: #f4f6f9; }
.ps-form { display: flex; flex-direction: column; gap: 18px; }
.ps-field { display: flex; flex-direction: column; gap: 7px; }
.ps-field label { font-size: 12.5px; color: var(--text-dim); }
.ps-opt { color: var(--text-ghost); font-weight: 400; }
.ps-icon-row { display: flex; align-items: center; gap: 10px; }
.ps-icon-row .proj-mark { width: 26px; height: 26px; border-radius: 7px; }
.ps-icon-row .proj-mark svg { width: 15px; height: 15px; }
.ps-meta { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.ps-meta .field-err { flex: 0 1 auto; margin: 0; }
.ps-count { font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.ps-actions { display: flex; justify-content: flex-end; }
/* Icon picker: a grid inside the existing dropdown-menu, no new primitive. */
.ps-icon-grid { display: grid; grid-template-columns: repeat(6, 30px); gap: 4px; padding: 8px; }
.ps-icon-cell { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 7px; border: 1px solid transparent; background: none; color: var(--text-dim); cursor: pointer; }
.ps-icon-cell svg { width: 16px; height: 16px; }
.ps-icon-cell:hover { background: var(--hover); color: var(--text); }
.ps-icon-cell.active { border-color: var(--accent); color: var(--accent-bright); }
/* Irreversible actions keep their own, clearly-marked card. */
.ps-danger [data-slot="card-title"] { font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em; color: #d2695e; font-weight: 600; }
/* Who can do what — one more card in the same stack. The read-only view is
the same markup with its controls disabled, so nothing shifts when the
caller's level changes. Native selects here rather than the shadcn Select:
the org admin's role picker already uses them, and this table matches it. */
.ps-chip { margin-left: 10px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--text-faint); font-size: 11px; font-weight: 600; letter-spacing: .02em; vertical-align: middle; }
.ps-people h4 { font-size: 12.5px; font-weight: 600; color: var(--text-dim); margin: 0; }
.ps-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 13px; color: var(--text-dim); margin: 0 0 10px; }
.ps-people select { height: 28px; padding: 0 8px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface); color: var(--text); font: inherit; font-size: 12.5px; }
.ps-people select:disabled { opacity: .6; cursor: default; }
.ps-note { color: var(--text-faint); font-size: 12.5px; margin: 0 0 12px; max-width: 56ch; line-height: 1.55; }
.ps-people-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 20px 0 8px; }
.ps-danger p { color: var(--text-dim); font-size: 13px; margin: 0 0 14px; max-width: 52ch; line-height: 1.55; }
.ps-facts { display: grid; grid-template-columns: auto 1fr; gap: 8px 20px; margin: 0; font-size: 13px; }
.ps-facts dt { color: var(--text-faint); }
.ps-facts dd { margin: 0; color: var(--text-dim); }
/* ---- admin panels ---- */
/* width comes from .page (app) — see #content */
.admin h1 { font-size: 21px; font-weight: 640; letter-spacing: -.02em; margin: 0 0 6px; color: #f4f6f9; }
.admin h3 { font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--text-faint); font-weight: 600; margin: 30px 0 10px; }
.admin-lbl { flex: 1 1 100%; margin: 0 0 6px; font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.admin-sub { color: var(--text-dim); font-size: 13.5px; margin: -2px 0 16px; line-height: 1.55; }
.admin-h { display: flex; align-items: center; justify-content: space-between; margin: 30px 0 10px; }
.admin-h h3 { margin: 0; }
.admin-row { display: flex; gap: 9px; margin-bottom: 8px; }
.admin-row input { flex: 1; height: 34px; padding: 0 12px; border-radius: var(--r-ctl); border: 1px solid var(--border); background: var(--surface); color: var(--text); font: inherit; font-size: 13px; outline: none; }
.admin-row input:focus { border-color: var(--accent); background: var(--hover); }
.admin-list { border: 1px solid var(--border); border-radius: var(--r-card); overflow: hidden; background: var(--bg-side); }
/* Belt and braces, and it must come after .admin-list: if a column still
overflows, scroll it rather than clipping a destructive control out of
reach. */
.admin-list.admin-card-table { overflow-x: auto; overflow-y: hidden; }
.admin-item { display: flex; align-items: center; gap: 11px; padding: 11px 14px; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.modal-actions .ai-btn { height: 32px; }
.empty a { color: var(--accent); text-decoration: none; display: inline-block; padding: 6px 10px; }
.empty a:hover { text-decoration: underline; }
.empty h3 { margin: 0 0 8px; font-size: 16px; color: var(--text); }
.ai-copy { text-align: left; background: none; border: 0; padding: 6px 0; cursor: pointer; }
.ai-copy:hover { color: var(--text); }
a.ai-main { color: var(--text-dim); text-decoration: none; padding: 6px 0; }
a.ai-main:hover { color: var(--accent); }
.th-sort { display: block; width: 100%; text-align: left; background: none; border: 0; padding: 6px 10px; font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit; cursor: pointer; }
.th-sort:hover { color: var(--text-dim); }
/* Role controls share one track so the value sits at the same x on every
row, whether it is a select or the static tag on your own row. */
.proj-trigger > [data-slot="select-value"] {
display: block; flex: 1 1 auto; min-width: 0; text-align: left;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.role-cell { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: center; justify-items: start; }
.admin-table td .role-static { text-align: left; }
.role-chip-row { margin: -6px 0 12px; }
.role-chip { margin-left: 0; padding: 2px 8px; border: 1px solid var(--border-2); border-radius: 99px; vertical-align: middle; }
.ext { margin-left: 4px; color: var(--text-faint); font-size: 11px; }
.admin-item:last-child { border-bottom: none; }
.admin-item:hover { background: var(--hover); }
.field-err { flex: 1 1 100%; margin: 6px 0 0; }
.admin-row { flex-wrap: wrap; }
.admin-row input[aria-invalid="true"] { border-color: var(--del); }
.ai-main { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.admin-item > .ai-main { flex: 1 1 55%; min-width: 22ch; }
.admin-item > .ai-tag { flex: 0 0 auto; min-width: 0; max-width: 45%; }
@media (max-width: 1000px) {
/* Between the drawer breakpoint and 1000px the meta still clipped; let it
drop to its own line rather than lose the expiry year. */
.admin-item { flex-wrap: wrap; }
.admin-item > .ai-tag { flex: 1 1 100%; max-width: 100%; }
}
.admin-table td .ai-main { min-width: 0; }
.ai-main.mono { font: 12px var(--mono); color: var(--text-dim); cursor: pointer; }
.ai-tag { font-size: 11.5px; color: var(--text-faint); flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-item select { height: 28px; background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: 6px; padding: 0 8px; font: inherit; font-size: 12.5px; cursor: pointer; }
.admin-item select:hover { border-color: var(--border-2); }
.ai-btn, .ai-del { flex: none; height: 27px; padding: 0 11px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface); color: var(--text-dim); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; }
/* One destructive treatment: Delete removes a whole project and was rendering
as bare text beside a bordered Rename. */
.ai-del { color: var(--del); border-color: rgba(242, 109, 109, .28); }
.ai-del:hover { background: rgba(242, 109, 109, .12); border-color: var(--del); color: #ff8b8b; }
.ai-btn:hover { background: var(--hover); color: var(--text); border-color: var(--border-2); }
.admin-empty { padding: 14px; color: var(--text-faint); font-size: 13px; }
.admin-item.toggle { cursor: pointer; align-items: flex-start; }
.admin-item.toggle .ai-main { white-space: normal; }
.tg-label { font-size: 13.5px; font-weight: 550; color: var(--text); }
.tg-desc { font-size: 12px; color: var(--text-faint); margin-top: 3px; line-height: 1.5; }
.admin-item.toggle input { margin-top: 2px; flex: none; }
/* ---- folder listing ---- */
/* .dirlist width comes from .page (app) */
.dl-title { display: flex; align-items: center; gap: 10px; font-size: 21px; font-weight: 640; letter-spacing: -.02em; margin: 0 0 4px; color: #f4f6f9; }
.dl-title-icon { display: flex; color: var(--accent); }
.dl-title-icon .ico { width: 20px; height: 20px; }
.dl-sub { color: var(--text-faint); font-size: 12.5px; margin: 0 0 18px; }
.dl-items { border: 1px solid var(--border); border-radius: var(--r-card); overflow: hidden; background: var(--bg-side); }
.dl-row { display: flex; align-items: center; gap: 11px; padding: 10px 14px; border-bottom: 1px solid var(--border); cursor: pointer; }
.dl-row:last-child { border-bottom: none; }
.dl-row:hover { background: var(--hover); }
.dl-row .ticon { flex: none; display: flex; color: var(--text-ghost); }
.dl-row .ticon .ico { width: 16px; height: 16px; }
.dl-row:hover .ticon { color: var(--text-faint); }
.dl-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px; color: var(--text); }
/* Peer-written names paint in the order they are stored.
journal.SafeText refuses the bidi format CONTROLS by name, citing Trojan
Source — but a single strong-RTL LETTER is category Lo, passes every ingest
check, and reorders the row on its own: `docא(1).exe` paints as
`doc)1(א.exe` in a plain span. Measured in Chromium, `unicode-bidi: isolate`,
`plaintext` and a `<bdi>` wrapper all leave that reordering intact — they
isolate the name from its neighbours, not its characters from each other.
`isolate-override` does both, which is why it is the one used here.
This is a RENDERING rule, not an ingest one: a Hebrew filename stays legal.
It is scoped to the strings a teammate chooses and a reader is expected to
CHECK — a listing row, a breadcrumb, a history path/note/device, an audit
row in Settings — and never to file content, where RTL prose must render as
prose.
ponytail: an all-RTL name now reads left-to-right for an RTL reader, which
is the cost of one CSS line. Upgrade path if that matters: override only
when the string mixes strong directions, decided per name in JS. */
.dl-name, #crumb, .hpath, .hnote, .hrun-note, .hdev, .ai-main {
unicode-bidi: isolate-override;
direction: ltr;
}
.dl-meta { flex: none; font-size: 12px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
/* read-heat dot: intensity grows with 30-day reads (heatLevel in app.js) */
.heatdot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.heatdot.lvl1 { opacity: .3; }
.heatdot.lvl2 { opacity: .55; }
.heatdot.lvl3 { opacity: .8; }
.heatdot.lvl4 { opacity: 1; box-shadow: 0 0 6px rgba(245, 166, 35, .55); }
.dl-empty { padding: 24px 14px; color: var(--text-faint); font-size: 13px; border: 1px dashed var(--border); border-radius: var(--r-card); text-align: center; }
.dl-h3 { margin: 28px 0 8px; font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--text-faint); font-weight: 600; }
.dl-hlist { border: 1px solid var(--border); border-radius: var(--r-card); background: var(--bg-side); overflow: hidden; max-width: none; }
.dl-hlist .hentry:last-child { border-bottom: none; }
.hentry.clickable { cursor: pointer; }
.hentry.clickable:hover { background: var(--hover); }
.dl-more { margin-top: 10px; }
/* ---- project home: the connect-an-agent guide ---- */
#vault-name.vault-link { cursor: pointer; }
#vault-name.vault-link:hover { color: var(--accent-bright); }
/* .guide width comes from .page (app) */
#account-btn.active { background: var(--glow); }
#account-btn.active .acct b { color: var(--accent-bright); }
.gd-body { margin-top: 18px; }
.gd-desc { margin: 2px 0 8px; color: var(--text-faint); font-size: 13px; line-height: 1.5; }
.gd-list { margin: 4px 0 8px; padding-left: 18px; display: grid; gap: 6px; }
.gd-code { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 10px; margin: 6px 0 10px; padding: 10px 12px; background: var(--bg-raise); border: 1px solid var(--border); border-radius: var(--r-card); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; line-height: 1.6; color: var(--text); }
.gd-code > code { display: block; min-width: 0; overflow-x: auto; white-space: pre; }
.gd-copy { align-self: start; font: inherit; font-family: inherit; font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 6px; border: 1px solid var(--border-2); background: var(--bg-raise); color: var(--text-faint); cursor: pointer; }
.gd-copy:hover { color: var(--accent-bright); border-color: var(--accent-dim); }
.gd-manual { margin: 10px 0 0; }
.gd-manual > summary { display: inline-block; font-size: 12.5px; font-weight: 600; color: var(--text-faint); cursor: pointer; padding: 4px 0; }
.gd-manual > summary::before { content: "▸ "; color: var(--text-ghost); }
.gd-manual[open] > summary::before { content: "▾ "; }
.gd-manual > summary:hover { color: var(--text); }
.home-insights { margin-top: 30px; padding-top: 22px; border-top: 1px solid var(--border); }
/* ---- insights (read×write matrix) ---- */
/* .insights width comes from .page (app) */
.in-title { font-size: 21px; font-weight: 640; letter-spacing: -.02em; margin: 0 0 4px; color: #f4f6f9; }
.in-title .in-scope { color: var(--text-ghost); font-weight: 500; font-size: 15px; }
/* The project dashboard title carries the project's mark; its description
(when it has one) sits directly under the name. */
.gd-head { display: flex; align-items: center; gap: 9px; }
.gd-head .proj-mark { width: 22px; height: 22px; border-radius: 6px; }
.gd-head .proj-mark svg { width: 13px; height: 13px; }
.in-desc { color: var(--text-dim); font-size: 13.5px; line-height: 1.55; margin: 0 0 10px; max-width: 62ch; }
/* Zero files: one empty state in place of the lens switcher and all three
panels, so nothing draws a frame around no data. */
.in-blank { display: grid; justify-items: center; gap: 10px; padding: 40px 18px; margin-top: 14px; max-width: 760px; }
.in-blank p { margin: 0; max-width: 52ch; line-height: 1.55; }
.in-blank p:first-child { color: var(--text); font-size: 14.5px; font-weight: 600; }
.in-blank .pbtn { margin-top: 6px; }
.in-lens { display: flex; gap: 6px; margin: 0 0 14px; }
.in-lens-btn { font: inherit; font-size: 12px; padding: 5px 12px; border-radius: 999px; border: 1px solid var(--border); background: none; color: var(--text-faint); cursor: pointer; }
.in-lens-btn:hover { color: var(--text); }
.in-lens-btn.active { color: var(--accent); border-color: var(--accent); }
.in-chart { width: 100%; max-width: 760px; height: auto; border: 1px solid var(--border); border-radius: var(--r-card); background: var(--bg-side); margin-bottom: 6px; }
.in-axis { stroke: var(--border); stroke-width: 1; }
.in-threshold { stroke: var(--border); stroke-width: 1; stroke-dasharray: 4 4; }
.in-danger-zone { fill: rgba(242, 109, 109, .05); }
.in-label { fill: var(--text-ghost); font-size: 11px; }
.in-quad { fill: var(--text-ghost); font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; }
.in-quad-danger { fill: #e07070; }
.in-pt { fill: var(--accent); opacity: .5; cursor: pointer; }
.in-pt:hover { opacity: 1; }
.in-pt.cold { fill: var(--text-ghost); opacity: .25; }
.in-pt.danger { fill: #e05d5d; opacity: .6; }
/* basename beside a hot+stale dot; pointer-events off so it never eats the click */
.in-pt-label { fill: var(--text-faint); font-size: 11px; pointer-events: none; }
/* the "dot size = …" caption, right-aligned in the panel heading — same
max-width as .in-chart so it lands over the plot's right edge, not the
column's */
.in-h3-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; max-width: 760px; }
.in-cap { font-size: 11.5px; color: var(--text-faint); font-weight: 400; text-transform: none; letter-spacing: 0; }
/* treemap */
.in-treemap { background: #0c0d10; }
.in-tm-group { fill: none; stroke: var(--border); stroke-width: 1; cursor: pointer; pointer-events: all; }
.in-tm-glabel { fill: var(--text-faint); font-size: 10px; text-transform: uppercase; letter-spacing: .05em; cursor: pointer; }
.in-tm-cell { cursor: pointer; }
.in-tm-cell:hover { stroke: #fff; stroke-width: 1; }
.in-tm-label { fill: #0c0d10; font-size: 10.5px; font-weight: 620; cursor: pointer; pointer-events: none; }
/* hot path */
.in-hotpath { border: 1px solid var(--border); border-radius: var(--r-card); background: var(--bg-side); overflow: hidden; }
.in-hp-row { display: flex; align-items: center; gap: 10px; padding: 6px 12px; border-bottom: 1px solid var(--border); cursor: pointer; }
.in-hp-row:last-child { border-bottom: none; }
.in-hp-row:hover { background: var(--hover); }
.in-hp-name { flex: 0 0 300px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; color: var(--text); }
.in-hp-name.danger { color: var(--accent); }
.in-hp-bar { flex: 1; display: flex; height: 10px; border-radius: 3px; overflow: hidden; }
.in-hp-agent { background: var(--accent); }
.in-hp-human { background: #5b8def; }
/* share-link reads: a third hue, never the danger red (that means hot+stale) */
.in-hp-share { background: #b478e8; }
/* a read row whose file left the project — labelled, never silently dropped */
.in-hp-gone { flex: none; font-size: 11.5px; color: var(--text-ghost); white-space: nowrap; }
.in-hp-count { flex: none; width: 40px; text-align: right; font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.in-legend { margin: 8px 2px 0; font-size: 11.5px; color: var(--text-faint); }
.in-sw { display: inline-block; width: 10px; height: 10px; border-radius: 2px; vertical-align: -1px; }
.in-sw.agent { background: var(--accent); }
.in-sw.human { background: #5b8def; }
.in-sw.share { background: #b478e8; }
/* the treemap's freshness scale: one gradient bar, not a row of chips */
.in-sw-age { width: 84px; margin: 0 5px; }
/* The scale is off in this scope: grey the swatch out rather than recompute
the gradient, so the legend looks as inert as the cells now are. */
.in-sw-flat { filter: grayscale(1); opacity: .45; }
.in-tm-range { margin-left: 14px; color: var(--text-ghost); }
/* coverage matrix */
.in-matrix rect { transition: opacity .1s; }
.in-matrix rect:hover { opacity: .85; }
/* ---- history ---- */
/* The filter bar. Wraps rather than scrolls: every control has to stay
reachable on a phone, and a horizontally scrolled toolbar hides the
Clear that gets you back out. */
.hfilters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 0 0 12px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.hf-search { position: relative; display: flex; align-items: center; flex: 1 1 200px; min-width: 160px; }
.hf-search .ico { position: absolute; left: 9px; width: 14px; height: 14px; color: var(--text-ghost); pointer-events: none; }
.hf-search input { height: 30px; padding-left: 29px; font-size: 12.5px; border-radius: var(--r-ctl); background: var(--surface); }
.hf-search input::-webkit-search-cancel-button { filter: invert(.6); }
.hf-user { height: 30px; max-width: 190px; padding: 0 8px; border-radius: var(--r-ctl); border: 1px solid var(--border); background: var(--surface); color: var(--text); font: inherit; font-size: 12.5px; cursor: pointer; }
.hf-dates { display: flex; align-items: center; gap: 6px; }
.hf-lbl { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--text-ghost); }
.hf-date { width: 140px; height: 30px; font-size: 12.5px; border-radius: var(--r-ctl); background: var(--surface); }
.hf-date::-webkit-calendar-picker-indicator { filter: invert(.6); cursor: pointer; }
.hf-dash { color: var(--text-ghost); }
.hf-clear { height: 30px; padding: 0 10px; border: none; border-radius: var(--r-ctl); background: none; color: var(--text-dim); font: inherit; font-size: 12.5px; cursor: pointer; }
.hf-clear:hover { color: var(--text); background: var(--hover); }
.hf-clear-empty { margin-top: 12px; }
/* .history width comes from .page (app) */
.hentry { padding: 11px 12px; border-bottom: 1px solid var(--border); --hindent: 72px; }
.hentry:hover { background: rgba(255,255,255,.015); }
.hline { display: flex; gap: 10px; align-items: center; }
/* The kind is a status badge, not a control: a fixed-width pill of text,
so nothing in the row's leftmost slot has the size or shape of a
disclosure toggle. --hindent keeps meta and note aligned under the path. */
.hkind { flex: none; width: 62px; white-space: nowrap; text-align: center; font-size: 10px; text-transform: uppercase; letter-spacing: .06em; font-weight: 600; padding: 2px 6px; border-radius: 4px; color: var(--add); background: rgba(76,195,138,.12); }
.hentry.edit .hkind { color: var(--accent-bright); background: var(--glow); }
.hentry.delete .hkind { color: #ff8b8b; background: rgba(242,109,109,.12); }
.hpath { font-weight: 500; cursor: pointer; color: var(--text); font-size: 13px; }
.hpath:hover { color: var(--accent-bright); }
.htime { margin-left: auto; color: var(--text-faint); font-size: 12px; font-variant-numeric: tabular-nums; }
/* Foot of a paged feed: says out loud that older changes exist, which is
the whole point — the list used to just stop. */
.hmore { display: flex; margin: 14px auto; }
.hmore:disabled { opacity: .6; cursor: default; }
.hmeta { display: flex; align-items: center; gap: 14px; margin-top: 4px; padding-left: var(--hindent); font-size: 12px; color: var(--text-dim); }
.hdev, .hsize { color: var(--text-faint); }
.hsize { font-variant-numeric: tabular-nums; white-space: nowrap; flex: none; }
.hnote { margin-top: 4px; padding-left: var(--hindent); font-size: 12px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
.hnote:hover { color: var(--text); }
.hnote.open { white-space: normal; overflow-wrap: anywhere; }
/* The one real disclosure in a row — so it gets the chevron, and turns. */
.hnote::before { content: "›"; display: inline-block; margin-right: 5px; color: var(--text-ghost); transition: transform .12s; }
.hnote.open::before { transform: rotate(90deg); }
.hnote a { color: var(--accent-bright); text-decoration: none; }
.hnote a:hover { text-decoration: underline; }
/* ---- run groups (one agent session = one card) ---- */
.hrun { border: 1px solid var(--border); border-radius: var(--r-card); background: var(--bg-side); margin: 10px 0; overflow: hidden; }
.hrun-head { display: flex; align-items: center; gap: 9px; width: 100%; padding: 9px 12px; color: var(--text); font-size: 12.5px; }
.hrun-toggle { display: flex; flex: none; padding: 2px; border: none; border-radius: 4px; background: none; color: var(--text-faint); cursor: pointer; }
.hrun-toggle:hover { color: var(--text); background: var(--hover); }
.hrun-toggle .ico { width: 13px; height: 13px; }
.hrun-note { font-weight: 560; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46%; }
.hrun-note a { color: var(--accent-bright); text-decoration: none; }
.hrun-note a:hover { text-decoration: underline; }
.hrun-meta { color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hrun-time { margin-left: auto; flex: none; color: var(--text-faint); font-variant-numeric: tabular-nums; }
/* Rows inside a card don't repeat the card's own border or note. */
.hrun-body { border-top: 1px solid var(--border); }
.hrun-body .hentry:last-child { border-bottom: none; }
/* ---- restore / remove ---- */
.hrestore-btn, .hremove-btn { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; padding: 2px 8px 2px 5px; border: 1px solid var(--border); border-radius: 5px; background: none; color: var(--text-faint); font: inherit; font-size: 12px; cursor: pointer; }
.hrestore-btn:hover { color: var(--accent-bright); border-color: var(--border-2); background: var(--hover); }
/* Removing takes a file away on every device, so its hover reads as danger
rather than as the neighbouring restore. */
.hremove-btn:hover { color: var(--del); border-color: rgba(242, 109, 109, .38); background: var(--hover); }
.hrestore-btn:disabled, .hremove-btn:disabled { opacity: .5; cursor: default; }
.hrestore-btn .ico, .hremove-btn .ico { width: 12px; height: 12px; }
/* ---- a row's controls: diff (per-file only) + this version's handles ---- */
.hactions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 6px 0 0 23px; }
.hdiff-btn, .hver-btn { display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px 2px 4px; border: 1px solid var(--border); border-radius: 5px; background: none; color: var(--text-faint); font: inherit; font-size: 12px; cursor: pointer; text-decoration: none; }
.hdiff-btn:hover, .hver-btn:hover { color: var(--text); border-color: var(--border-2); background: var(--hover); }
.hdiff-btn .ico, .hver-btn .ico { width: 12px; height: 12px; }
/* Not a control — it takes its own line so the buttons under every row
start at the same place. */
.hdiff-none { flex-basis: 100%; font-size: 12px; color: var(--text-ghost); }
.dv { margin: 8px 0 2px 23px; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.dv-msg { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 9px 11px; font-size: 12px; color: var(--text-faint); }
.dv-dl { display: flex; gap: 12px; }
.dv-msg a { color: var(--accent-bright); text-decoration: none; }
.dv-msg a:hover { text-decoration: underline; }
.dv-head { display: flex; align-items: center; gap: 10px; padding: 5px 11px; border-bottom: 1px solid var(--border); font-size: 11px; font-variant-numeric: tabular-nums; }
.dv-add { color: var(--add); font-weight: 600; }
.dv-del { color: var(--del); font-weight: 600; }
.dv-same { color: var(--text-ghost); }
/* The diff scrolls inside its own box; the page body never scrolls sideways. */
.dv-body { overflow-x: auto; padding: 4px 0; }
.dv-line { display: flex; font-family: var(--mono); font-size: 12px; line-height: 1.55; white-space: pre; }
.dv-n { flex: none; width: 34px; padding-right: 8px; text-align: right; color: var(--text-ghost); user-select: none; font-variant-numeric: tabular-nums; }
.dv-mark { flex: none; width: 16px; text-align: center; user-select: none; }
.dv-text { padding-right: 12px; }
.dv-ins { background: rgba(76,195,138,.10); color: var(--add); }
.dv-rm { background: rgba(242,109,109,.10); color: #ff8b8b; }
.dv-ctx { color: var(--text-dim); }
/* ---- command palette ---- */
#palette { position: fixed; top: 12vh; left: 50%; transform: translateX(-50%); z-index: 151; display: block; width: min(560px, 92vw); background: var(--bg-raise); border: 1px solid var(--border-2); border-radius: var(--r-over); box-shadow: 0 24px 70px -18px rgba(0,0,0,.8); overflow: hidden; outline: none; padding: 0; }
#palette-inputwrap { display: flex; align-items: center; gap: 11px; padding: 14px 16px; border-bottom: 1px solid var(--border); }
#palette-inputwrap [data-slot="command-input-wrapper"] { flex: 1; display: flex; border-bottom: 0; padding: 0; height: auto; }
#palette-inputwrap [data-slot="command-input-wrapper"] > svg:not(.ico) { display: none; }
/* Never anchor palette rules on an id handed to a cmdk primitive: cmdk
spreads caller props and then writes its own generated `id`, so
`#palette-input` / `#palette-results` silently stopped matching at the
cmdk migration (b7ce76b) and the input fell back to the UA's black
`fieldtext`. `#palette` / `#palette-inputwrap` are our own elements and
survive; `[cmdk-*]` are cmdk's documented styling hooks. (BEA-54) */
#palette input, #palette input:focus { flex: 1; width: 100%; border: none; background: transparent; box-shadow: none; color: var(--text); font: inherit; font-size: 15px; letter-spacing: -.01em; outline: none; padding: 0; }
#palette input::placeholder { color: var(--text-ghost); }
#palette-inputwrap .ico { width: 17px; height: 17px; color: var(--text-faint); }
#palette [cmdk-list] { list-style: none; margin: 0; padding: 8px; max-height: 46vh; overflow-y: auto; }
#palette [cmdk-item] { display: flex; align-items: center; gap: 11px; height: 38px; padding: 0 10px; border-radius: 9px; cursor: pointer; color: var(--text-dim); font-size: 13.5px; }
#palette [cmdk-item][data-selected="true"] { background: var(--glow); }
#palette [cmdk-item][data-selected="true"] .picon { color: var(--accent); }
#palette [cmdk-item][data-selected="true"] .plabel { color: var(--accent-bright); }
#palette [cmdk-item][data-selected="true"] .plabel b { color: var(--accent-bright); }
#palette [cmdk-item] .picon { width: 18px; flex: none; display: flex; justify-content: center; color: var(--text-faint); }
#palette [cmdk-item] .picon .ico { width: 15px; height: 15px; }
#palette [cmdk-item] .plabel { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text); }
#palette [cmdk-item] .plabel b { color: var(--accent-bright); font-weight: 600; }
#palette [cmdk-item] .pkind { flex: none; font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-ghost); }
#palette [cmdk-list] .pempty { color: var(--text-faint); cursor: default; justify-content: center; height: auto; padding: 14px; }
#palette-hint { padding: 9px 16px; border-top: 1px solid var(--border); font-size: 11px; color: var(--text-ghost); }
/* ---- modal ---- */
[data-slot="dialog-overlay"] { position: fixed; inset: 0; background: rgba(6,7,9,.6); backdrop-filter: blur(3px); z-index: 150; }
.modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 151; display: block; background: var(--bg-raise); border: 1px solid var(--border-2); border-radius: var(--r-over); padding: 22px 24px; width: min(460px, calc(100vw - 40px)); box-shadow: 0 24px 70px -18px rgba(0,0,0,.8); outline: none; }
.modal h3 { margin: 0 0 10px; font-size: 16px; font-weight: 620; letter-spacing: -.01em; }
.modal p { margin: 0 0 16px; font-size: 13.5px; color: var(--text-dim); line-height: 1.55; }
.modal p b { color: var(--text); }
.modal-url { font: 12px var(--mono); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-ctl); padding: 9px 11px; color: var(--text-dim); word-break: break-all; margin-bottom: 16px; }
.modal-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.modal-expiry { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; font-size: 12.5px; color: var(--text-dim); }
.modal-expiry select { height: 28px; background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: 6px; padding: 0 8px; font: inherit; font-size: 12.5px; cursor: pointer; }
.modal-expiry select:disabled { opacity: .6; cursor: default; }
.modal-expiry-note { margin-left: auto; color: var(--text-dim); }
.modal-label { display: block; font-size: 12.5px; color: var(--text-dim); margin: 0 0 6px; }
.modal-msg { margin: 0 0 16px; font-size: 13.5px; color: var(--text-dim); line-height: 1.55; }
.modal-input { width: 100%; height: 36px; padding: 0 12px; border-radius: var(--r-ctl); border: 1px solid var(--border); background: var(--surface); color: var(--text); font: inherit; font-size: 14px; margin-bottom: 16px; outline: none; }
.modal-input:focus { border-color: var(--accent); background: var(--hover); }
/* starting-point picker (new-project dialog) — native radios, styled with
the accent token; no extra ui/* component for three options. */
.start-points { border: 0; margin: 0 0 18px; padding: 0; }
.start-points legend { padding: 0; }
.start-point { display: flex; align-items: flex-start; gap: 10px; padding: 9px 11px; border: 1px solid var(--border); border-radius: var(--r-ctl); background: var(--surface); cursor: pointer; margin-bottom: 6px; }
.start-point:hover { background: var(--hover); }
.start-point.on { border-color: var(--accent); background: var(--hover); }
.start-point input { accent-color: var(--accent); margin: 2px 0 0; flex: none; }
.sp-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sp-title { font-size: 13.5px; color: var(--text); display: flex; align-items: center; gap: 8px; }
.sp-rec { font-size: 10.5px; letter-spacing: .02em; text-transform: uppercase; color: var(--accent); border: 1px solid var(--accent); border-radius: 999px; padding: 0 6px; line-height: 15px; }
.sp-blurb { font-size: 12px; color: var(--text-dim); overflow-wrap: anywhere; }
/* Extra space separates "seed something" from "seed nothing" — the two rows
below it produce the same empty project and differ only in intent. A
hairline was tried here first and dropped: --border is 7% white, which at
1px in a gap renders as nothing. The gap is the cue that actually reads,
and both rows say "nothing is seeded" in their own words anyway. */
.start-point.sp-rule { margin-top: 16px; }
/* Five rows can outgrow a short viewport; the dialog scrolls rather than
pushing Create off-screen. */
.modal { max-height: calc(100vh - 32px); overflow-y: auto; }
/* The reassurance on the connect guide — the question people actually have
when they point BearDrive at notes they already care about. */
.gd-note { margin: -4px 0 16px; font-size: 13px; color: var(--text-dim); border-left: 2px solid var(--accent); padding-left: 11px; line-height: 1.55; }
/* ---- toast ---- */
[data-sonner-toast] { background: var(--bg-raise) !important; color: var(--text) !important; border: 1px solid var(--border-2) !important; border-radius: 10px !important; font-size: 13.5px !important; box-shadow: 0 18px 44px -12px rgba(0,0,0,.7) !important; }
[data-sonner-toast][data-type="error"] { border-color: rgba(242,109,109,.5) !important; color: #ffb0aa !important; }
#sb-backdrop { display: none; }
/* ---- responsive ----
900px covers tablet portrait (768) and phone landscape (844): with the
fixed 264px sidebar plus the full topbar those widths overflow the page
(measured 849px needed at 768) — they need the off-canvas/collapsed
chrome just like phones. */
@media (max-width: 900px) {
#sidebar { position: fixed; z-index: 60; top: 0; left: 0; height: 100%; transform: translateX(-100%); transition: transform .2s ease; box-shadow: 0 0 40px rgba(0,0,0,.6); }
body.sb-open #sidebar { transform: translateX(0); }
body.sb-open #sb-backdrop { display: block; position: fixed; inset: 0; background: rgba(0,0,0,.55); z-index: 50; }
.icon-btn, #search-btn { display: inline-flex; width: 44px; height: 44px; }
#content { padding: 24px 18px 70px; }
#topbar { padding: 0 8px; gap: 4px; }
.btn .lbl { display: none; }
#topbar .btn { min-width: 44px; min-height: 44px; padding: 0; justify-content: center; gap: 0; }
#topbar .btn .ico { width: 18px; height: 18px; }
#more-btn:not([hidden]) { display: inline-flex; }
#history-btn, #upload-btn, #download { display: none !important; }
/* Who/when/how-hot is the line people catch up from, so it survives to a
phone on its own row instead of being hidden. `order: 1` is load-bearing:
meta sits before the actions in the DOM, so a bare flex-wrap would drag
Search/Share/⋯ down with it. The crumb keeps flex:1 and stays the spacer
that pins them to the top-right corner. */
#topbar { flex-wrap: wrap; height: auto; min-height: 52px; }
#meta {
order: 1; flex: 1 1 100%; text-align: left;
white-space: normal; overflow: visible; padding: 0 0 8px;
}
/* Folder, dashboard and history routes carry no meta — without this they
gain a blank strip under the crumb. */
#meta:empty { display: none; }
#crumb { flex: 1; }
#vault { padding: 0 8px 0 12px; }
.icon-btn2, #signout, #tree .row, #projects .row { height: 44px; }
#account-btn { min-height: 44px; }
#project-select { min-height: 44px; }
.nav-add { min-width: 44px; min-height: 44px; }
/* Columns already collapse on their own (.page is width: 100%); only
content that can't shrink needs its own escape hatch. */
.markdown table, pre.plain { display: block; overflow-x: auto; max-width: 100%; }
/* Admin rows: 27-28px selects/buttons are too small to tap; let rows
wrap so the controls keep room next to long names/URLs. */
.admin-item { flex-wrap: wrap; row-gap: 8px; padding: 12px 14px; }
.admin-item select { height: 44px; }
/* 30px controls are too small to tap; the bar already wraps, so growing
them costs nothing but a second row. */
.hf-search input, .hf-user, .hf-date, .hf-clear { height: 44px; }
.hf-dates { flex: 1 1 100%; }
.hf-date { flex: 1; width: auto; min-width: 0; }
/* The desktop run header ellipsises the note at 46% and the meta at
whatever is left, which on a phone clips to `claude-…` / `Alice <ali…` —
the two fields the row exists to carry. Let it wrap instead; `order: 1`
drops the meta below the note while the time stays on the first row. */
.hrun-head { flex-wrap: wrap; row-gap: 4px; }
.hrun-note { max-width: none; white-space: normal; overflow: visible; }
.hrun-meta { order: 1; flex: 1 1 100%; white-space: normal; overflow: visible; }
.ai-btn, .ai-del { height: auto; min-height: 44px; padding: 0 12px; }
/* react-table renders rows as `display: table-row`, which makes every
flex rule above inert and lets the last column (Remove) fall outside
the card, where `.admin-card-table { overflow: hidden }` clips it —
one long email made member removal impossible on every row. Stack the
table into rows instead; a nested horizontal scroller on touch would
be undiscoverable. */
.admin-table thead { display: none; }
.admin-table, .admin-table tbody, .admin-table td { display: block; width: auto; }
.admin-table tr.admin-item { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.admin-table tr.admin-item td { padding: 0; border-bottom: none; }
.admin-table tr.admin-item td:first-child { flex: 1 1 100%; width: auto; }
/* Beats `.admin-table th:last-child, td:last-child` (0,0,2,1): stacked rows
are flex children, so the desktop track and right-alignment are wrong. */
.admin-table tr.admin-item td:last-child { width: auto; text-align: left; }
/* Radix menu items miss the 44px floor the rest of the app meets. */
[data-slot="dropdown-menu-item"] { min-height: 44px; }
/* The rename row put a 34px input beside a 44px button and truncated the
value; stack them at full height instead. */
/* The projects list was capped at 32% of the drawer and sliced "Settings"
in half on every phone; let the tree below absorb the pressure instead. */
#projects { flex: 0 1 auto; max-height: none; }
.admin-row { flex-wrap: wrap; }
.admin-row input { flex: 1 1 100%; min-height: 44px; }
.admin-row button { flex: 0 0 auto; align-self: flex-start; min-height: 44px; }
/* No hover on touch: a truncated email/URL would be unreadable — wrap
it. The name/URL takes the whole row (tag + buttons drop below):
without full-row basis, a long flex:none .ai-tag starves the URL down
to one character per line. break-word prefers natural break points
(@, .) and still splits long unbroken URLs when it must. */
.admin-item .ai-main { flex: 1 1 100%; white-space: normal; overflow-wrap: anywhere; }
/* Beats the desktop truncation rule (0,2,1) inside the stacked table. */
.admin-table td { white-space: normal; }
.admin-table td .ai-main, .admin-table td a.ai-main, .admin-table td .ai-copy, .admin-table td .ai-tag {
white-space: normal; overflow-wrap: anywhere;
}
/* Invite meta (uses, expiry) is the only triage signal an owner has; it was
truncating to "· unus…" on every phone-width row. */
.admin-item .ai-tag { flex: 1 1 100%; max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
/* The invite row's primary action was a 28px target. */
.ai-copy { min-height: 44px; display: block; padding: 12px 0; white-space: normal; overflow-wrap: anywhere; text-overflow: clip; }
a.ai-main { min-height: 44px; display: flex; align-items: center; }
/* The code-block Copy button needs a real touch target; give one-line
blocks the height to hold it. */
.gd-code { min-height: 62px; padding-top: 12px; padding-bottom: 12px; }
.gd-copy { min-height: 44px; padding: 0 14px; }
.gd-tab { min-height: 44px; }
.in-lens-btn { min-height: 44px; padding: 0 14px; }
.modal-input { height: 44px; }
.modal-actions button { height: auto; min-height: 44px; }
.modal-expiry select { height: 44px; }
.pbtn { height: auto; min-height: 44px; }
#palette [cmdk-item] { height: auto; min-height: 44px; }
.more-item { min-height: 44px; }
}
/* The destructive Revoke must not sit flush against the safe Done on a
touch row — push it to the far side (share dialog only; prompts and
confirms have no .ai-del). */
.modal-actions .ai-del { margin-right: auto; }
@media (max-width: 430px) {
/* The name still wins the row — but the meta wraps under it instead of
disappearing: hiding it left an unexplained coloured dot as the only
signal. Indent 27px (16px icon + 11px gap) to align under the name. */
.dl-row { flex-wrap: wrap; row-gap: 2px; }
.dl-meta { flex: 1 1 100%; padding-left: 27px; }
.ai-tag { font-size: 11px; }
.htime { white-space: nowrap; font-size: 12px; }
.hline { flex-wrap: wrap; }
/* 78px of indent is a fifth of a 360px row, and the wrapped .hline has
nothing left to align under anyway. */
.hentry { --hindent: 0px; }
/* Four share-dialog buttons don't fit one row at 360 — the destructive
Revoke takes its own line rather than sitting 9px from Done. */
.modal-actions .ai-del { flex: 0 0 100%; justify-content: center; text-align: center; }
}
/* ---- markdown reading view ---- */
/* .markdown is typography only; the column is .page.read around it */
.markdown h1, .markdown h2, .markdown h3, .markdown h4 { color: #f4f6f9; line-height: 1.25; letter-spacing: -.018em; margin: 1.5em 0 .5em; text-wrap: balance; }
.markdown h1:first-child { margin-top: 0; }
.markdown h1 { font-size: 1.85em; font-weight: 660; letter-spacing: -.024em; }
.markdown h2 { font-size: 1.32em; font-weight: 620; margin-top: 1.7em; }
.markdown h3 { font-size: 1.08em; font-weight: 620; }
.markdown p, .markdown li { color: #c6cbd3; font-size: 14.5px; line-height: 1.72; }
.markdown p { margin: 0 0 1em; }
.markdown strong { color: var(--text); font-weight: 620; }
.markdown a { color: var(--accent-bright); text-decoration: none; border-bottom: 1px solid rgba(245,166,35,.28); }
.markdown a:hover { border-bottom-color: var(--accent); }
.markdown ul, .markdown ol { margin: 0 0 1em; padding-left: 1.4em; }
.markdown li { margin-bottom: .4em; }
.markdown li::marker { color: var(--text-ghost); }
.markdown code { background: var(--hover); border: 1px solid var(--border); padding: .1em .4em; border-radius: 5px; font: 12.5px/1.5 var(--mono); color: #e4d9c4; }
.markdown pre { background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--r-card); padding: 14px 16px; overflow-x: auto; margin: 1.3em 0; }
.markdown pre code { background: none; border: none; padding: 0; color: #c6cbd3; }
.markdown blockquote { margin: 1.3em 0; padding: .3em 1em; border-left: 2px solid var(--accent); background: linear-gradient(90deg, var(--glow), transparent); border-radius: 0 8px 8px 0; color: #d8cdb6; }
.markdown blockquote p { margin: .3em 0; color: #d8cdb6; }
.markdown table { border-collapse: collapse; margin: 1.3em 0; font-size: 13.5px; }
.markdown th, .markdown td { border-bottom: 1px solid var(--border); padding: 9px 13px; text-align: left; }
.markdown th { color: var(--text-faint); font-size: 11px; text-transform: uppercase; letter-spacing: .05em; font-weight: 600; border-bottom-color: var(--border-2); }
.markdown tr:hover td { background: rgba(255,255,255,.02); }
.markdown img { max-width: 100%; border-radius: 8px; border: 1px solid var(--border); }
.markdown hr { border: none; border-top: 1px solid var(--border); margin: 2.2em 0; }
/* Frontmatter key/value table (server-rendered from a doc's YAML header). */
.markdown table.frontmatter { margin: 0 0 1.8em; font-size: 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; border-collapse: separate; border-spacing: 0; }
.markdown table.frontmatter th { text-transform: none; letter-spacing: 0; font-size: 11.5px; color: var(--text-faint); font-weight: 600; text-align: left; white-space: nowrap; vertical-align: top; padding: 6px 14px 6px 12px; border-bottom: 1px solid var(--border); }
.markdown table.frontmatter td { color: var(--text-dim); padding: 6px 12px 6px 0; border-bottom: 1px solid var(--border); }
.markdown table.frontmatter tr:last-child th, .markdown table.frontmatter tr:last-child td { border-bottom: none; }
.markdown table.frontmatter code { white-space: pre-wrap; font-size: 11px; }
.markdown .admin input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.admin input[aria-invalid="true"]:focus-visible { outline-color: var(--del); }
[role="dialog"] input[aria-invalid="true"] { border-color: var(--del); }
[role="dialog"] input[aria-invalid="true"]:focus-visible { outline-color: var(--del); }
button:disabled, .btn:disabled { cursor: default; }
input[type="checkbox"] { accent-color: var(--accent); }
/* rendered HTML files: a sandboxed page in a framed viewport */
.htmlview, .pdfview { display: block; width: 100%; height: calc(100vh - 150px); border: 1px solid var(--border); border-radius: var(--r-card); background: #fff; }
/* missing path: friendly not-found with the uploading hint */
.notfound { margin-top: var(--hero-top); text-align: center; color: var(--text-dim); }
.notfound h1 { color: var(--text); font-size: 1.4em; margin-bottom: .5em; }
.notfound code { background: var(--hover); border: 1px solid var(--border); padding: .15em .5em; border-radius: 6px; }
.notfound .nf-sub { max-width: 440px; margin: 12px auto 20px; font-size: 13px; color: var(--text-faint); line-height: 1.6; }
/* viewing a past version: the banner that keeps the page from lying about
which bytes are on screen — it stays put for as long as ?v= is set */
.vbanner { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 22px; padding: 11px 14px; border: 1px solid var(--accent-dim); border-radius: var(--r-card); background: var(--glow); }
.vbanner .vb-icon { flex: none; display: flex; color: var(--accent-bright); }
.vbanner .vb-text { flex: 1 1 220px; min-width: 0; display: flex; flex-direction: column; gap: 1px; font-size: 12.5px; line-height: 1.45; }
.vbanner .vb-text b { color: var(--accent-bright); font-weight: 600; }
.vbanner .vb-text span { color: var(--text-dim); }
.vbanner .vb-actions { flex: none; display: flex; gap: 8px; }
.vbanner .vb-actions .ai-btn { display: inline-flex; align-items: center; text-decoration: none; }
/* plain file / binary views */
pre.plain { background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--r-card); padding: 14px 16px; overflow-x: auto; font: 12.5px/1.6 var(--mono); color: #c6cbd3; white-space: pre-wrap; overflow-wrap: anywhere; }
/* long unbreakable lines wrap instead of blowing out the column: .page has min-width: 0 */
.filecard { margin-top: var(--hero-top); text-align: center; color: var(--text-dim); }
.filecard .name { font-size: 1.2em; color: var(--text); margin-bottom: .3em; }
.filecard .btn { margin-top: 14px; }