Files
pad/web/src/app.css
T
xarmian 6d08b72f5d fix(web): thin horizontal scrollbars — set ::-webkit-scrollbar height (BUG-2127 follow-up) (#950)
The global `::-webkit-scrollbar` rule set `width: 6px` but no `height`.
`width` only sizes vertical scrollbars; a horizontal scrollbar's thickness
is `height`, which was unset — so horizontal scrollbars (notably the
board-view lane track) fell back to the chunky ~15px browser default while
every vertical scrollbar was a thin 6px. That "weirdly large horizontal
scrollbar" is the real defect PR #946 papered over by making the lanes
shrink-to-fill so horizontal overflow never occurred. Add `height: 6px`
so horizontal and vertical scrollbars match.

Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra
2026-07-16 22:08:54 -04:00

994 lines
26 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:root {
--bg-primary: #1a1a1a;
--bg-secondary: #242424;
--bg-tertiary: #2e2e2e;
--bg-hover: #353535;
--bg-active: #3a3a3a;
--text-primary: #e0e0e0;
--text-secondary: #a0a0a0;
--text-muted: #666666;
--border: #333333;
--border-subtle: #2a2a2a;
--accent-blue: #4a9eff;
--accent-green: #4ade80;
--accent-purple: #a78bfa;
--accent-amber: #fbbf24;
--accent-cyan: #22d3ee;
--accent-orange: #fb923c;
--accent-gray: #9ca3af;
--accent-brown: #a8896c;
--status-draft: #666666;
--status-active: #4ade80;
--status-completed: #4a9eff;
--status-archived: #444444;
--topbar-height: 44px;
--bottom-nav-height: 56px;
--context-bar-height: 44px;
--sidebar-width: 260px;
--detail-panel-width: 300px;
--content-max-width: 960px;
--font-ui: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--font-content: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--radius: 6px;
--radius-sm: 4px;
--radius-lg: 8px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
font-family: var(--font-ui);
font-size: 14px;
line-height: 1.5;
color: var(--text-primary);
background: var(--bg-primary);
-webkit-font-smoothing: antialiased;
}
a {
color: var(--accent-blue);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
button {
font-family: inherit;
font-size: inherit;
cursor: pointer;
border: none;
background: none;
color: inherit;
}
input, textarea, select {
font-family: inherit;
font-size: inherit;
color: var(--text-primary);
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: var(--space-2) var(--space-3);
outline: none;
}
input:focus, textarea:focus {
border-color: var(--accent-blue);
}
/* Scrollbar. `width` styles the VERTICAL scrollbar; `height` styles the
HORIZONTAL one. Both are needed — without `height` a horizontal scrollbar
(e.g. the board-view lane track) falls back to the chunky ~15px browser
default while every vertical scrollbar is a thin 6px, which reads as a
"weirdly large" scrollbar (BUG-2127 follow-up). Keep them equal. */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 3px;
}
/* Prose styles for rendered markdown */
.prose {
font-family: var(--font-content);
font-size: 15px;
line-height: 1.7;
color: var(--text-primary);
max-width: var(--content-max-width);
}
.prose h1 { font-size: 1.8em; margin: 1.2em 0 0.6em; font-weight: 700; }
.prose h2 { font-size: 1.4em; margin: 1em 0 0.5em; font-weight: 600; }
.prose h3 { font-size: 1.15em; margin: 0.8em 0 0.4em; font-weight: 600; }
.prose p { margin: 0.6em 0; }
.prose ul, .prose ol { margin: 0.6em 0; padding-left: 1.5em; }
.prose li { margin: 0.2em 0; }
.prose li input[type="checkbox"] { margin-right: 0.4em; }
.prose pre {
background: var(--bg-tertiary);
padding: var(--space-4);
border-radius: var(--radius);
overflow-x: auto;
margin: 0.8em 0;
font-family: var(--font-mono);
font-size: 0.9em;
line-height: 1.5;
}
.prose code {
background: var(--bg-tertiary);
padding: 0.15em 0.4em;
border-radius: var(--radius-sm);
font-family: var(--font-mono);
font-size: 0.9em;
}
.prose pre code {
background: none;
padding: 0;
}
.prose blockquote {
border-left: 3px solid var(--border);
padding-left: var(--space-4);
margin: 0.8em 0;
color: var(--text-secondary);
}
.prose table {
border-collapse: collapse;
width: 100%;
margin: 0.8em 0;
}
.prose th, .prose td {
border: 1px solid var(--border);
padding: var(--space-2) var(--space-3);
text-align: left;
}
.prose th {
background: var(--bg-secondary);
font-weight: 600;
}
.prose hr {
border: none;
border-top: 1px solid var(--border);
margin: 1.5em 0;
}
.prose img {
max-width: 100%;
border-radius: var(--radius);
}
.prose a.doc-link {
background: var(--bg-tertiary);
padding: 0.1em 0.5em;
border-radius: var(--radius-sm);
color: var(--accent-blue);
font-weight: 500;
}
.prose a.doc-link:hover {
background: var(--bg-hover);
text-decoration: none;
}
.prose .doc-link.broken {
color: var(--accent-orange);
opacity: 0.7;
text-decoration: line-through;
}
/* External links in rendered markdown */
.prose .external-link {
color: var(--accent-blue);
text-decoration: none;
}
.prose .external-link:hover {
text-decoration: underline;
}
.prose .external-icon {
font-size: 0.75em;
opacity: 0.6;
vertical-align: super;
}
/* Editor link styles — autolinked URLs and wiki-links */
.prose a.editor-link {
color: var(--accent-blue);
text-decoration: none;
border-bottom: 1px solid color-mix(in srgb, var(--accent-blue) 30%, transparent);
transition: border-color 0.15s;
}
.prose a.editor-link:hover {
border-bottom-color: var(--accent-blue);
}
/* Attachment images — both inside the editor and in rendered markdown.
Lazy-loaded via the node's renderHTML/NodeView; styles below set a
sane default size so the editor doesn't reflow once thumbnails arrive. */
.attachment-image {
max-width: 100%;
height: auto;
border-radius: var(--radius);
cursor: zoom-in;
display: block;
margin: 0.4em 0;
}
.attachment-image:hover {
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-blue) 35%, transparent);
}
/* Wrapper appended by the AttachmentImage NodeView to host the
selection-time rotate toolbar (TASK-879). Position relative so
the toolbar can pin to the top-right corner of the image. */
.attachment-image-wrapper {
position: relative;
display: inline-block;
max-width: 100%;
}
.attachment-image-wrapper.attachment-image-selected .attachment-image {
box-shadow: 0 0 0 2px var(--accent-blue);
}
/* Rotate toolbar — appears when the AttachmentImage node is
selected. Pinned top-right with absolute positioning so it doesn't
shift the image's layout when toggling visibility. */
.attachment-image-toolbar {
position: absolute;
top: 8px;
right: 8px;
display: inline-flex;
gap: 2px;
padding: 4px;
background: rgba(20, 20, 20, 0.85);
border-radius: var(--radius);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
z-index: 5;
-webkit-user-select: none;
user-select: none;
}
.attachment-image-toolbar-hidden {
display: none;
}
.attachment-image-toolbar-btn {
width: 28px;
height: 28px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
border: none;
background: transparent;
color: #fff;
font-size: 18px;
line-height: 1;
border-radius: 4px;
cursor: pointer;
transition: background 0.15s, opacity 0.15s;
font-family: inherit;
}
.attachment-image-toolbar-btn:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.15);
}
.attachment-image-toolbar-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
/* Attachment file chips — Notion-style chip rendering for non-image
attachments. Used by both the editor's AttachmentChip node and the
read-only markdown render path (TASK-874). Styles target the same
class names from both surfaces so the visual matches edge to edge. */
.file-chip {
display: inline-flex;
align-items: center;
gap: 0.4em;
max-width: 100%;
padding: 4px 10px;
margin: 0 1px;
background: var(--bg-tertiary);
border: 1px solid var(--border-subtle);
border-radius: 6px;
color: var(--text-primary);
text-decoration: none;
font-size: 0.92em;
line-height: 1.3;
vertical-align: baseline;
transition: background 0.15s, border-color 0.15s;
cursor: pointer;
}
.file-chip:hover {
background: var(--bg-hover);
border-color: color-mix(in srgb, var(--accent-blue) 35%, var(--border));
text-decoration: none;
}
.file-chip-icon {
flex-shrink: 0;
font-size: 1.05em;
line-height: 1;
}
.file-chip-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 38ch;
font-weight: 500;
}
.file-chip-size {
flex-shrink: 0;
color: var(--text-muted);
font-size: 0.88em;
}
.file-chip-size:empty {
display: none;
}
/* Editor selection styling — when the chip is the ProseMirror
selection (atom node selected for delete/replace), give it the same
selection treatment ProseMirror gives other inline nodes. */
.ProseMirror-selectednode.file-chip,
.file-chip.ProseMirror-selectednode {
outline: 2px solid var(--accent-blue);
outline-offset: 1px;
}
/* Upload-in-progress placeholder — rendered as a ProseMirror widget
decoration by the AttachmentUpload extension. Dashed border + faded
color so it visually reads as "not yet committed content" while the
network round-trip resolves. */
.attachment-upload-placeholder {
display: inline-flex;
align-items: center;
gap: 0.4em;
padding: 4px 10px;
margin: 0 1px;
background: var(--bg-tertiary);
border: 1px dashed color-mix(in srgb, var(--accent-blue) 50%, var(--border));
border-radius: 6px;
color: var(--text-secondary);
font-size: 0.92em;
line-height: 1.3;
vertical-align: baseline;
}
.attachment-upload-spinner {
width: 12px;
height: 12px;
border: 2px solid color-mix(in srgb, var(--accent-blue) 35%, transparent);
border-top-color: var(--accent-blue);
border-radius: 50%;
animation: attachment-upload-spin 0.7s linear infinite;
flex-shrink: 0;
}
@keyframes attachment-upload-spin {
to {
transform: rotate(360deg);
}
}
.attachment-upload-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 30ch;
}
/* Crop modal (TASK-880). Appended to document.body so styles must
be global. Uses the native <dialog> element with a flex layout
so the image fills available space while header/footer stay fixed. */
dialog.attachment-crop-modal {
border: none;
background: transparent;
padding: 0;
max-width: 100vw;
max-height: 100vh;
color: #fff;
overflow: visible;
}
dialog.attachment-crop-modal::backdrop {
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(2px);
}
.attachment-crop-modal-shell {
display: flex;
flex-direction: column;
width: min(95vw, 1200px);
height: min(92vh, 900px);
background: var(--bg-secondary);
color: var(--text-primary);
border-radius: var(--radius);
overflow: hidden;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55);
}
.attachment-crop-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
padding: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--border);
background: var(--bg-secondary);
}
.attachment-crop-modal-title {
font-weight: 600;
font-size: 0.95em;
}
.attachment-crop-modal-aspects {
display: inline-flex;
gap: 4px;
background: var(--bg-tertiary);
padding: 2px;
border-radius: var(--radius);
}
.attachment-crop-modal-aspect-btn {
padding: 4px 12px;
border: none;
background: transparent;
color: var(--text-secondary);
border-radius: 4px;
font-size: 0.85em;
cursor: pointer;
font-family: inherit;
transition: background 0.15s, color 0.15s;
}
.attachment-crop-modal-aspect-btn:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.attachment-crop-modal-aspect-active,
.attachment-crop-modal-aspect-active:hover {
background: var(--accent-blue);
color: #fff;
}
.attachment-crop-modal-stage {
flex: 1;
min-height: 0;
position: relative;
overflow: hidden;
background: rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
justify-content: center;
-webkit-user-select: none;
user-select: none;
}
.attachment-crop-modal-img {
max-width: 100%;
max-height: 100%;
display: block;
-webkit-user-drag: none;
pointer-events: none;
}
.attachment-crop-modal-rect {
position: absolute;
box-sizing: border-box;
border: 1px solid #fff;
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.45);
cursor: move;
touch-action: none;
}
.attachment-crop-modal-handle {
position: absolute;
width: 12px;
height: 12px;
background: var(--accent-blue);
border: 2px solid #fff;
border-radius: 50%;
touch-action: none;
}
.attachment-crop-modal-handle-nw {
top: -7px;
left: -7px;
cursor: nwse-resize;
}
.attachment-crop-modal-handle-ne {
top: -7px;
right: -7px;
cursor: nesw-resize;
}
.attachment-crop-modal-handle-sw {
bottom: -7px;
left: -7px;
cursor: nesw-resize;
}
.attachment-crop-modal-handle-se {
bottom: -7px;
right: -7px;
cursor: nwse-resize;
}
.attachment-crop-modal-footer {
display: flex;
justify-content: flex-end;
gap: var(--space-2);
padding: var(--space-3) var(--space-4);
border-top: 1px solid var(--border);
background: var(--bg-secondary);
}
.attachment-crop-modal-cancel,
.attachment-crop-modal-apply {
padding: 6px 16px;
border: 1px solid var(--border);
background: var(--bg-tertiary);
color: var(--text-primary);
border-radius: var(--radius);
font-family: inherit;
font-size: 0.92em;
cursor: pointer;
transition: background 0.15s, border-color 0.15s;
}
.attachment-crop-modal-cancel:hover {
background: var(--bg-hover);
}
.attachment-crop-modal-primary {
background: var(--accent-blue);
color: #fff;
border-color: var(--accent-blue);
}
.attachment-crop-modal-primary:hover {
filter: brightness(1.1);
}
/* Lightbox dialog — appended to document.body when an attachment image
is clicked, so styles must be global rather than scoped to Editor.svelte.
Uses the native <dialog> element; the ::backdrop pseudo handles the
overlay shade. The dialog content fills the viewport with a centered
image and a single close button. */
dialog.attachment-image-lightbox {
border: none;
background: transparent;
padding: 0;
max-width: 100vw;
max-height: 100vh;
overflow: visible;
color: #fff;
}
dialog.attachment-image-lightbox::backdrop {
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(2px);
}
dialog.attachment-image-lightbox .attachment-image-lightbox-img {
max-width: 95vw;
max-height: 95vh;
display: block;
margin: 0 auto;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55);
cursor: zoom-out;
border-radius: var(--radius);
}
dialog.attachment-image-lightbox .attachment-image-lightbox-close {
position: fixed;
top: 16px;
right: 16px;
width: 40px;
height: 40px;
border-radius: 50%;
border: none;
background: rgba(0, 0, 0, 0.6);
color: #fff;
font-size: 28px;
line-height: 1;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
dialog.attachment-image-lightbox .attachment-image-lightbox-close:hover {
background: rgba(0, 0, 0, 0.85);
}
/* Light mode */
[data-theme="light"] {
--bg-primary: #ffffff;
--bg-secondary: #f7f7f8;
--bg-tertiary: #eeeff1;
--bg-hover: #e5e6e8;
--bg-active: #dddee0;
--text-primary: #1a1a1a;
--text-secondary: #555555;
--text-muted: #999999;
--border: #e0e0e0;
--border-subtle: #eaeaea;
--accent-blue: #2563eb;
--accent-green: #16a34a;
--accent-purple: #7c3aed;
--accent-amber: #d97706;
--accent-cyan: #0891b2;
--accent-orange: #ea580c;
--accent-gray: #6b7280;
--accent-brown: #92400e;
--status-draft: #999999;
--status-active: #16a34a;
--status-completed: #2563eb;
--status-archived: #bbbbbb;
}
@media (prefers-color-scheme: light) {
:root:not([data-theme="dark"]) {
--bg-primary: #ffffff;
--bg-secondary: #f7f7f8;
--bg-tertiary: #eeeff1;
--bg-hover: #e5e6e8;
--bg-active: #dddee0;
--text-primary: #1a1a1a;
--text-secondary: #555555;
--text-muted: #999999;
--border: #e0e0e0;
--border-subtle: #eaeaea;
--accent-blue: #2563eb;
--accent-green: #16a34a;
--accent-purple: #7c3aed;
--accent-amber: #d97706;
--accent-cyan: #0891b2;
--accent-orange: #ea580c;
--accent-gray: #6b7280;
--accent-brown: #92400e;
--status-draft: #999999;
--status-active: #16a34a;
--status-completed: #2563eb;
--status-archived: #bbbbbb;
}
}
/* Responsive */
@media (max-width: 768px) {
:root {
--sidebar-width: 280px;
}
}
/* ============================================================================
Print styles (PLAN-620 / TASK-621).
Tune Ctrl/Cmd+P output so users can save any Pad page as a clean PDF.
This file handles LAYOUT-LEVEL concerns:
- hide the workspace chrome (sidebar, top bar, modals, floating UI)
- force a light color palette regardless of the active theme
- unlock the fixed 100vh / overflow:hidden app shell so content flows
across pages in the print preview
Item-level formatting (title, properties block, markdown body, header /
footer rules, child-item checklist) is layered on top in tasks 622–624.
============================================================================ */
@media print {
/* Force a light color palette for print regardless of the active theme.
We only need to pin the color variables; spacing / radii stay. */
:root,
:root[data-theme="dark"],
:root[data-theme="light"] {
--bg-primary: #ffffff;
--bg-secondary: #ffffff;
--bg-tertiary: #f5f5f5;
--bg-hover: transparent;
--bg-active: transparent;
--text-primary: #000000;
--text-secondary: #222222;
--text-muted: #555555;
--border: #cccccc;
--border-subtle: #dddddd;
}
html, body {
height: auto;
background: #ffffff !important;
color: #000000 !important;
font-size: 11pt;
}
/* Unlock the screen shell — it uses height: 100vh / overflow: hidden
to keep the sidebar + main column pinned. In print we need the
content to flow naturally across pages. */
.app-layout,
.app-shell {
display: block !important;
height: auto !important;
max-height: none !important;
overflow: visible !important;
}
.main-content {
flex: none !important;
width: 100% !important;
min-width: 0 !important;
overflow: visible !important;
}
/* Hide the workspace chrome. */
.sidebar,
.topbar,
.sidebar-expand-btn,
.topbar-expand-btn,
.mobile-header,
.hamburger,
.toast-container,
.overlay,
.palette,
.modal,
.modal-backdrop,
dialog[open] {
display: none !important;
}
/* Modals / dialogs / tooltips. Most KeyboardShortcuts-style overlays
carry role="dialog" on the backdrop. The shared Modal primitive uses a
native <dialog> (implicit dialog role, no literal role attribute), so it
is covered by the `dialog[open]` selector above. */
[role="dialog"],
[role="tooltip"],
[role="menu"] {
display: none !important;
}
/* Opt-in: any element tagged with data-print-hide disappears in print.
Components can use this to strip interactive affordances (edit buttons,
status pickers, hover cards, etc.) without needing a new CSS rule. */
[data-print-hide] {
display: none !important;
}
/* Strip decorative shadows, gradients, and background images. */
* {
box-shadow: none !important;
text-shadow: none !important;
background-image: none !important;
}
/* Page margins + page number (PLAN-620 / BUG-625).
- Top/bottom margins carve out space for the rendered header /
footer (TASK-623). Previously declared in +page.svelte, but
Svelte scoped-style timing meant the rule didn't always win
over this block, causing the header strip to overlap the
title on page 1. Single source of truth here.
- The page number lives in `@bottom-right` because
`counter(page)` evaluated inside a fixed element's ::after
pseudo captures its initial value (0) and never increments
per page in Chromium. Margin-box counters evaluate per page
correctly. */
@page {
/* Top margin is generous-but-plain (no reserved header strip):
the page-1 document header lives in normal flow via
`.title-row` + `.meta-info`, so subsequent pages start at
this margin with no overlap risk. Bottom margin reserves
space for the fixed footer + the `@bottom-right` page number.
See BUG-626 for why the repeating header was dropped. */
margin: 0.6in 0.6in 1in 0.6in;
@bottom-right {
content: "Page " counter(page);
font-family: var(--font-ui);
font-size: 9pt;
color: #555;
}
}
/* -----------------------------------------------------------------
TASK-622 — rendered markdown (.prose) tuned for print.
----------------------------------------------------------------- */
.prose {
font-size: 11pt;
line-height: 1.55;
color: #000 !important;
max-width: none;
}
.prose h1, .prose h2, .prose h3,
.prose h4, .prose h5, .prose h6 {
color: #000 !important;
page-break-after: avoid;
break-after: avoid-page;
page-break-inside: avoid;
break-inside: avoid-page;
}
.prose h1 { font-size: 18pt; }
.prose h2 { font-size: 14pt; }
.prose h3 { font-size: 12pt; }
.prose p, .prose li {
orphans: 3;
widows: 3;
}
/* Links: keep the colored style AND append the URL inline so a printed
page still carries the reference. Skip internal wiki-links (which
resolve to the printed document's siblings) and fragment-only links.
Note: rich-editor links are rendered via Tiptap's SafeLink extension
using `data-href` instead of `href`, so we target both. */
.prose a[href]::after {
content: " (" attr(href) ")";
font-size: 0.85em;
color: #555;
word-break: break-all;
}
.prose a[data-href]::after {
content: " (" attr(data-href) ")";
font-size: 0.85em;
color: #555;
word-break: break-all;
}
.prose a[href^="#"]::after,
.prose a[data-href^="#"]::after,
.prose a.doc-link::after,
.prose a.editor-link[href^="/"]::after,
.prose a.editor-link[data-href^="/"]::after {
content: "";
}
/* Code blocks: visible, readable in light palette, don't orphan across
pages when we can help it. */
.prose pre {
background: #f5f5f5 !important;
border: 1px solid #ddd !important;
color: #000 !important;
font-size: 9.5pt;
page-break-inside: avoid;
break-inside: avoid;
}
.prose code {
background: #f5f5f5 !important;
color: #000 !important;
}
/* Images: full page width max, don't split across pages. */
.prose img {
max-width: 100% !important;
page-break-inside: avoid;
break-inside: avoid;
}
/* Tables: visible borders, contained to page width. */
.prose table {
page-break-inside: avoid;
break-inside: avoid;
border: 1px solid #ccc !important;
}
.prose th, .prose td {
border: 1px solid #ccc !important;
color: #000 !important;
}
.prose th {
background: #f5f5f5 !important;
}
.prose blockquote {
border-left: 3px solid #666 !important;
color: #333 !important;
}
/* Editor overlays (bubble menu, link popover, slash menu, mobile
toolbar, table toolbar, editor-toolbar) — never print these. */
.bubble-menu,
.link-popover,
.slash-menu,
.mobile-toolbar,
.table-toolbar,
.toolbar,
.editor-toolbar,
.editor-link-popover,
.editor-bubble-menu {
display: none !important;
}
/* Editor wrapper: in print the content is read-only — remove any
interactive cursor, borders, or focus styling. */
.editor-wrapper,
.editor-content {
border: none !important;
background: transparent !important;
padding: 0 !important;
outline: none !important;
}
/* -----------------------------------------------------------------
TASK-622 — Properties panel form widgets in print.
These rules live here (not in the item detail page's scoped style
block) because the widgets are rendered by the FieldEditor child
component — Svelte's scoped selectors don't cross component
boundaries. Strip interactive styling so the selected value reads
as plain text; the `.toggle` checkbox button is exempted and
rendered as a visible outlined box with a check mark when on.
----------------------------------------------------------------- */
.field-value select,
.field-value input,
.field-value textarea,
.field-value button:not(.toggle) {
appearance: none !important;
-webkit-appearance: none !important;
background: transparent !important;
border: none !important;
padding: 0 !important;
margin: 0 !important;
color: #000 !important;
font: inherit !important;
cursor: default !important;
pointer-events: none;
box-shadow: none !important;
}
/* Custom select controls (FieldEditor) render a dropdown chevron as
an inline SVG (`.select-chevron`) inside the button — `appearance:
none` on the button doesn't affect SVG children, so the `∨` caret
leaks into print. Same story for the expanded dropdown panel
(`.select-dropdown`) if one happens to be open at print time.
BUG-625. */
.field-value .select-chevron,
.field-value .select-dropdown,
.select-chevron,
.select-dropdown {
display: none !important;
}
/* Checkbox field (`.toggle`): visible outlined box; on-state gets a
check mark overlay so the on/off signal survives in print. */
.field-value .toggle {
appearance: none !important;
-webkit-appearance: none !important;
display: inline-block !important;
width: 11pt !important;
height: 11pt !important;
padding: 0 !important;
margin: 0 !important;
background: #fff !important;
border: 1px solid #000 !important;
border-radius: 2pt !important;
position: relative;
cursor: default !important;
pointer-events: none;
box-shadow: none !important;
vertical-align: -1pt;
}
.field-value .toggle-knob {
display: none !important;
}
.field-value .toggle.on::after {
content: "✓";
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: #000;
font-size: 10pt;
font-weight: 700;
line-height: 1;
}
}
/*
Mobile bottom-nav reflow (PLAN-1694). <BottomNav /> is rendered in the
workspace layout and uses position: fixed; bottom: 0 on mobile. It toggles
`body.has-bottom-nav` while mounted on mobile so the scroll container
(.main-content, defined in the root +layout.svelte) gets bottom padding and
content can't hide under the bar. Gated by the same ≤768px breakpoint
uiStore.isMobile uses, and by the body class so non-workspace shell pages
(which share .main-content but have no bottom nav) aren't padded. The
safe-area inset keeps the last row clear of the iOS home indicator.
*/
@media (max-width: 768px) {
body.has-bottom-nav .main-content {
padding-bottom: calc(var(--bottom-nav-height) + env(safe-area-inset-bottom, 0px));
}
/*
Contextual top bar on detail screens (MobileContextBar, PLAN-1694
Phase 2). It's position: fixed; top: 0 and toggles body.has-context-bar
while shown, so pad the scroll container's top to match. Same body-class
pattern as the bottom-nav reflow above; safe-area inset clears the notch.
*/
body.has-context-bar .main-content {
padding-top: calc(var(--context-bar-height) + env(safe-area-inset-top, 0px));
}
}