syntheogenesis / dee /static /catalog.css
github-actions[bot]
Deploy 2565f24
7b284c7
Raw
History Blame Contribute Delete
13.8 kB
/* catalog.css β€” the work catalog and the draggable rail.
===========================================================================
A separate stylesheet rather than more of app.css: these are two new,
self-contained surfaces, and keeping them out of an 8,600-line file makes
them reviewable. Every colour here is one of app.css's existing tokens, so
both themes and the light/dark switch work with nothing added.
=========================================================================== */
/* ═══════════════════════════════════════════════════════ draggable rail ══ */
.rail-split {
position: fixed;
top: var(--bench-strip-h, 56px);
bottom: 0;
/* Centred ON the boundary, so the grab area straddles the line rather
than sitting to one side of it and feeling offset. */
left: calc(var(--bench-rail, 392px) - 5px);
width: 11px;
z-index: 36;
cursor: col-resize;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
/* No transition on `left`: it tracks the pointer, and easing a drag makes
the handle lag behind the cursor. */
}
.rail-split[hidden] { display: none; }
/* Visible AT REST, and this is the whole point of the control.
The first version started at opacity 0 and only appeared on hover. Measured
in the browser: `getComputedStyle(.rail-split-grip).opacity === "0"` with
the split open and the pointer elsewhere β€” so the boundary between the
conversation and the bench looked exactly like the fixed 1px border it used
to be, and nothing on screen said it could move. A handle you have to
already know about is not a handle.
So it shows: a short grip on the seam, in --line-bold β€” one step up from
the .cockpit border-right it sits on, present enough to read as a grab
point, quiet enough not to become a third vertical rule in the layout. It
grows and takes ink on hover, focus and drag, which is where the affordance
should be loud. */
.rail-split-grip {
width: 3px;
height: 34px;
border-radius: 3px;
background: var(--line-bold);
opacity: 1;
transition: opacity var(--t-fast, .12s) var(--ease, ease),
height var(--t-fast, .12s) var(--ease, ease),
background var(--t-fast, .12s) var(--ease, ease);
}
.rail-split:hover .rail-split-grip,
.rail-split:focus-visible .rail-split-grip,
body.is-railsplitting .rail-split-grip {
height: 46px;
background: var(--ink-soft);
}
@media (prefers-reduced-motion: reduce) { .rail-split-grip { transition: none; } }
.rail-split:focus-visible {
outline: 2px solid var(--ink-strong);
outline-offset: -2px;
}
/* While dragging, kill text selection and stop the pointer being captured by
anything underneath β€” including the Mol* canvas, which otherwise starts
rotating the model the moment the cursor crosses it. */
body.is-railsplitting { user-select: none; cursor: col-resize; }
body.is-railsplitting .workspace,
body.is-railsplitting .cockpit { pointer-events: none; }
@media (max-width: 900px) { .rail-split { display: none; } }
/* ═════════════════════════════════════════════════════════════ catalog ══ */
.cat {
position: fixed; inset: 0; z-index: 90;
display: flex; align-items: center; justify-content: center;
padding: 32px 20px;
}
.cat[hidden] { display: none; }
body.cat-open { overflow: hidden; }
.cat-backdrop {
position: absolute; inset: 0;
background: rgba(6, 8, 12, 0.55);
backdrop-filter: blur(2px);
animation: catFade 160ms var(--ease, ease);
}
@keyframes catFade { from { opacity: 0; } to { opacity: 1; } }
.cat-panel {
position: relative;
width: min(1080px, 100%);
max-height: min(820px, 100%);
display: flex; flex-direction: column;
background: var(--bg-card);
border: 1px solid var(--line-strong);
border-radius: var(--r-4, 6px);
box-shadow: 0 24px 64px rgba(0, 0, 0, .34);
overflow: hidden;
animation: catRise 200ms var(--ease, ease);
}
@keyframes catRise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
.cat-backdrop, .cat-panel { animation: none; }
}
.cat-head {
display: flex; align-items: center; gap: 12px;
padding: 16px 18px 12px;
border-bottom: 1px solid var(--line);
flex: 0 0 auto;
}
.cat-title { margin: 0; font-size: 16px; font-weight: 600; letter-spacing: -0.01em; color: var(--ink-strong); }
.cat-x {
margin-left: auto; background: none; border: none; cursor: pointer;
font-size: 22px; line-height: 1; color: var(--ink-faint); padding: 2px 8px;
font-family: inherit;
}
.cat-x:hover { color: var(--ink-strong); }
.cat-controls {
display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
padding: 12px 18px; border-bottom: 1px solid var(--line); flex: 0 0 auto;
}
.cat-q {
flex: 1 1 260px; min-width: 0;
padding: 8px 12px; font-size: 13.5px; font-family: inherit;
color: var(--ink); background: var(--bg-app);
border: 1px solid var(--line-strong); border-radius: 999px;
}
.cat-q::placeholder { color: var(--ink-faint); }
.cat-q:focus { outline: none; border-color: var(--ink-soft); }
.cat-seg { display: flex; gap: 2px; flex: 0 0 auto; }
.cat-segb {
padding: 6px 13px; font-size: 12px; font-family: inherit; cursor: pointer;
background: none; border: 1px solid transparent; border-radius: 999px;
color: var(--ink-soft);
}
.cat-segb:hover { color: var(--ink-strong); }
.cat-segb.is-on {
color: var(--ink-strong); border-color: var(--line-strong); background: var(--bg-app);
}
.cat-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 4px 18px 20px; }
.cat-body:focus { outline: none; }
.cat-sec {
margin: 18px 0 10px; font-family: var(--font-mono); font-size: 10px;
letter-spacing: 0.13em; text-transform: uppercase; color: var(--ink-faint);
font-weight: 500; display: flex; align-items: baseline; gap: 7px;
}
.cat-sec span { color: var(--ink-disabled); }
.cat-grid {
display: grid; gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
/* Equal-height rows: chip counts differ per run, and ragged card bottoms
make a grid read as broken rather than varied. */
grid-auto-rows: 1fr;
}
.cat-empty { color: var(--ink-faint); font-size: 13.5px; padding: 40px 4px; text-align: center; }
/* ── card ───────────────────────────────────────────────────────────────── */
.cat-card {
display: grid;
grid-template-columns: 104px 1fr;
/* 1fr on the content row, auto on the actions row: with equal-height grid
rows this pins Rename/Delete to the bottom edge of every card, so the
buttons line up across the row however many chips a run carries. */
grid-template-rows: 1fr auto;
grid-template-areas: "thumb main" "acts acts";
gap: 12px;
padding: 12px;
border: 1px solid var(--line);
border-radius: var(--r-4, 6px);
background: var(--bg-app);
position: relative;
transition: border-color var(--t-fast, .12s) var(--ease, ease);
}
.cat-card:hover { border-color: var(--line-strong); }
.cat-card.is-sel { border-color: var(--ink-soft); }
.cat-card.is-current { border-color: var(--ink-soft); }
.cat-card.is-current::before {
content: "Open now"; position: absolute; top: -1px; right: 10px;
font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--bg-app); background: var(--ink-soft);
padding: 1px 6px; border-radius: 0 0 3px 3px;
}
.cat-card.is-busy { opacity: .55; pointer-events: none; }
/* ── the fold ───────────────────────────────────────────────────────────── */
.cat-thumb {
grid-area: thumb;
position: relative;
width: 104px; height: 84px;
border-radius: 4px;
background: var(--bg-subtle);
border: 1px solid var(--line);
display: flex; align-items: center; justify-content: center;
overflow: hidden; flex: none;
}
.cat-canvas { width: 100%; height: 100%; display: block; }
.cat-thumb-msg {
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
text-align: center; padding: 6px;
font-family: var(--font-mono); font-size: 8.5px; line-height: 1.35;
letter-spacing: 0.04em; color: var(--ink-disabled);
}
/* The accession is the receipt: it says WHICH model this is, so the picture
is checkable rather than decorative. */
.cat-acc {
position: absolute; left: 0; right: 0; bottom: 0;
font-family: var(--font-mono); font-size: 8px; letter-spacing: 0.06em;
color: var(--ink-faint); background: color-mix(in srgb, var(--bg-subtle) 84%, transparent);
padding: 1px 4px; text-align: center;
}
.cat-thumb[data-sc="idle"] .cat-canvas,
.cat-thumb[data-sc="loading"] .cat-canvas { opacity: 0; }
.cat-thumb[data-sc="ok"] .cat-thumb-msg { display: none; }
.cat-thumb[data-sc="loading"] .cat-thumb-msg::after { content: "Folding…"; }
.cat-thumb[data-sc="error"] .cat-thumb-msg::after { content: "Structure unavailable"; }
.cat-thumb[data-sc="ok"] .cat-acc { display: block; }
.cat-thumb[data-sc="none"] .cat-acc { display: none; }
/* A construct has no fold to show, so its tile carries a mark instead of a
picture. Dropping the border is the point: a 1px frame around a small glyph
on an empty ground is the shape of a broken image, and that is what it was
being read as. Without the frame it reads as an icon beside a title, which
is what it is. */
.cat-thumb--cx {
color: var(--ink-faint);
border-color: transparent;
background: var(--bg-subtle);
}
.cat-thumb--cx svg { width: 34px; height: 34px; }
.cat-card--cx:hover .cat-thumb--cx { color: var(--ink-soft); }
/* ── body ───────────────────────────────────────────────────────────────── */
.cat-main { grid-area: main; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.cat-open {
background: none; border: none; padding: 0; text-align: left; cursor: pointer;
font-family: inherit; color: var(--ink-strong); min-width: 0;
}
.cat-name {
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
overflow: hidden; font-size: 13.5px; font-weight: 600; line-height: 1.35;
letter-spacing: -0.005em;
}
.cat-open:hover .cat-name { text-decoration: underline; text-underline-offset: 2px; }
.cat-open:focus-visible { outline: 2px solid var(--ink-strong); outline-offset: 2px; border-radius: 2px; }
.cat-flag {
align-self: flex-start;
font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.09em;
text-transform: uppercase; color: var(--warning, var(--ink-strong));
border: 1px solid currentColor; border-radius: 3px; padding: 1px 5px;
}
.cat-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.cat-chip {
font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.04em;
padding: 2px 6px; border-radius: 3px;
background: var(--bg-subtle); color: var(--ink-soft); border: 1px solid var(--line);
}
.cat-chip.is-key { color: var(--ink-strong); border-color: var(--line-strong); }
.cat-chip.is-soft { color: var(--ink-faint); background: none; }
.cat-meta { font-family: var(--font-mono); font-size: 10px; color: var(--ink-faint); letter-spacing: 0.02em; }
.cat-note { margin: 2px 0 0; font-size: 11.5px; color: var(--danger); }
/* ── actions ────────────────────────────────────────────────────────────── */
.cat-acts {
grid-area: acts;
display: flex; gap: 6px; justify-content: flex-end;
padding-top: 8px; margin-top: 2px; border-top: 1px solid var(--line);
}
.cat-act {
font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.06em;
padding: 4px 10px; cursor: pointer;
background: none; color: var(--ink-soft);
border: 1px solid var(--line-strong); border-radius: 999px;
}
.cat-act:hover { color: var(--ink-strong); border-color: var(--ink-soft); }
.cat-act.is-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, var(--line)); }
.cat-act.is-danger:hover { border-color: var(--danger); }
.cat-act:focus-visible { outline: 2px solid var(--ink-strong); outline-offset: 1px; }
.cat-rename { display: flex; gap: 6px; align-items: center; }
.cat-rename-i {
flex: 1 1 auto; min-width: 0; padding: 5px 9px; font-size: 12.5px;
font-family: inherit; color: var(--ink); background: var(--bg-card);
border: 1px solid var(--line-strong); border-radius: 4px;
}
.cat-rename-i:focus { outline: none; border-color: var(--ink-soft); }
.cat-confirm {
grid-column: 1 / -1;
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
margin-top: 8px; padding: 9px 10px;
border: 1px solid color-mix(in srgb, var(--danger) 34%, var(--line));
border-radius: 4px;
font-size: 12px; color: var(--ink);
}
.cat-confirm span { flex: 1 1 180px; min-width: 0; }
@media (max-width: 620px) {
.cat { padding: 0; }
.cat-panel { max-height: 100%; height: 100%; border-radius: 0; border: none; }
.cat-grid { grid-template-columns: 1fr; }
}