mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
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>
1065 lines
70 KiB
CSS
1065 lines
70 KiB
CSS
/* 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; }
|