Closes the Phase 5i implementation chain. When `views.is_default_for=<page>`
is set, opening that page with a "clean" URL (no chip params, no
?view=) auto-applies the saved filter + view_type. A "Showing default
view: <name> · clear" banner makes the swap visible and gives the user
a one-click out. Adding any chip param to the URL bypasses the default;
?nodefault=1 is the explicit opt-out for "I want the bare default tree".
New web/views.go: applyDefaultView gates on the param-cleanness check
+ Store.DefaultViewFor lookup. Resolution + view_type revalidation
mirror the slice D ?view=<uuid> path so a kanban-default opened on a
route that doesn't allow kanban falls back cleanly.
handleTree wires it into the existing slice D else-branch (no default
when ?view= is set). DefaultBanner field passes the applied view to
the template for the banner.
Test:
- TestDefaultViewAppliedOnCleanURL — seeds a tree default with
filter_json={tags:[work]} + view_type=card, then asserts: clean GET /
applies (card grid + banner with the view's name); ?tag=dev bypasses
(forest, no banner); ?nodefault=1 opt-out (forest, no banner).
1421 lines
57 KiB
CSS
1421 lines
57 KiB
CSS
/* projax — minimal style. Desktop-first; Phase 3i adds phone + tablet
|
|
responsiveness via two breakpoints at the file end.
|
|
Phase 4b: theme palette. Default is dark; the layout flips
|
|
`<html data-theme="light">` from the projax_theme cookie. Every panel
|
|
colour lives in a CSS variable so a switch is just a class flip — no
|
|
inline overrides anywhere in the codebase. */
|
|
|
|
/* Theme-independent: kept so we get sane behaviour even if both selectors
|
|
below somehow miss (defence in depth). */
|
|
:root { color-scheme: dark; }
|
|
|
|
:root, :root[data-theme="dark"] {
|
|
color-scheme: dark;
|
|
--fg: #e6e6e0;
|
|
--muted: #8a8880;
|
|
--bg: #0e0e0e;
|
|
--bg-alt: #1a1a1a;
|
|
--surface: #161616;
|
|
--surface-hover: #1f1f1f;
|
|
--border: #2c2c2c;
|
|
--accent: #6fa7e8;
|
|
--accent-fg: #0a0a0a;
|
|
--warn: #f0a060;
|
|
--ok: #74c79b;
|
|
--bad: #e07070;
|
|
--highlight: #3a3208;
|
|
--highlight-warn: #3a2a10;
|
|
--highlight-bad: #3a1212;
|
|
--kind-event-bg: #1a2540;
|
|
--kind-event-fg: #8eb5f0;
|
|
--kind-todo-bg: #3a2810;
|
|
--kind-todo-fg: #f0c068;
|
|
--kind-doc-bg: #143020;
|
|
--kind-doc-fg: #7adba0;
|
|
--kind-creation-bg: #281a40;
|
|
--kind-creation-fg: #b090ec;
|
|
/* Graph node keys — adjusted up in dark mode for contrast against the dark canvas. */
|
|
--graph-mai: #6fa7e8;
|
|
--graph-self: #74c79b;
|
|
--graph-external: #f0a060;
|
|
--graph-mixed: #b090ec;
|
|
--graph-unmanaged: #8a8880;
|
|
}
|
|
|
|
:root[data-theme="light"] {
|
|
color-scheme: light;
|
|
--fg: #1a1a1a;
|
|
--muted: #6a6a6a;
|
|
--bg: #fafafa;
|
|
--bg-alt: #f0efe8;
|
|
--surface: #ffffff;
|
|
--surface-hover: #f7f7f7;
|
|
--border: #d8d4c8;
|
|
--accent: #2f5d9e;
|
|
--accent-fg: #ffffff;
|
|
--warn: #b35900;
|
|
--ok: #2b7a4b;
|
|
--bad: #a02929;
|
|
--highlight: #fff5d6;
|
|
--highlight-warn: #fff5e6;
|
|
--highlight-bad: #fff5f5;
|
|
--kind-event-bg: #e8f0ff;
|
|
--kind-event-fg: #2055aa;
|
|
--kind-todo-bg: #fef6e0;
|
|
--kind-todo-fg: #a87000;
|
|
--kind-doc-bg: #e8f8ee;
|
|
--kind-doc-fg: #267a4d;
|
|
--kind-creation-bg: #f0e8ff;
|
|
--kind-creation-fg: #5a3aaa;
|
|
--graph-mai: #2563eb;
|
|
--graph-self: #15803d;
|
|
--graph-external: #ea580c;
|
|
--graph-mixed: #7c3aed;
|
|
--graph-unmanaged: #9ca3af;
|
|
}
|
|
* { box-sizing: border-box; }
|
|
html { font: 14px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif; color: var(--fg); background: var(--bg); }
|
|
body { margin: 0; }
|
|
/* Phase 5g — top-nav is gone; .projax-sidebar fills the same role on
|
|
desktop, .projax-bottom-nav (Slice B) on mobile. main has a sidebar-
|
|
width margin on desktop and zero on mobile. */
|
|
main.projax-main {
|
|
padding: 16px 24px;
|
|
max-width: 1100px;
|
|
margin: 0 auto 0 var(--projax-sidebar-width, 220px);
|
|
transition: margin-left 200ms ease;
|
|
}
|
|
html[data-sidebar-collapsed="true"] main.projax-main {
|
|
margin-left: var(--projax-sidebar-collapsed-width, 56px);
|
|
}
|
|
@media (max-width: 767px) {
|
|
main.projax-main { margin-left: 0; }
|
|
}
|
|
h1 { font-size: 1.4em; margin: 0 0 8px; }
|
|
h2 { font-size: 1.1em; margin: 24px 0 8px; }
|
|
.counts { color: var(--muted); margin: 0 0 16px; }
|
|
.tree ul { list-style: none; padding-left: 18px; margin: 4px 0; border-left: 1px dotted var(--border); }
|
|
.tree > ul.forest { padding-left: 0; border-left: none; }
|
|
.node { margin: 2px 0; }
|
|
.node.area > a { font-weight: 600; }
|
|
.slug { color: var(--muted); font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.85em; margin-left: 8px; }
|
|
.status { display: inline-block; font-size: 0.75em; padding: 1px 6px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); margin-left: 8px; }
|
|
.status-active { color: var(--ok); }
|
|
.status-done { color: var(--accent); }
|
|
.status-archived { color: var(--muted); }
|
|
.source { display: inline-block; font-size: 0.75em; padding: 1px 6px; border-radius: 4px; background: var(--bg-alt); border: 1px solid var(--border); }
|
|
.source-mai\.projects { color: var(--warn); }
|
|
.tag { display: inline-block; font-size: 0.72em; padding: 1px 6px; border-radius: 999px; background: var(--bg-alt); border: 1px solid var(--border); margin-left: 4px; color: var(--accent); text-decoration: none; }
|
|
a.tag:hover { background: var(--accent); color: var(--accent-fg); }
|
|
.tag-on { background: var(--accent); color: var(--accent-fg); }
|
|
.mgmt { display: inline-block; font-size: 0.72em; padding: 1px 6px; border-radius: 4px; background: var(--surface); border: 1px solid var(--border); margin-left: 4px; color: var(--muted); }
|
|
.mgmt-mai { color: var(--warn); border-color: var(--warn); }
|
|
.mgmt-self { color: var(--ok); border-color: var(--ok); }
|
|
.tagbar { margin: 12px 0; padding: 8px 0; border-bottom: 1px dotted var(--border); }
|
|
.tagbar .muted { color: var(--muted); margin-right: 8px; }
|
|
.tagbar .clear { margin-left: 12px; color: var(--bad); }
|
|
.muted { color: var(--muted); }
|
|
.add { margin-left: 6px; color: var(--accent); text-decoration: none; }
|
|
.add:hover { text-decoration: underline; }
|
|
.orphans { margin-top: 32px; }
|
|
.flat { list-style: none; padding: 0; }
|
|
.flat li { padding: 4px 0; border-bottom: 1px dashed var(--border); }
|
|
.edit, .promote, .inline-promote { display: grid; gap: 12px; max-width: 720px; }
|
|
.inline-promote { display: contents; }
|
|
form label { display: flex; flex-direction: column; gap: 4px; font-size: 0.9em; color: var(--muted); }
|
|
form label.checkbox { flex-direction: row; align-items: center; gap: 8px; }
|
|
form input[type="text"], form input:not([type]), form select, form textarea {
|
|
font: inherit; padding: 6px 8px; border: 1px solid var(--border); background: var(--surface); border-radius: 4px;
|
|
}
|
|
form textarea { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.92em; }
|
|
form .actions { display: flex; gap: 12px; align-items: center; }
|
|
button { font: inherit; padding: 6px 14px; border: 1px solid var(--accent); background: var(--accent); color: var(--accent-fg); border-radius: 4px; cursor: pointer; }
|
|
button:hover { filter: brightness(0.92); }
|
|
.cancel { color: var(--muted); text-decoration: none; }
|
|
.cancel:hover { text-decoration: underline; color: var(--bad); }
|
|
.readonly pre { background: var(--bg-alt); padding: 12px; border: 1px solid var(--border); border-radius: 4px; white-space: pre-wrap; }
|
|
table.classify { width: 100%; border-collapse: collapse; margin-top: 16px; }
|
|
table.classify th, table.classify td { padding: 8px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; }
|
|
table.classify input, table.classify select { width: 100%; }
|
|
.error { color: var(--bad); }
|
|
|
|
/* Tasks section — HTMX-driven VTODO writeback (phase 2.b). */
|
|
.tasks .cal-block { border: 1px solid var(--border); border-radius: 4px; padding: 8px 12px; margin: 8px 0 16px; background: var(--surface); }
|
|
.tasks .cal-block h3 { font-size: 0.95em; margin: 0 0 8px; color: var(--muted); }
|
|
.tasks ul.todo { list-style: none; padding: 0; margin: 0; }
|
|
.tasks li.todo-row { display: flex; gap: 6px; align-items: center; padding: 4px 0; border-bottom: 1px dotted var(--border); }
|
|
.tasks li.todo-row:last-child { border-bottom: none; }
|
|
.tasks li.todo-row form.inline { display: inline-flex; align-items: center; gap: 4px; margin: 0; }
|
|
.tasks li.todo-row .todo-edit { flex: 1; }
|
|
.tasks li.todo-row .todo-edit input[type="text"] { flex: 1; min-width: 12em; }
|
|
.tasks li.todo-row button { padding: 2px 8px; }
|
|
.tasks li.todo-row button.check, .tasks li.todo-row button.x {
|
|
background: var(--surface); color: var(--muted); border-color: var(--border);
|
|
font-size: 1.1em; line-height: 1; padding: 2px 6px;
|
|
}
|
|
.tasks li.todo-row button.check:hover { color: var(--ok); border-color: var(--ok); }
|
|
.tasks li.todo-row button.x:hover { color: var(--bad); border-color: var(--bad); }
|
|
.tasks .todo-create { display: flex; gap: 6px; margin: 6px 0 10px; }
|
|
.tasks .todo-create input[type="text"] { flex: 1; }
|
|
.tasks ul.done .summary { color: var(--muted); text-decoration: line-through; flex: 1; }
|
|
.banner.warn { background: var(--highlight-warn); border: 1px solid var(--warn); color: var(--warn); padding: 6px 10px; border-radius: 4px; margin: 8px 0; }
|
|
|
|
/* Issues section — Gitea-issue ingest (phase 2.d). */
|
|
.issues .repo-block { border: 1px solid var(--border); border-radius: 4px; padding: 8px 12px; margin: 8px 0 16px; background: var(--surface); }
|
|
.issues .repo-block h3 { font-size: 0.95em; margin: 0 0 8px; display: flex; gap: 12px; align-items: baseline; }
|
|
.issues .repo-block h3 a { color: var(--accent); text-decoration: none; }
|
|
.issues .repo-block h3 a:hover { text-decoration: underline; }
|
|
.issues ul.issues { list-style: none; padding: 0; margin: 0; }
|
|
.issues li.issue-row { display: flex; gap: 6px; align-items: baseline; padding: 4px 0; border-bottom: 1px dotted var(--border); flex-wrap: wrap; }
|
|
.issues li.issue-row:last-child { border-bottom: none; }
|
|
.issues li.issue-row .num { color: var(--muted); font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.85em; text-decoration: none; }
|
|
.issues li.issue-row .title { color: var(--fg); text-decoration: none; flex: 1; min-width: 12em; }
|
|
.issues li.issue-row .title:hover { text-decoration: underline; color: var(--accent); }
|
|
.issues li.issue-row .label { display: inline-block; font-size: 0.72em; padding: 1px 6px; border-radius: 999px; background: var(--bg-alt); border: 1px solid var(--border); color: var(--accent); }
|
|
.issues li.issue-row .milestone { font-size: 0.72em; padding: 1px 6px; border-radius: 4px; background: var(--surface); border: 1px solid var(--border); color: var(--warn); }
|
|
.issues li.issue-row .assignee { font-size: 0.78em; color: var(--muted); }
|
|
.issues ul.closed .title { color: var(--muted); }
|
|
|
|
/* Tree-page filter bar (phase 3b). */
|
|
.tree-section { display: block; }
|
|
#tree-filterbar { padding: 12px 0; border-bottom: 1px dotted var(--border); margin-bottom: 12px; }
|
|
#tree-filterbar .search { margin: 0 0 8px; display: flex; }
|
|
#tree-filterbar .search input[type="search"] {
|
|
width: 100%; font: inherit; padding: 6px 10px;
|
|
border: 1px solid var(--border); background: var(--surface); border-radius: 4px;
|
|
}
|
|
#tree-filterbar .chip-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 4px 0; }
|
|
#tree-filterbar .chip-row .muted { width: 4em; flex-shrink: 0; }
|
|
.mgmt-chip, .status-chip, .has-chip {
|
|
display: inline-block; font-size: 0.78em; padding: 1px 8px; border-radius: 999px;
|
|
background: var(--surface); border: 1px solid var(--border); color: var(--muted); text-decoration: none;
|
|
}
|
|
.mgmt-chip:hover, .status-chip:hover, .has-chip:hover { color: var(--fg); border-color: var(--accent); }
|
|
.chip-on { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
|
|
.chip-on:hover { color: var(--accent-fg); filter: brightness(0.92); }
|
|
/* Phase 5i Slice A — project scope chip. The picker uses a bare <select>
|
|
inside a tagbar form; the active state mirrors the mgmt/status/has chips. */
|
|
.proj-chip, .proj-desc-chip {
|
|
display: inline-flex; align-items: center; gap: 4px;
|
|
font-size: 0.78em; padding: 1px 8px; border-radius: 999px;
|
|
background: var(--surface); border: 1px solid var(--border); color: var(--muted); text-decoration: none;
|
|
}
|
|
.proj-chip.chip-on { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
|
|
.proj-chip .proj-name { font-weight: 500; }
|
|
.proj-chip .proj-clear { color: inherit; opacity: 0.75; margin-left: 4px; padding: 0 4px; }
|
|
.proj-chip .proj-clear:hover { opacity: 1; }
|
|
.proj-desc-chip:hover { color: var(--fg); border-color: var(--accent); }
|
|
.proj-picker select { font-size: 0.85em; padding: 1px 4px; }
|
|
/* Phase 5i Slice B — view-type chip strip + card grid. */
|
|
.view-type-chip {
|
|
display: inline-block; font-size: 0.78em; padding: 1px 8px; border-radius: 999px;
|
|
background: var(--surface); border: 1px solid var(--border); color: var(--muted); text-decoration: none;
|
|
text-transform: capitalize;
|
|
}
|
|
.view-type-chip:hover { color: var(--fg); border-color: var(--accent); }
|
|
.view-type-chip.chip-locked { opacity: 0.4; }
|
|
.view-type-chip.chip-locked:hover { color: var(--muted); border-color: var(--border); cursor: not-allowed; }
|
|
.tree-card-grid {
|
|
display: grid; gap: 12px; padding: 12px 0;
|
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
}
|
|
.tree-card {
|
|
border: 1px solid var(--border); border-radius: 6px; background: var(--surface);
|
|
padding: 10px 12px;
|
|
}
|
|
.tree-card-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 6px; }
|
|
.tree-card-title { font-weight: 500; color: var(--fg); text-decoration: none; }
|
|
.tree-card-title:hover { color: var(--accent); }
|
|
.tree-card-slug { font-size: 0.78em; }
|
|
.tree-card-meta { display: flex; flex-wrap: wrap; gap: 4px; margin: 0; font-size: 0.78em; }
|
|
.tree-card-empty { grid-column: 1 / -1; padding: 24px; color: var(--muted); }
|
|
/* Phase 5i Slice C — kanban columns + cards. */
|
|
.kanban-controls { margin: 8px 0; }
|
|
.groupby-chip {
|
|
display: inline-block; font-size: 0.78em; padding: 1px 8px; border-radius: 999px;
|
|
background: var(--surface); border: 1px solid var(--border); color: var(--muted); text-decoration: none;
|
|
}
|
|
.groupby-chip:hover { color: var(--fg); border-color: var(--accent); }
|
|
.kanban-board {
|
|
display: grid; gap: 12px; padding: 12px 0; overflow-x: auto;
|
|
grid-auto-flow: column; grid-auto-columns: minmax(220px, 280px);
|
|
}
|
|
.kanban-column {
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: 6px;
|
|
padding: 8px 10px; display: flex; flex-direction: column; gap: 8px;
|
|
min-height: 120px;
|
|
}
|
|
.kanban-col-head {
|
|
display: flex; justify-content: space-between; align-items: baseline;
|
|
border-bottom: 1px solid var(--border); padding-bottom: 6px;
|
|
}
|
|
.kanban-col-label { font-weight: 500; }
|
|
.kanban-cards { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
|
|
.kanban-card {
|
|
background: var(--bg); border: 1px solid var(--border); border-radius: 4px;
|
|
padding: 6px 8px;
|
|
}
|
|
.kanban-card-title { font-weight: 500; color: var(--fg); text-decoration: none; display: block; }
|
|
.kanban-card-title:hover { color: var(--accent); }
|
|
.kanban-card-meta { display: flex; flex-wrap: wrap; gap: 4px; margin: 4px 0 0; font-size: 0.78em; }
|
|
.kanban-empty { padding: 24px; }
|
|
/* Phase 5i Slice E — default-view banner. Sits above the counts line on
|
|
any Views-supporting page when a default view is auto-applied. */
|
|
.default-banner {
|
|
background: var(--surface); border: 1px solid var(--border);
|
|
border-left: 3px solid var(--accent);
|
|
padding: 6px 10px; border-radius: 4px;
|
|
font-size: 0.85em; margin: 4px 0 8px;
|
|
}
|
|
.default-banner a { color: var(--bad); }
|
|
#tree-filterbar small { opacity: 0.75; margin-left: 2px; }
|
|
.tree-section .empty { padding: 24px; color: var(--muted); }
|
|
.tree-section .clear { color: var(--bad); }
|
|
|
|
/* Documents / PER-dated artifacts (phase 3c). */
|
|
.documents { margin-top: 24px; }
|
|
.documents .doc-add { display: flex; gap: 6px; margin: 8px 0 12px; align-items: center; flex-wrap: wrap; }
|
|
.documents .doc-add input[type="text"] { flex: 1; min-width: 8em; }
|
|
.documents ul.docs { list-style: none; padding: 0; margin: 0; }
|
|
.documents li.doc-row {
|
|
display: flex; gap: 8px; align-items: baseline; padding: 6px 0;
|
|
border-bottom: 1px dotted var(--border); flex-wrap: wrap;
|
|
}
|
|
.documents li.doc-row:last-child { border-bottom: none; }
|
|
.documents li.doc-row.highlight { background: var(--highlight); padding-left: 8px; border-left: 3px solid var(--warn); }
|
|
.documents .per {
|
|
font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.88em;
|
|
color: var(--accent); background: var(--bg-alt); padding: 1px 6px; border-radius: 4px;
|
|
}
|
|
.documents .ref-type {
|
|
display: inline-block; font-size: 0.72em; padding: 1px 6px; border-radius: 999px;
|
|
background: var(--surface); border: 1px solid var(--border); color: var(--muted);
|
|
}
|
|
.documents .ref-type-document { color: var(--accent); border-color: var(--accent); }
|
|
.documents .ref-type-note { color: var(--ok); border-color: var(--ok); }
|
|
.documents .ref-type-url { color: var(--warn); border-color: var(--warn); }
|
|
.documents .ref-id { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.85em; flex: 1; min-width: 8em; }
|
|
.documents .doc-note { color: var(--muted); font-style: italic; }
|
|
.documents .doc-remove .x {
|
|
background: var(--surface); color: var(--muted); border-color: var(--border);
|
|
font-size: 1.05em; line-height: 1; padding: 2px 6px;
|
|
}
|
|
.documents .doc-remove .x:hover { color: var(--bad); border-color: var(--bad); }
|
|
|
|
/* --- /admin/bulk page --- */
|
|
.bulk-section .counts { margin: 8px 0; color: var(--muted); }
|
|
#bulk-filter { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; }
|
|
#bulk-filter input[type=search] { width: 28em; }
|
|
#bulk-filter select[multiple] { min-width: 9em; }
|
|
#bulk-filter label.checkbox { display: inline-flex; align-items: center; gap: 4px; }
|
|
#bulk-actions fieldset.actions {
|
|
margin: 12px 0; padding: 8px 12px; border: 1px solid var(--border); border-radius: 4px;
|
|
}
|
|
#bulk-actions .action-row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
|
|
#bulk-actions .action-row label { display: inline-flex; flex-direction: column; font-size: 0.85em; color: var(--muted); }
|
|
#bulk-actions .action-row input, #bulk-actions .action-row select { padding: 4px 6px; }
|
|
#bulk-actions button[type=submit] { padding: 6px 14px; align-self: flex-end; }
|
|
table.bulk { width: 100%; border-collapse: collapse; margin-top: 12px; font-size: 0.92em; }
|
|
table.bulk th, table.bulk td { padding: 6px 8px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: middle; }
|
|
table.bulk th { background: var(--bg-alt); color: var(--muted); font-weight: 500; }
|
|
table.bulk td.cell-tags .tag { margin-right: 4px; }
|
|
table.bulk .chip-x {
|
|
background: transparent; border: none; color: var(--muted); padding: 0 4px; cursor: pointer; font-size: 1em;
|
|
}
|
|
table.bulk .chip-x:hover { color: var(--bad); }
|
|
table.bulk .chip-add { display: inline-flex; align-items: center; gap: 2px; margin-left: 4px; }
|
|
table.bulk .chip-add input { padding: 1px 4px; font-size: 0.85em; width: 7em; }
|
|
table.bulk .chip-add-btn {
|
|
background: transparent; border: 1px solid var(--border); color: var(--accent);
|
|
padding: 0 6px; font-size: 0.9em; cursor: pointer; min-height: 0; border-radius: 3px;
|
|
}
|
|
table.bulk .chip-add-btn:hover { background: var(--accent); color: var(--accent-fg); }
|
|
|
|
/* --- /dashboard --- */
|
|
.dashboard .dash-grid { display: grid; grid-template-columns: 1fr; gap: 16px; margin-top: 12px; }
|
|
.dashboard .card {
|
|
border: 1px solid var(--border); border-radius: 6px; padding: 12px 16px;
|
|
background: var(--surface);
|
|
}
|
|
.dashboard .card header h2 { margin: 0 0 4px 0; font-size: 1.1em; }
|
|
.dashboard .card header .task-groups { display: flex; gap: 12px; flex-wrap: wrap; font-size: 0.85em; margin: 4px 0 8px; }
|
|
.dashboard .card header .task-groups .overdue { color: var(--bad); font-weight: 600; }
|
|
.dashboard .empty { padding: 8px 0; font-style: italic; }
|
|
.dashboard .task-list, .dashboard .issue-list, .dashboard .doc-list { list-style: none; padding: 0; margin: 0; }
|
|
.dashboard .task-row, .dashboard .issue-row, .dashboard .doc-row {
|
|
display: flex; gap: 8px; align-items: baseline;
|
|
padding: 6px 0; border-bottom: 1px dotted var(--border); flex-wrap: wrap;
|
|
}
|
|
.dashboard .task-row:last-child, .dashboard .issue-row:last-child, .dashboard .doc-row:last-child { border-bottom: none; }
|
|
.dashboard .task-row .check button {
|
|
background: transparent; border: 1px solid var(--border); border-radius: 3px;
|
|
padding: 0 6px; cursor: pointer; color: var(--muted);
|
|
}
|
|
.dashboard .task-row .check button:hover { color: var(--ok); border-color: var(--ok); }
|
|
.dashboard .task-row .proj, .dashboard .issue-row .proj, .dashboard .doc-row .proj {
|
|
font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.85em; color: var(--muted);
|
|
min-width: 10em;
|
|
}
|
|
.dashboard .task-row .summary { flex: 1; }
|
|
.dashboard .task-row .due { font-size: 0.85em; color: var(--muted); }
|
|
.dashboard .task-row .due.bad { color: var(--bad); font-weight: 600; }
|
|
.dashboard .task-row.bucket-overdue { background: var(--highlight-bad); }
|
|
.dashboard .issue-row .iss { flex: 1; }
|
|
.dashboard .issue-row .label {
|
|
font-size: 0.72em; padding: 1px 6px; border-radius: 999px;
|
|
background: var(--bg-alt); color: var(--muted); border: 1px solid var(--border);
|
|
}
|
|
.dashboard .issue-row .upd { font-size: 0.8em; }
|
|
|
|
/* --- /graph --- */
|
|
.graph-canvas { overflow: auto; border: 1px solid var(--border); margin-top: 12px; background: var(--bg); }
|
|
.graph-svg { display: block; }
|
|
#graph-filterbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
|
|
#graph-filterbar input[type=search] { width: 22em; }
|
|
#graph-filterbar select[multiple] { min-width: 9em; }
|
|
#graph-filterbar .download { color: var(--accent); margin-left: auto; }
|
|
.graph-legend { margin: 8px 0; font-size: 0.85em; }
|
|
.graph-legend .legend-key {
|
|
display: inline-block; padding: 2px 8px; border-radius: 3px;
|
|
border: 2px solid; margin-right: 4px; font-family: ui-monospace, monospace; font-size: 0.85em;
|
|
}
|
|
.graph-legend .key-mai { border-color: var(--graph-mai); color: var(--graph-mai); }
|
|
.graph-legend .key-self { border-color: var(--graph-self); color: var(--graph-self); }
|
|
.graph-legend .key-external { border-color: var(--graph-external); color: var(--graph-external); }
|
|
.graph-legend .key-mixed { border-color: var(--graph-mixed); color: var(--graph-mixed); border-style: dashed; }
|
|
.graph-legend .key-unmanaged { border-color: var(--graph-unmanaged); color: var(--graph-unmanaged); }
|
|
|
|
/* ------------------------------------------------------------------
|
|
Phase 5h — Dashboard view switcher (Tiles / Tasks / Events tabs)
|
|
The tab strip sits below the filter bar; the tile grid is the
|
|
default surface. Tiles use CSS grid auto-fill so the column count
|
|
follows the viewport width.
|
|
------------------------------------------------------------------ */
|
|
.dashboard .dash-tabs {
|
|
display: flex; gap: 4px; margin: 8px 0 12px;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.dashboard .dash-tab {
|
|
padding: 6px 14px; font-size: 0.95em;
|
|
border: 1px solid transparent; border-bottom: none; border-radius: 4px 4px 0 0;
|
|
color: var(--muted); text-decoration: none;
|
|
margin-bottom: -1px;
|
|
}
|
|
.dashboard .dash-tab:hover { color: var(--fg); }
|
|
.dashboard .dash-tab.active {
|
|
color: var(--fg); background: var(--surface);
|
|
border-color: var(--border);
|
|
}
|
|
.dashboard .dash-scope-chip {
|
|
margin-left: auto; padding: 4px 12px;
|
|
font-size: 0.85em; color: var(--muted);
|
|
border: 1px solid var(--border); border-radius: 999px;
|
|
text-decoration: none; align-self: center;
|
|
margin-bottom: 4px;
|
|
}
|
|
.dashboard .dash-scope-chip:hover { color: var(--fg); border-color: var(--accent); }
|
|
|
|
.dashboard .dash-tiles {
|
|
display: grid; gap: 12px; margin-top: 8px;
|
|
/* minmax(0, 1fr) overrides the implicit min-content minimum on 1fr —
|
|
without it, any tile with a long unbreakable string (slug-path or
|
|
a long task summary) widens its column and pushes the grid past
|
|
the viewport, causing horizontal scroll. */
|
|
grid-template-columns: minmax(0, 1fr);
|
|
min-width: 0;
|
|
}
|
|
.dashboard .tile {
|
|
border: 1px solid var(--border); border-radius: 6px; padding: 12px 14px;
|
|
background: var(--surface);
|
|
display: flex; flex-direction: column; gap: 6px;
|
|
min-height: 110px;
|
|
/* min-width: 0 lets the grid item shrink below its content's
|
|
min-content width — critical pairing with minmax(0, 1fr) above. */
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
}
|
|
.dashboard .tile.tile-pinned { border-left: 3px solid var(--accent); }
|
|
.dashboard .tile.tile-stale { border-style: dashed; opacity: 0.85; }
|
|
.dashboard .tile .tile-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
|
|
.dashboard .tile .tile-title { font-weight: 600; color: var(--fg); text-decoration: none; }
|
|
.dashboard .tile .tile-title:hover { text-decoration: underline; }
|
|
.dashboard .tile .tile-pin-form { display: inline-flex; margin: 0 4px 0 0; }
|
|
.dashboard .tile .tile-pin {
|
|
background: transparent; border: none; cursor: pointer;
|
|
color: var(--muted); padding: 0; font-size: 1.1em; line-height: 1;
|
|
}
|
|
.dashboard .tile .tile-pin.pinned { color: var(--accent); }
|
|
.dashboard .tile .tile-pin:hover { color: var(--accent); }
|
|
.dashboard .tile .tile-path {
|
|
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
font-size: 0.78em;
|
|
/* Long dot-separated slugs (dev.youpc.kommentar.knowledge-tools)
|
|
are unbreakable by default — let them wrap at any character so
|
|
they don't widen the tile. */
|
|
overflow-wrap: anywhere;
|
|
min-width: 0;
|
|
}
|
|
.dashboard .tile .tile-live {
|
|
font-size: 0.72em; padding: 1px 6px; border-radius: 999px;
|
|
background: var(--bg-alt); color: var(--accent); border: 1px solid var(--accent);
|
|
margin-left: auto;
|
|
}
|
|
.dashboard .tile .tile-counts {
|
|
display: flex; gap: 10px; flex-wrap: wrap; margin: 0;
|
|
font-size: 0.85em;
|
|
}
|
|
.dashboard .tile .tile-counts .tile-overdue { color: var(--bad); }
|
|
.dashboard .tile .tile-counts strong { font-size: 1.05em; }
|
|
.dashboard .tile .tile-signal {
|
|
margin: 0; font-size: 0.85em;
|
|
display: flex; gap: 6px; align-items: baseline;
|
|
overflow: hidden;
|
|
}
|
|
.dashboard .tile .tile-signal-text {
|
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
}
|
|
.dashboard .tile .tile-foot {
|
|
margin-top: auto; font-size: 0.78em;
|
|
display: flex; justify-content: flex-end;
|
|
}
|
|
.dashboard .tile .tile-stale-flag {
|
|
margin-left: auto; font-size: 0.7em;
|
|
padding: 1px 6px; border-radius: 999px;
|
|
border: 1px dashed var(--warn); color: var(--warn);
|
|
}
|
|
|
|
.dashboard .dash-tiles-empty { margin: 24px 0; }
|
|
|
|
.dashboard .dash-quiet { margin-top: 18px; }
|
|
.dashboard .dash-quiet > summary.dash-quiet-summary {
|
|
cursor: pointer; padding: 6px 0;
|
|
border-top: 1px dotted var(--border);
|
|
font-size: 0.9em; list-style: revert;
|
|
}
|
|
.dashboard .dash-quiet[open] > summary.dash-quiet-summary { margin-bottom: 8px; }
|
|
.dashboard .dash-tiles-quiet .tile { opacity: 0.85; }
|
|
|
|
.dashboard .dash-events-view { margin-top: 8px; }
|
|
.dashboard .dash-events-summary { margin: 4px 0 16px; }
|
|
.dashboard .dash-events-summary h2 { margin: 0; font-size: 1.05em; font-weight: 600; }
|
|
.dashboard .dash-events-view .event-day-large { margin: 18px 0; }
|
|
.dashboard .dash-events-view .event-day-heading {
|
|
font-size: 0.95em; font-weight: 600; margin: 0 0 8px 0;
|
|
border-bottom: 1px dotted var(--border); padding-bottom: 4px;
|
|
display: flex; gap: 10px; align-items: baseline;
|
|
}
|
|
.dashboard .dash-events-view .event-day-label { color: var(--fg); }
|
|
.dashboard .dash-events-view .event-day-date {
|
|
font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.85em;
|
|
}
|
|
.dashboard .dash-events-view .event-day-count { margin-left: auto; font-size: 0.85em; }
|
|
.dashboard .dash-events-empty { margin: 24px 0; max-width: 540px; }
|
|
.dashboard .dash-events-view .event-list { list-style: none; padding: 0; margin: 0; }
|
|
.dashboard .dash-events-view .event-row {
|
|
display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
|
|
padding: 6px 0; border-bottom: 1px dotted var(--border);
|
|
}
|
|
.dashboard .dash-events-view .event-row:last-child { border-bottom: none; }
|
|
.dashboard .dash-events-view .start { font-family: ui-monospace, SFMono-Regular, monospace; min-width: 4em; }
|
|
.dashboard .dash-events-view .proj {
|
|
font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.85em; color: var(--muted);
|
|
}
|
|
.dashboard .dash-events-view .summary { flex: 1; }
|
|
|
|
/* --- /dashboard polish (3g) --- */
|
|
.dashboard .counts .refresh { margin-left: 12px; color: var(--accent); cursor: pointer; }
|
|
.dashboard .counts .refresh:hover { text-decoration: underline; }
|
|
.dashboard .card-collapsed {
|
|
margin: 6px 0; padding: 4px 12px;
|
|
border-left: 3px solid var(--border); font-style: italic;
|
|
}
|
|
.dashboard .card-stale header h2 { color: var(--warn); }
|
|
.dashboard .stale-list { list-style: none; padding: 0; margin: 0; }
|
|
.dashboard .stale-row {
|
|
display: flex; gap: 8px; align-items: baseline;
|
|
padding: 6px 0; border-bottom: 1px dotted var(--border);
|
|
}
|
|
.dashboard .stale-row:last-child { border-bottom: none; }
|
|
.dashboard .stale-row .repo { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.85em; }
|
|
.dashboard .stale-row .last-active { color: var(--warn); font-size: 0.9em; }
|
|
|
|
/* --- Issues writeback (3h) --- */
|
|
.issues .new-issue { margin: 8px 0; }
|
|
.issues .new-issue-form, .issues .comment-form {
|
|
display: flex; flex-direction: column; gap: 4px; padding: 6px 0;
|
|
}
|
|
.issues .new-issue-form input[name=title] { width: 100%; }
|
|
.issues .new-issue-form textarea, .issues .comment-form textarea {
|
|
width: 100%; resize: vertical; font-family: inherit; padding: 4px;
|
|
}
|
|
.issues .issue-row form { display: inline-flex; align-items: center; margin-left: 4px; }
|
|
.issues .issue-row form button {
|
|
background: transparent; border: 1px solid var(--border); border-radius: 3px;
|
|
padding: 1px 6px; cursor: pointer; color: var(--muted); font-size: 0.85em;
|
|
}
|
|
.issues .issue-close button:hover { color: var(--ok); border-color: var(--ok); }
|
|
.issues .issue-reopen button:hover { color: var(--warn); border-color: var(--warn); }
|
|
.issues .issue-comment summary { font-size: 0.85em; cursor: pointer; }
|
|
|
|
/* ------------------------------------------------------------------
|
|
Phase 3i — Mobile responsiveness
|
|
Two breakpoints: tablet (≤ 768px), phone (≤ 480px).
|
|
Goals:
|
|
- Touch targets ≥ 44px on phone for buttons + chip toggles.
|
|
- Filter-chip strips become horizontal-scroll with edge fade.
|
|
- Tables (classify, bulk) become card lists.
|
|
- Dashboard cards stack vertically.
|
|
- SVG graph stays scrollable inside its container.
|
|
- Header nav wraps cleanly without burger (small site, ≤ 5 links).
|
|
------------------------------------------------------------------ */
|
|
|
|
@media (max-width: 768px) {
|
|
main.projax-main { padding: 12px 14px; }
|
|
|
|
/* Filter chip strips become horizontal-scrollable. */
|
|
.tagbar { overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
|
#tree-filterbar .chip-row,
|
|
#bulk-filter,
|
|
#dashboard-filter,
|
|
#graph-filterbar {
|
|
flex-wrap: nowrap; white-space: nowrap;
|
|
}
|
|
/* Form rows inside filter bars can still wrap their internal labels. */
|
|
#tree-filterbar .chip-row .muted { position: sticky; left: 0; background: var(--bg); padding-right: 6px; z-index: 1; }
|
|
|
|
/* Touch targets — chip buttons, status pills, action buttons. */
|
|
.tag, .mgmt-chip, .status-chip, .has-chip, .tag-on, .chip-on { padding: 6px 10px; font-size: 0.85em; }
|
|
button { padding: 10px 16px; min-height: 44px; }
|
|
.add { font-size: 1.4em; padding: 4px 10px; }
|
|
|
|
/* Trees: tighter indent so deep paths still fit. */
|
|
.tree ul { padding-left: 10px; }
|
|
.node { padding: 4px 0; }
|
|
.slug { display: block; margin-left: 0; }
|
|
|
|
/* Tables → card lists (classify / bulk / caldav admin). */
|
|
table.classify, table.bulk { display: block; border: none; }
|
|
table.classify thead, table.bulk thead { display: none; }
|
|
table.classify tbody, table.bulk tbody { display: block; }
|
|
table.classify tr, table.bulk tr {
|
|
display: block; margin-bottom: 12px; padding: 10px 12px;
|
|
border: 1px solid var(--border); border-radius: 6px; background: var(--surface);
|
|
}
|
|
table.classify td, table.bulk td {
|
|
display: block; padding: 4px 0; border: none;
|
|
}
|
|
table.classify td::before, table.bulk td::before {
|
|
content: attr(data-label); display: block;
|
|
color: var(--muted); font-size: 0.8em; margin-bottom: 2px;
|
|
}
|
|
|
|
/* Bulk action row wraps. */
|
|
#bulk-actions .action-row { flex-direction: column; align-items: stretch; gap: 8px; }
|
|
#bulk-actions .action-row label { width: 100%; }
|
|
#bulk-actions button[type=submit] { width: 100%; }
|
|
|
|
/* Dashboard: cards already stack in 1-col grid; widen them a bit. */
|
|
.dashboard .card { padding: 10px 12px; }
|
|
.dashboard .task-row, .dashboard .issue-row, .dashboard .doc-row, .dashboard .stale-row {
|
|
flex-wrap: wrap;
|
|
}
|
|
.dashboard .task-row .proj, .dashboard .issue-row .proj, .dashboard .doc-row .proj, .dashboard .stale-row .proj {
|
|
min-width: 0; flex-basis: 100%; font-size: 0.78em;
|
|
}
|
|
.dashboard .task-row .check button { padding: 8px 14px; }
|
|
|
|
/* Phase 5h — Tiles dashboard mobile polish.
|
|
Tab strip wraps gracefully and the scope chip drops to its own row
|
|
(the auto margin still right-aligns it within the wrapped flow).
|
|
Tile padding tightens; tile-pin and tile-live buttons get touch
|
|
targets that match the rest of the mobile button budget. */
|
|
.dashboard .dash-tabs { flex-wrap: wrap; gap: 2px; }
|
|
.dashboard .dash-tab { padding: 8px 12px; font-size: 0.9em; }
|
|
.dashboard .dash-scope-chip {
|
|
flex-basis: 100%; text-align: center; margin-left: 0; margin-top: 4px;
|
|
padding: 8px 12px;
|
|
}
|
|
.dashboard .tile { padding: 10px 12px; }
|
|
.dashboard .tile .tile-head { gap: 6px; }
|
|
.dashboard .tile .tile-pin {
|
|
font-size: 1.25em; padding: 4px 6px; min-height: 36px; min-width: 36px;
|
|
}
|
|
.dashboard .tile .tile-live { min-height: 32px; padding: 4px 10px; }
|
|
/* Quiet fold summary needs a tappable hit area. */
|
|
.dashboard .dash-quiet > summary.dash-quiet-summary { padding: 12px 0; }
|
|
/* Events tab on mobile: day headings drop the right-aligned count
|
|
onto its own line for legibility. */
|
|
.dashboard .dash-events-view .event-day-heading { flex-wrap: wrap; gap: 6px; }
|
|
.dashboard .dash-events-view .event-day-count { margin-left: 0; flex-basis: 100%; }
|
|
|
|
/* Detail page edit form — single column. */
|
|
.edit, .promote, .inline-promote { grid-template-columns: 1fr; }
|
|
|
|
/* Issues writeback — collapsed forms expand to full width. */
|
|
.issues .repo-block { padding: 0 4px; }
|
|
.issues .issue-row { flex-wrap: wrap; }
|
|
.issues .issue-row form { width: 100%; margin-left: 0; margin-top: 4px; }
|
|
|
|
/* Graph: SVG container scrolls; the SVG itself keeps its natural size
|
|
so pinch-zoom (viewport meta) just works. */
|
|
.graph-canvas { max-width: 100vw; max-height: 75vh; overflow: auto; }
|
|
|
|
/* Documents section — chips wrap. */
|
|
.documents li.doc-row { flex-wrap: wrap; }
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
html { font-size: 15px; } /* nudge up so default body text is legible without zooming */
|
|
main.projax-main { padding: 8px 10px; }
|
|
|
|
/* Even tighter chip-row scroll behaviour on phone. */
|
|
.tag, .mgmt-chip, .status-chip, .has-chip { padding: 8px 12px; }
|
|
|
|
/* Inline forms in tree / classify use stacked layout. */
|
|
.classify .inline-classify { display: block; }
|
|
.classify .inline-classify select, .classify .inline-classify button { width: 100%; margin-top: 4px; }
|
|
|
|
/* Dashboard header counts wrap onto two lines. */
|
|
.dashboard .card header .task-groups { gap: 6px; font-size: 0.78em; }
|
|
|
|
/* Bulk page — drop the primary-path column entirely on phone to save space. */
|
|
table.bulk td.col-path { display: none; }
|
|
}
|
|
|
|
/* Wide-laptop tier: anchor max-width so the page doesn't sprawl on a 4K monitor. */
|
|
@media (min-width: 1280px) {
|
|
main { max-width: 1200px; }
|
|
.dashboard .dash-grid { grid-template-columns: 1fr 1fr; }
|
|
.dashboard .card-stale { grid-column: span 2; }
|
|
}
|
|
|
|
/* Phase 5h — Tiles grid breakpoints (1/2/3 cols at 600/900/—).
|
|
minmax(0, 1fr) on every breakpoint so long unbreakable strings
|
|
(slug-paths, task summaries) don't push columns past the viewport. */
|
|
@media (min-width: 600px) {
|
|
.dashboard .dash-tiles { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
|
|
}
|
|
@media (min-width: 900px) {
|
|
.dashboard .dash-tiles { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); }
|
|
}
|
|
|
|
/* Graph fit-to-screen toggle (Phase 3i): when ".fit" is on the canvas,
|
|
the SVG width:100% squashes it to the viewport instead of natural size.
|
|
Maintains aspect ratio via preserveAspectRatio default. */
|
|
.graph-canvas .graph-controls {
|
|
display: flex; gap: 8px; align-items: center; padding: 4px 8px; margin: 0;
|
|
background: var(--bg-alt); border-bottom: 1px solid var(--border);
|
|
}
|
|
.graph-canvas .fit-screen {
|
|
font-size: 0.85em; padding: 4px 10px; min-height: 0;
|
|
background: var(--surface); color: var(--accent); border: 1px solid var(--border);
|
|
}
|
|
.graph-canvas .fit-screen:hover { background: var(--accent); color: var(--accent-fg); }
|
|
.graph-canvas.fit .graph-svg { width: 100%; height: auto; }
|
|
|
|
/* --- Dashboard Events card (Phase 3l) --- */
|
|
.dashboard .card-events .event-day { margin-bottom: 12px; }
|
|
.dashboard .card-events .event-day:last-child { margin-bottom: 0; }
|
|
.dashboard .card-events .event-day h3 {
|
|
font-size: 0.85em; margin: 8px 0 4px; color: var(--muted); font-weight: 500;
|
|
}
|
|
.dashboard .card-events .event-list { list-style: none; padding: 0; margin: 0; }
|
|
.dashboard .card-events .event-row {
|
|
display: flex; gap: 8px; align-items: baseline;
|
|
padding: 4px 0; border-bottom: 1px dotted var(--border); flex-wrap: wrap;
|
|
}
|
|
.dashboard .card-events .event-row:last-child { border-bottom: none; }
|
|
.dashboard .card-events .event-row .start {
|
|
font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.88em;
|
|
color: var(--muted); min-width: 4.5em;
|
|
}
|
|
.dashboard .card-events .event-row .proj {
|
|
font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.85em; color: var(--muted);
|
|
}
|
|
.dashboard .card-events .event-row .summary { flex: 1; min-width: 8em; }
|
|
.dashboard .card-events .event-row .loc { font-size: 0.85em; }
|
|
.dashboard .card-events .event-row .recurring {
|
|
font-size: 0.85em; color: var(--accent); cursor: help;
|
|
}
|
|
|
|
/* --- /admin index (Phase 3o) --- */
|
|
.admin-cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
|
gap: 16px;
|
|
margin: 16px 0 32px;
|
|
}
|
|
.admin-card {
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
padding: 12px 16px;
|
|
background: var(--surface);
|
|
}
|
|
.admin-card.disabled { opacity: 0.55; }
|
|
.admin-card h2 { margin: 0 0 4px; font-size: 1.05em; }
|
|
.admin-card h2 a { color: var(--accent); text-decoration: none; }
|
|
.admin-card h2 a:hover { text-decoration: underline; }
|
|
.admin-card .count { margin: 0 0 8px; font-size: 0.95em; }
|
|
.admin-card .count strong { font-size: 1.4em; color: var(--fg); }
|
|
.admin-card .desc { margin: 0; font-size: 0.9em; color: var(--muted); }
|
|
|
|
.admin-system { margin-top: 32px; padding-top: 16px; border-top: 1px dotted var(--border); }
|
|
.admin-system h2 { font-size: 1.05em; margin: 0 0 8px; color: var(--muted); }
|
|
.system-grid {
|
|
display: grid;
|
|
grid-template-columns: max-content 1fr;
|
|
gap: 6px 16px;
|
|
margin: 0;
|
|
font-size: 0.92em;
|
|
}
|
|
.system-grid dt { font-weight: 600; color: var(--muted); }
|
|
.system-grid dd { margin: 0; }
|
|
.system-grid code { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 0.9em; color: var(--accent); }
|
|
.system-grid .ok { color: var(--ok); font-weight: 500; }
|
|
|
|
.health-list { list-style: none; padding: 0; margin: 0; }
|
|
.health-row { display: flex; gap: 8px; align-items: center; padding: 4px 0; }
|
|
.health-row .dot {
|
|
display: inline-block; width: 8px; height: 8px; border-radius: 50%;
|
|
background: var(--muted);
|
|
}
|
|
.health-ok .dot { background: var(--ok); }
|
|
.health-down .dot { background: var(--bad); }
|
|
.health-row a { color: var(--muted); text-decoration: none; }
|
|
.health-row a:hover { color: var(--accent); }
|
|
|
|
/* --- /timeline (Phase 4a) --- */
|
|
.timeline .spine { list-style: none; padding: 0; margin: 16px 0; }
|
|
.timeline .spine-day {
|
|
border-left: 2px solid var(--border);
|
|
padding: 4px 0 12px 16px;
|
|
margin-left: 8px;
|
|
position: relative;
|
|
}
|
|
.timeline .spine-day.sticky-today {
|
|
border-left-color: var(--accent);
|
|
}
|
|
.timeline .spine-day.sticky-tomorrow {
|
|
border-left-color: var(--ok);
|
|
}
|
|
.timeline .day-header { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }
|
|
.timeline .day-header h2 {
|
|
margin: 0; font-size: 0.95em; font-weight: 600; color: var(--muted);
|
|
}
|
|
.timeline .day-header h2 a { color: inherit; text-decoration: none; }
|
|
.timeline .day-header h2 a:hover { color: var(--accent); }
|
|
.timeline .sticky-pill {
|
|
display: inline-block;
|
|
background: var(--accent); color: var(--accent-fg);
|
|
padding: 1px 8px; border-radius: 9999px;
|
|
font-size: 0.75em; font-weight: 600; text-transform: uppercase;
|
|
}
|
|
.timeline .spine-day.sticky-tomorrow .sticky-pill { background: var(--ok); }
|
|
.timeline .day-rows { list-style: none; padding: 0; margin: 0; }
|
|
.timeline .row {
|
|
display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
|
|
padding: 4px 6px;
|
|
border-bottom: 1px dotted transparent;
|
|
font-size: 0.95em;
|
|
}
|
|
.timeline .row:hover { background: var(--surface-hover); }
|
|
.timeline .row.far-future { opacity: 0.5; }
|
|
.timeline .row .time {
|
|
display: inline-block; min-width: 48px; font-variant-numeric: tabular-nums;
|
|
color: var(--muted); font-size: 0.85em;
|
|
}
|
|
.timeline .row .proj { color: var(--accent); text-decoration: none; font-size: 0.9em; }
|
|
.timeline .row .proj:hover { text-decoration: underline; }
|
|
.timeline .row .summary { flex: 1; }
|
|
.timeline .kind-badge {
|
|
font-size: 0.7em; font-weight: 600; text-transform: uppercase;
|
|
padding: 1px 6px; border-radius: 3px;
|
|
background: var(--border); color: var(--muted);
|
|
}
|
|
.timeline .kind-event { background: var(--kind-event-bg); color: var(--kind-event-fg); }
|
|
.timeline .kind-todo { background: var(--kind-todo-bg); color: var(--kind-todo-fg); }
|
|
.timeline .kind-doc { background: var(--kind-doc-bg); color: var(--kind-doc-fg); }
|
|
.timeline .kind-creation { background: var(--kind-creation-bg); color: var(--kind-creation-fg); }
|
|
.timeline .row .check button,
|
|
.timeline .row .x {
|
|
background: none; border: 1px solid var(--border);
|
|
border-radius: 3px; padding: 0 6px; cursor: pointer;
|
|
font-size: 0.9em; color: var(--muted);
|
|
}
|
|
.timeline .row .check button:hover { color: var(--ok); border-color: var(--ok); }
|
|
.timeline .row .x:hover { color: var(--bad); border-color: var(--bad); }
|
|
.timeline .row .todo-edit input[type=text] { width: 14em; max-width: 40vw; }
|
|
.timeline .row .todo-edit input[type=date] { width: 9em; }
|
|
.timeline .row.row-todo.done .summary { text-decoration: line-through; color: var(--muted); }
|
|
.timeline .row .per {
|
|
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
font-size: 0.85em; color: var(--accent);
|
|
}
|
|
.timeline .row .ref-id {
|
|
font-size: 0.85em; color: var(--muted); text-decoration: none;
|
|
max-width: 22em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.timeline .row .ref-id:hover { color: var(--accent); text-decoration: underline; }
|
|
.timeline .row .ref-type {
|
|
font-size: 0.7em; font-weight: 600; padding: 1px 4px;
|
|
border-radius: 3px; background: var(--border); color: var(--muted);
|
|
}
|
|
.timeline .row .creation-marker { font-style: italic; }
|
|
.timeline .row .recurring { color: var(--accent); font-size: 0.85em; cursor: help; }
|
|
.timeline .row .duration { font-size: 0.8em; }
|
|
.timeline .empty { padding: 16px 0; font-style: italic; }
|
|
|
|
/* --- /dashboard task-row edit + delete (Phase 4a) --- */
|
|
.dashboard .task-row .task-edit { font-size: 0.85em; }
|
|
.dashboard .task-row .task-edit summary {
|
|
cursor: pointer; padding: 0 4px; user-select: none;
|
|
}
|
|
.dashboard .task-row .task-edit summary:hover { color: var(--accent); }
|
|
.dashboard .task-row .task-edit .todo-edit {
|
|
display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px;
|
|
}
|
|
.dashboard .task-row .task-edit input[type=text] { width: 16em; max-width: 50vw; }
|
|
.dashboard .task-row .task-edit input[type=date] { width: 9em; }
|
|
.dashboard .task-row .todo-delete {
|
|
display: inline-flex; align-items: center;
|
|
}
|
|
.dashboard .task-row .todo-delete .x {
|
|
background: none; border: 1px solid var(--border);
|
|
border-radius: 3px; padding: 0 6px; cursor: pointer;
|
|
font-size: 0.9em; color: var(--muted);
|
|
}
|
|
.dashboard .task-row .todo-delete .x:hover { color: var(--bad); border-color: var(--bad); }
|
|
|
|
/* --- Theme toggle (Phase 4b) — relocated into the Phase 5g sidebar.
|
|
The .theme-icon span keeps its semantic role (the JS toggles its
|
|
textContent ☀ ↔ ☾) but is now restyled as a sidebar nav-icon. The
|
|
pre-5g header .theme-toggle rules are gone. */
|
|
|
|
/* --- Public listing (Phase 4d) --- */
|
|
fieldset.public-listing {
|
|
margin: 20px 0 8px;
|
|
padding: 12px 16px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
background: var(--bg-alt);
|
|
}
|
|
fieldset.public-listing legend {
|
|
font-weight: 600;
|
|
font-size: 0.95em;
|
|
padding: 0 6px;
|
|
color: var(--accent);
|
|
}
|
|
fieldset.public-listing > p.muted {
|
|
margin: 4px 0 12px;
|
|
font-size: 0.85em;
|
|
}
|
|
fieldset.public-listing label { margin-top: 8px; }
|
|
.public-screenshots { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
|
|
.public-screenshot-row { display: flex; gap: 6px; align-items: center; }
|
|
.public-screenshot-row input[type=url] { flex: 1; }
|
|
.public-screenshot-remove {
|
|
background: var(--surface); color: var(--muted);
|
|
border: 1px solid var(--border); border-radius: 3px;
|
|
padding: 2px 8px; font-size: 1em; line-height: 1; cursor: pointer; min-height: 0;
|
|
}
|
|
.public-screenshot-remove:hover { color: var(--bad); border-color: var(--bad); }
|
|
.public-screenshot-add {
|
|
margin-top: 6px;
|
|
background: transparent; color: var(--accent);
|
|
border: 1px solid var(--border); border-radius: 4px;
|
|
padding: 4px 10px; font-size: 0.9em; cursor: pointer; min-height: 0;
|
|
}
|
|
.public-screenshot-add:hover { background: var(--accent); color: var(--accent-fg); }
|
|
|
|
/* --- Detail-page collapsibles (Phase 4e) --- */
|
|
details.proj-section {
|
|
margin: 16px 0;
|
|
border-top: 1px solid var(--border);
|
|
padding-top: 8px;
|
|
}
|
|
details.proj-section[open] {
|
|
border-bottom: 1px dotted var(--border);
|
|
padding-bottom: 8px;
|
|
}
|
|
details.proj-section > summary.proj-section-summary {
|
|
cursor: pointer;
|
|
list-style: none;
|
|
font-size: 1.05em;
|
|
font-weight: 600;
|
|
padding: 4px 0 4px 22px;
|
|
position: relative;
|
|
user-select: none;
|
|
}
|
|
details.proj-section > summary.proj-section-summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
details.proj-section > summary.proj-section-summary::before {
|
|
content: "▸";
|
|
position: absolute;
|
|
left: 4px;
|
|
color: var(--muted);
|
|
font-size: 0.9em;
|
|
transition: transform 0.12s;
|
|
display: inline-block;
|
|
}
|
|
details.proj-section[open] > summary.proj-section-summary::before {
|
|
transform: rotate(90deg);
|
|
}
|
|
details.proj-section > summary.proj-section-summary:hover { color: var(--accent); }
|
|
details.proj-section > summary.proj-section-summary small { font-weight: 400; }
|
|
|
|
.proj-section-reset {
|
|
color: var(--muted);
|
|
font-size: 0.85em;
|
|
text-decoration: none;
|
|
margin-left: auto;
|
|
}
|
|
.proj-section-reset:hover { color: var(--accent); text-decoration: underline; }
|
|
.visually-hidden {
|
|
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
|
}
|
|
|
|
/* --- Calendar month-grid view (Phase 5e) --- */
|
|
.calendar-header {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: baseline;
|
|
gap: 16px;
|
|
margin-bottom: 12px;
|
|
}
|
|
.calendar-header h1 { margin: 0; }
|
|
.calendar-nav {
|
|
display: inline-flex;
|
|
gap: 6px;
|
|
align-items: baseline;
|
|
}
|
|
.calendar-nav a {
|
|
color: var(--muted);
|
|
text-decoration: none;
|
|
padding: 2px 10px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
font-size: 0.9em;
|
|
font-variant-numeric: tabular-nums;
|
|
transition: color 0.12s, border-color 0.12s, background 0.12s;
|
|
}
|
|
.calendar-nav a:hover { color: var(--accent); border-color: var(--accent); }
|
|
.calendar-nav .today { font-weight: 600; }
|
|
#calendar-filterbar {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: flex-end;
|
|
gap: 12px;
|
|
margin: 8px 0 12px;
|
|
}
|
|
#calendar-filterbar .counts {
|
|
margin: 0 0 0 auto;
|
|
align-self: center;
|
|
}
|
|
.calendar-grid {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
table-layout: fixed;
|
|
}
|
|
.calendar-grid thead th {
|
|
text-align: left;
|
|
font-weight: 500;
|
|
color: var(--muted);
|
|
font-size: 0.85em;
|
|
padding: 4px 6px;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.calendar-cell {
|
|
vertical-align: top;
|
|
border: 1px solid var(--border);
|
|
padding: 4px 6px;
|
|
height: 110px;
|
|
width: 14.2857%; /* 1/7 */
|
|
transition: background 0.12s, border-color 0.12s;
|
|
}
|
|
.calendar-cell:hover { background: var(--surface-alt, rgba(127, 127, 127, 0.06)); }
|
|
.calendar-cell.adjacent-month { opacity: 0.45; }
|
|
.calendar-cell.adjacent-month:hover { opacity: 0.7; }
|
|
.calendar-cell.is-today {
|
|
border-color: var(--accent);
|
|
box-shadow: inset 0 0 0 1px var(--accent);
|
|
}
|
|
.cell-header {
|
|
display: flex;
|
|
align-items: baseline;
|
|
justify-content: space-between;
|
|
gap: 4px;
|
|
margin-bottom: 4px;
|
|
}
|
|
.cell-header .day-num {
|
|
font-size: 0.95em;
|
|
font-weight: 600;
|
|
}
|
|
.cell-header .day-label {
|
|
/* Hidden on desktop — only the day number is needed when the weekday
|
|
header runs across the top of the grid. The mobile breakpoint shows
|
|
it. */
|
|
display: none;
|
|
}
|
|
.today-pill {
|
|
font-size: 0.65em;
|
|
background: var(--accent);
|
|
color: var(--accent-fg);
|
|
padding: 1px 6px;
|
|
border-radius: 999px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
line-height: 1.4;
|
|
}
|
|
.cell-rows { list-style: none; padding: 0; margin: 0; }
|
|
.cell-row {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 4px;
|
|
font-size: 0.82em;
|
|
padding: 1px 4px;
|
|
border-radius: 3px;
|
|
margin-bottom: 2px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
line-height: 1.3;
|
|
}
|
|
.cell-row .time { color: var(--muted); font-variant-numeric: tabular-nums; min-width: 32px; }
|
|
.cell-row .row-link { color: inherit; text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
|
|
.cell-row .row-link:hover { color: var(--accent); }
|
|
.cell-row.row-event { border-left: 3px solid var(--accent); padding-left: 6px; }
|
|
.cell-row.row-todo { border-left: 3px solid var(--ok, #5e8ad0); padding-left: 6px; }
|
|
.cell-row.row-todo.overdue { border-left-color: var(--warn, #d08a5e); }
|
|
.cell-row.row-doc { border-left: 3px solid var(--muted); padding-left: 6px; opacity: 0.85; }
|
|
.cell-more {
|
|
display: inline-block;
|
|
font-size: 0.78em;
|
|
margin-top: 2px;
|
|
text-decoration: none;
|
|
}
|
|
.cell-more:hover { color: var(--accent); text-decoration: underline; }
|
|
|
|
/* Mobile breakpoint — collapse the 7-column grid to a vertical list. A
|
|
horizontal week grid on a 360px-wide phone is unreadable; stacking each
|
|
day as its own row keeps the rows full-width and surfaces the long
|
|
weekday label that was hidden on desktop. Adjacent-month cells drop
|
|
out completely on mobile so the list is calendar-scoped. */
|
|
@media (max-width: 480px) {
|
|
.calendar-grid, .calendar-grid thead, .calendar-grid tbody,
|
|
.calendar-grid tr, .calendar-grid th, .calendar-grid td {
|
|
display: block;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
.calendar-grid thead { display: none; }
|
|
.calendar-week { margin-bottom: 4px; }
|
|
.calendar-cell {
|
|
height: auto;
|
|
min-height: 48px;
|
|
padding: 6px 10px;
|
|
margin-bottom: 4px;
|
|
}
|
|
.calendar-cell.adjacent-month { display: none; }
|
|
.cell-header .day-label {
|
|
display: inline;
|
|
font-size: 0.95em;
|
|
color: var(--muted);
|
|
margin-left: 6px;
|
|
}
|
|
.cell-header .day-num { font-size: 1.1em; }
|
|
.cell-row { font-size: 0.95em; padding: 4px 6px; }
|
|
.calendar-nav { flex-wrap: wrap; }
|
|
#calendar-filterbar { gap: 8px; }
|
|
#calendar-filterbar .counts { margin-left: 0; }
|
|
}
|
|
|
|
/* --- Phase 5g layout: desktop sidebar nav --- */
|
|
:root {
|
|
--projax-sidebar-width: 220px;
|
|
--projax-sidebar-collapsed-width: 56px;
|
|
}
|
|
.projax-sidebar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;
|
|
width: var(--projax-sidebar-width);
|
|
background: var(--bg-alt);
|
|
border-right: 1px solid var(--border);
|
|
display: flex;
|
|
flex-direction: column;
|
|
z-index: 50;
|
|
transition: width 200ms ease;
|
|
overflow: hidden;
|
|
}
|
|
html[data-sidebar-collapsed="true"] .projax-sidebar {
|
|
width: var(--projax-sidebar-collapsed-width);
|
|
}
|
|
.projax-sidebar .sidebar-top {
|
|
padding: 14px 16px;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.projax-sidebar .brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
color: var(--fg);
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
font-size: 1.05em;
|
|
white-space: nowrap;
|
|
}
|
|
.projax-sidebar .brand-icon {
|
|
font-size: 1.2em;
|
|
flex-shrink: 0;
|
|
color: var(--accent);
|
|
}
|
|
html[data-sidebar-collapsed="true"] .projax-sidebar .brand-label {
|
|
display: none;
|
|
}
|
|
.projax-sidebar .sidebar-nav {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
padding: 6px 0;
|
|
}
|
|
.projax-sidebar .nav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 8px 16px;
|
|
color: var(--muted);
|
|
text-decoration: none;
|
|
font-size: 0.9em;
|
|
white-space: nowrap;
|
|
border: none;
|
|
background: none;
|
|
width: 100%;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
min-height: 36px;
|
|
box-sizing: border-box;
|
|
-webkit-tap-highlight-color: transparent;
|
|
position: relative;
|
|
}
|
|
.projax-sidebar .nav-item:hover {
|
|
color: var(--fg);
|
|
background: var(--bg);
|
|
}
|
|
.projax-sidebar .nav-item.active {
|
|
color: var(--accent);
|
|
background: var(--bg);
|
|
border-left: 2px solid var(--accent);
|
|
padding-left: 14px;
|
|
}
|
|
.projax-sidebar .nav-icon {
|
|
width: 18px;
|
|
height: 18px;
|
|
flex-shrink: 0;
|
|
}
|
|
.projax-sidebar .nav-label {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
html[data-sidebar-collapsed="true"] .projax-sidebar .nav-label {
|
|
display: none;
|
|
}
|
|
.projax-sidebar .sidebar-bottom {
|
|
border-top: 1px solid var(--border);
|
|
padding: 4px 0;
|
|
}
|
|
.projax-sidebar .sidebar-bottom .logout-form {
|
|
margin: 0;
|
|
}
|
|
.projax-sidebar .theme-icon {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.1em;
|
|
line-height: 1;
|
|
}
|
|
.projax-sidebar .collapse-icon {
|
|
transition: transform 200ms ease;
|
|
}
|
|
html[data-sidebar-collapsed="true"] .projax-sidebar .collapse-icon {
|
|
transform: rotate(180deg);
|
|
}
|
|
@media (max-width: 767px) {
|
|
/* Bottom-nav (Slice B) replaces the sidebar on mobile. Hidden here so
|
|
Slice A can ship without leaving the phone with a 220px-wide hole. */
|
|
.projax-sidebar { display: none; }
|
|
}
|
|
|
|
/* --- Phase 5g layout: mobile bottom-nav + drawer (≤767px) --- */
|
|
.projax-bottom-nav {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: calc(56px + env(safe-area-inset-bottom, 0px));
|
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
background: var(--bg-alt);
|
|
border-top: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: stretch;
|
|
justify-content: space-around;
|
|
z-index: 1021;
|
|
}
|
|
.projax-bottom-nav .bottom-nav-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 2px;
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
padding: 4px 8px;
|
|
color: var(--muted);
|
|
text-decoration: none;
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
font-size: 0.7rem;
|
|
line-height: 1;
|
|
-webkit-tap-highlight-color: transparent;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
-webkit-touch-callout: none;
|
|
}
|
|
.projax-bottom-nav .bottom-nav-item.active {
|
|
color: var(--accent);
|
|
}
|
|
.projax-bottom-nav .bottom-nav-item span { line-height: 1; }
|
|
.projax-bottom-nav .capture-btn { padding: 0; }
|
|
.projax-bottom-nav .capture-circle {
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 50%;
|
|
background: var(--accent);
|
|
color: var(--accent-fg);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-top: -10px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
/* Drawer that pops up from the Menu slot. <details> handles open/close
|
|
natively — no JS. The drawer-sheet is absolutely positioned ABOVE the
|
|
bottom-nav so it doesn't push the grid; tap-outside still closes via
|
|
browser's default <details> dismiss because we keep the sheet inside
|
|
the <details> subtree. */
|
|
.projax-mobile-drawer { position: static; display: contents; }
|
|
.projax-mobile-drawer summary.drawer-toggle {
|
|
list-style: none;
|
|
outline: none;
|
|
}
|
|
.projax-mobile-drawer summary.drawer-toggle::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
.projax-mobile-drawer[open] summary.drawer-toggle { color: var(--accent); }
|
|
.projax-mobile-drawer .drawer-sheet {
|
|
position: fixed;
|
|
right: 8px;
|
|
bottom: calc(56px + env(safe-area-inset-bottom, 0px) + 8px);
|
|
width: min(260px, calc(100vw - 16px));
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: 10px;
|
|
padding: 8px;
|
|
box-shadow: 0 6px 22px rgba(0, 0, 0, 0.32);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
z-index: 1022;
|
|
animation: projax-drawer-up 160ms ease-out both;
|
|
}
|
|
@keyframes projax-drawer-up {
|
|
from { transform: translateY(8px); opacity: 0; }
|
|
to { transform: translateY(0); opacity: 1; }
|
|
}
|
|
.projax-mobile-drawer .drawer-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 10px 12px;
|
|
color: var(--fg);
|
|
text-decoration: none;
|
|
font-size: 0.95em;
|
|
background: none;
|
|
border: none;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
width: 100%;
|
|
text-align: left;
|
|
min-height: 40px;
|
|
}
|
|
.projax-mobile-drawer .drawer-item:hover { background: var(--bg-alt); }
|
|
.projax-mobile-drawer .drawer-item.active { color: var(--accent); background: var(--bg-alt); }
|
|
.projax-mobile-drawer .drawer-icon {
|
|
width: 18px;
|
|
height: 18px;
|
|
flex-shrink: 0;
|
|
}
|
|
.projax-mobile-drawer .drawer-icon.theme-icon {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.1em;
|
|
}
|
|
.projax-mobile-drawer .drawer-form { margin: 0; }
|
|
.projax-mobile-drawer .drawer-logout { color: var(--bad); }
|
|
|
|
@media (min-width: 768px) {
|
|
.projax-bottom-nav { display: none; }
|
|
}
|
|
@media (max-width: 767px) {
|
|
main.projax-main {
|
|
padding-bottom: calc(56px + 1rem + env(safe-area-inset-bottom, 0px));
|
|
}
|
|
}
|
|
|
|
/* --- Detail page: form-group ordering polish (Phase 5i, detail-page-order) --- */
|
|
.detail-form { display: flex; flex-direction: column; gap: 20px; max-width: 720px; }
|
|
.detail-form .form-group { display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 0; }
|
|
.detail-form .form-group-heading {
|
|
margin: 0;
|
|
font-size: 0.78em;
|
|
font-weight: 600;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
border-bottom: 1px dotted var(--border);
|
|
padding-bottom: 4px;
|
|
}
|
|
.detail-form .form-group-flags { flex-direction: row; flex-wrap: wrap; gap: 18px 24px; align-items: baseline; }
|
|
.detail-form .form-group-flags .form-group-heading { flex-basis: 100%; }
|
|
.detail-form .form-group-content-label > textarea { font-family: ui-monospace, SFMono-Regular, monospace; }
|
|
.detail-form .form-group-content-label { gap: 0; }
|
|
.detail-form > details.proj-section { margin-top: 4px; }
|
|
|
|
/* Divider between the editable form and the read-only auxiliary
|
|
collapsibles. <hr> is semantically a thematic break — matches the
|
|
intent. The "Related" heading below it makes the change-of-mode
|
|
obvious without leaning on the line alone. */
|
|
.aux-divider {
|
|
border: 0;
|
|
border-top: 1px solid var(--border);
|
|
margin: 32px 0 16px;
|
|
}
|
|
.aux-sections { display: flex; flex-direction: column; gap: 4px; max-width: 720px; }
|
|
.aux-heading {
|
|
margin: 0 0 8px;
|
|
font-size: 0.95em;
|
|
font-weight: 600;
|
|
color: var(--muted);
|
|
}
|
|
.aux-reset { margin: 12px 0 0; font-size: 0.85em; }
|
|
.aux-reset .proj-section-reset { color: var(--muted); }
|
|
.aux-reset .proj-section-reset:hover { color: var(--bad); }
|