Files
odysseus/static/css/15-notes.css
T
Alexandre Teixeira f74a262f73 merge: reconcile PR 40 with current lab
Integrate lab fff55a78 into PR #40 (cc25d5ba). Lab's modular email
backend/frontend, modular settings, split stylesheets (static/style.css
stays deleted), procfs compatibility, and request-scoped TurnContract
authority win; PR #40's routing classifiers, editor/email/task features,
and style.css changes are ported into lab's module and stylesheet homes.

Integration fixes:
- settings/api.js imports ui.js under its canonical versioned URL
- browser observations keep legacy CAPTCHA/access-block evidence
- artifact turns do not re-trigger broad-web research recovery
- env reference documents PR test-tool variables; page regenerated

PR #40 defects surfaced by lab gates and fixed here:
- web_fetch generic schema drops top-level anyOf (OpenAI contract);
  the compact preview contract still requires url or urls
- get_weather registered as a brokered network read
- new lazy editor modules precached for offline use
- SearXNG pin mirrored into GPU standalone compose files
- image model picker again skips offline endpoints

Tests updated where PR #40 changed behaviour on purpose, and PR tests
moved onto lab's document_source helpers.
2026-10-01 05:03:58 +01:00

4036 lines
118 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.
/* 15-notes - part of the former static/style.css.
*
* The stylesheet is split by area, but the split is mechanical: rules were
* moved verbatim and NOT regrouped. The numeric prefix is the load order, and
* that order is what preserves the cascade. A rule sits in the file its
* original position fell into, so an area's rules are not all in its file and
* a file is not only that area's rules.
*
* Do not reorder the <link> tags in static/index.html, and do not move a rule
* between files: with 1,151 redeclarations and 1,886 !important in this
* cascade, either can change which declaration wins.
*
* static/index.html and the PRECACHE list in static/sw.js must agree on this
* order. tests/test_static_stylesheet_manifest.py and
* tests/test_css_computed_style_snapshot.py fail if that drifts.
*/
body.notes-drag-mode .note-card {
cursor: grab;
transform: scale(0.985);
filter: saturate(0.7);
opacity: 0.92;
position: relative;
overflow: hidden;
transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.3, 1),
filter 0.18s ease,
opacity 0.18s ease;
}
@media (max-width: 768px) {
body.notes-drag-mode .note-card {
filter: none;
opacity: 0.96;
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent, var(--red)) 28%, transparent);
}
body.notes-drag-mode .note-card::after {
display: none;
}
}
/* Placeholder — same footprint as the lifted card, dotted outline so
the drop target is visible while the card follows the finger. */
.note-card-placeholder {
background: color-mix(in srgb, var(--fg) 5%, transparent);
border: 2px dashed color-mix(in srgb, var(--fg) 25%, transparent);
border-radius: 8px;
box-sizing: border-box;
flex-shrink: 0;
transition: width 0.18s ease, height 0.18s ease;
}
/* ── Fullscreen single-note edit overlay ──────────────── */
.note-fullscreen-overlay {
position: fixed;
inset: 0;
z-index: 10500;
background: var(--bg);
display: flex;
flex-direction: column;
opacity: 0;
/* Bigger zoom range so the transition reads as a real "tile expands
into a page" motion. transform-origin is set per-tile by JS. */
transform: scale(0.45);
transform-origin: center center;
transition: opacity 0.28s ease, transform 0.32s cubic-bezier(0.18, 0.74, 0.24, 1.06);
will-change: transform, opacity;
}
.note-fullscreen-overlay.open {
opacity: 1;
transform: scale(1);
}
.note-fullscreen-overlay.closing {
opacity: 0;
transform: scale(0.7);
transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.4, 0, 0.6, 1);
}
.note-fullscreen-header {
flex-shrink: 0;
padding: 8px 12px;
display: flex;
align-items: center;
gap: 6px;
border-bottom: 1px solid var(--border);
}
.note-fullscreen-actions {
margin-left: auto;
display: flex;
align-items: center;
gap: 6px;
}
/* Compact the relocated Archive/Delete buttons so two of them fit
alongside the back chevron without crowding. */
.note-fullscreen-actions .note-form-text-btn {
padding: 8px 12px;
font-size: 12px;
border-radius: 8px;
}
.note-fullscreen-actions .note-form-text-btn svg {
width: 14px;
height: 14px;
margin-right: 4px;
}
.note-fullscreen-back {
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px; height: 40px;
background: none;
border: none;
color: var(--fg);
border-radius: 8px;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.note-fullscreen-back:active {
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.note-fullscreen-body {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 8px 14px 24px;
}
/* The reused .note-form inside the fullscreen overlay should breathe —
it was originally sized to slot into a grid cell. */
.note-fullscreen-body .note-form {
max-width: 720px;
margin: 0 auto;
border: none;
box-shadow: none;
background: transparent;
}
/* Bigger touch targets inside the fullscreen overlay. The default form
is tuned for the grid-card context (tiny icons fit a 200px wide
tile); on a phone-sized full-bleed page we want chunky thumb-sized
controls. */
.note-fullscreen-back svg {
width: 28px;
height: 28px;
}
.note-fullscreen-overlay .note-form-type-pill svg {
width: 16px;
height: 16px;
}
.note-fullscreen-overlay .note-cl-row {
padding: 8px 0;
gap: 8px;
align-items: center;
}
.note-fullscreen-overlay .note-cl-grip {
font-size: 22px;
line-height: 1;
padding: 6px 8px;
opacity: 0.5;
cursor: grab;
touch-action: none;
user-select: none;
/* Pull 4px toward the left edge of the row. */
margin-left: -4px;
}
.note-fullscreen-overlay .note-cl-dot {
width: 16px;
height: 16px;
border-width: 2px;
border-radius: 50%;
flex-shrink: 0;
position: relative;
/* Generous invisible tap target around the visible dot */
}
/* Filled checkmark glyph when the row is marked done — gives a clear
visual cue beyond the colored fill. Nudged up 2px so it sits
centered in the smaller dot. */
.note-fullscreen-overlay .note-cl-row.done .note-cl-dot::after {
content: '';
position: absolute;
left: 2px; top: 1px;
width: 8px; height: 4px;
border-left: 2px solid #fff;
border-bottom: 2px solid #fff;
transform: rotate(-45deg);
}
.note-fullscreen-overlay .note-cl-rm {
width: 40px;
height: 40px;
font-size: 28px;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 0.7;
/* Nudge 4px further toward the right edge of the row. */
margin-right: -4px;
}
/* Lifted row + placeholder while dragging a checklist item. Mirrors
the card-drag look so the two interactions feel consistent. */
.note-cl-row-dragging {
box-shadow: 0 12px 28px rgba(0,0,0,0.4);
background: var(--bg);
border-radius: 8px;
opacity: 0.98;
}
.note-cl-row-placeholder {
background: color-mix(in srgb, var(--fg) 5%, transparent);
border: 2px dashed color-mix(in srgb, var(--fg) 22%, transparent);
border-radius: 6px;
margin: 4px 0;
box-sizing: border-box;
transition: height 0.16s ease;
}
.note-fullscreen-overlay .note-form-text-btn {
padding: 10px 16px;
font-size: 14px;
}
.note-fullscreen-overlay .note-form-text-btn svg {
width: 16px;
height: 16px;
}
/* + Add and the camera/photo button share a row inside checklists, so
they're both within thumb reach when editing items. The whole row
reads as a single dashed "tap to add" surface — tapping ANYWHERE
on it triggers + Add (the photo button stays its own target). */
.note-fullscreen-overlay .note-cl-add-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-top: 10px;
padding: 4px 8px 4px 4px;
border: 1px solid color-mix(in srgb, var(--fg) 22%, transparent);
border-radius: 10px;
cursor: pointer;
transition: background 0.12s ease, border-color 0.12s ease;
}
.note-fullscreen-overlay .note-cl-add-row:active {
background: color-mix(in srgb, var(--fg) 6%, transparent);
border-color: color-mix(in srgb, var(--fg) 36%, transparent);
}
.note-fullscreen-overlay .note-cl-add-row .note-cl-add {
flex: 1;
justify-content: flex-start;
background: transparent;
border: none;
}
.note-fullscreen-overlay .note-cl-add-row .note-form-photo-btn {
display: inline-flex;
align-items: center;
gap: 4px;
width: auto;
height: 36px;
padding: 0 12px 0 10px;
border-radius: 8px;
/* No border / no background — the surrounding +Add row provides the
bordered container, the camera is just an icon inside it. */
border: none;
background: transparent;
}
/* Read-mode overlay shown over the textarea for plain notes. Renders
linkified content so URLs are tappable. Click anywhere off a link
to flip to edit mode (focuses the underlying textarea). */
.note-form-content-reader {
font-size: 15px;
line-height: 1.55;
padding: 10px 6px;
white-space: pre-wrap;
word-wrap: break-word;
color: var(--fg);
cursor: text;
min-height: 40px;
}
/* Per-row read mode for todo items — replaces the bare <input> with a
clickable linkified span when the value contains a URL. */
.note-fullscreen-overlay .note-cl-text-reader {
flex: 1;
padding: 7px 6px;
font-size: 14px;
line-height: 1.4;
min-height: 32px;
display: inline-flex;
align-items: center;
word-break: break-word;
color: var(--fg);
cursor: text;
}
.note-fullscreen-overlay .note-cl-row.done .note-cl-text-reader {
opacity: 0.5;
text-decoration: line-through;
}
.note-fullscreen-overlay .note-form-icon-btn svg {
width: 16px;
height: 16px;
}
/* Tags input is JS-relocated into the bottom actions row, pinned
left of Cancel/Update. Style it as a dashed pill so it reads as
metadata rather than another control. */
.note-fullscreen-overlay .note-form-actions-group .note-form-label {
flex: 1 1 auto;
min-width: 0;
font-size: 13px;
padding: 8px 10px;
margin: 0 auto 0 0;
background: transparent;
border: 1px dashed color-mix(in srgb, var(--fg) 16%, transparent);
border-radius: 6px;
text-align: left;
}
.note-fullscreen-overlay .note-color-picker { order: 1; }
.note-fullscreen-overlay .note-form-type-seg {
order: 3;
margin-left: auto; /* shove right */
/* The overlay enlarges the pills (padding 10px), but the base seg is only
28px tall with overflow:hidden — which clipped them. Grow the track so
the bigger touch targets fit. */
height: 40px;
border-radius: 12px;
}
.note-fullscreen-overlay .note-form-actions-group {
order: 4;
width: 100%;
display: flex;
align-items: center;
gap: 8px;
margin-top: 6px;
}
.notes-mobile-grabber { display: none; }
/* Notes panel: mounted like a window, docked to the right on desktop. */
.notes-pane-backdrop {
position: fixed;
inset: 0;
z-index: 200;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
backdrop-filter: none;
-webkit-backdrop-filter: none;
pointer-events: none;
animation: notes-backdrop-fade-in 180ms ease both;
}
.notes-pane-backdrop .notes-pane {
pointer-events: auto;
}
.notes-pane-backdrop:has(.notes-pane.modal-right-docked),
.notes-pane-backdrop:has(.notes-pane.modal-left-docked) {
background: transparent;
backdrop-filter: none;
-webkit-backdrop-filter: none;
pointer-events: none;
}
.notes-pane-backdrop:has(.notes-pane.modal-right-docked) .notes-pane,
.notes-pane-backdrop:has(.notes-pane.modal-left-docked) .notes-pane {
pointer-events: auto;
}
.notes-pane-backdrop:has(.notes-pane.notes-window-fullscreen) {
background: transparent;
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
.notes-pane {
display: flex;
flex-direction: column;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
overflow: hidden;
width: min(880px, 92vw);
height: min(80vh, 820px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45);
box-sizing: border-box;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
letter-spacing: -0.015em;
/* Smooth open: scale up + fade in from the centre. */
animation: notes-pane-enter 200ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
transform-origin: center center;
will-change: transform, opacity;
}
.notes-pane.modal-right-docked {
border-left: 1px solid var(--border);
border-radius: 0;
box-shadow: -4px 0 14px rgba(0, 0, 0, 0.18);
transition: none !important;
}
.notes-pane.modal-left-docked {
border-right: 1px solid var(--border);
border-radius: 0;
box-shadow: 4px 0 14px rgba(0, 0, 0, 0.18);
transition: none !important;
}
.notes-pane-header {
display: flex;
align-items: center;
gap: 4px;
justify-content: space-between;
padding: 0 0 6px;
margin: 10px 10px 0;
border-bottom: 1px solid var(--border);
background: inherit;
flex-shrink: 0;
flex-wrap: nowrap;
min-height: 30px;
overflow: hidden;
}
/* Archive mode — sepia tint over the panel + a header strip so it's clearly
a different view. Active reminders sort + glow rules don't apply here. */
.notes-pane-archive {
background: color-mix(in srgb, #b48a4a 6%, var(--bg));
}
.notes-pane-archive .notes-pane-header {
background: color-mix(in srgb, #b48a4a 12%, var(--bg));
border-bottom-color: color-mix(in srgb, #b48a4a 35%, var(--border));
}
.notes-pane-archive .notes-pane-body {
background: color-mix(in srgb, #b48a4a 4%, var(--bg));
}
/* Smooth the archive↔active swap so the colour tint eases in/out instead
of snapping. Covers the pane, its header, and its body. */
.notes-pane,
.notes-pane .notes-pane-header,
.notes-pane .notes-pane-body {
transition: background 0.28s ease, border-color 0.28s ease;
}
.notes-pane-archive .notes-pane-title::after {
content: 'Archive';
margin-left: 8px;
padding: 2px 8px 2px 22px;
font-size: 9px;
font-weight: 700;
letter-spacing: 1px;
text-transform: uppercase;
color: #b48a4a;
background-color: color-mix(in srgb, #b48a4a 18%, transparent);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b48a4a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='3' width='20' height='5' rx='1'/%3E%3Cpath d='M4 8v11a2 2 0 002 2h12a2 2 0 002-2V8'/%3E%3Cpath d='M10 12h4'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: 6px center;
background-size: 11px 11px;
border: 1px solid color-mix(in srgb, #b48a4a 45%, transparent);
border-radius: 10px;
vertical-align: middle;
}
.notes-pane-archive .note-card {
background: color-mix(in srgb, #b48a4a 8%, var(--panel));
border-color: color-mix(in srgb, #b48a4a 25%, var(--border));
}
.notes-pane-header .doc-action-icon-btn {
flex-shrink: 0;
}
.notes-pane-header .notes-pane-title {
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
flex-shrink: 1;
}
.notes-pane-footer {
padding: 6px 8px;
border-top: 1px solid var(--border);
background: var(--panel);
flex-shrink: 0;
display: flex;
gap: 6px;
}
.notes-new-btn {
background: none;
border: 1px dashed color-mix(in srgb, var(--accent) 40%, transparent);
color: color-mix(in srgb, var(--fg) 50%, transparent);
border-radius: 6px;
padding: 6px 0;
flex: 1;
cursor: pointer;
font-size: 11px;
font-family: inherit;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
transition: border-color 0.15s, color 0.15s;
}
.notes-new-btn:hover {
border-color: var(--accent);
color: var(--fg);
}
.notes-pane-title {
/* Match the other tool headers (.modal-header h4). */
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 1rem;
font-weight: 600;
letter-spacing: -0.03em;
color: var(--red);
white-space: nowrap;
height: 24px;
line-height: 22px;
padding: 0 6px;
margin: 6px 0 0 0;
display: inline-block;
vertical-align: middle;
/* Optical nudge — sits 1px above its row baseline on desktop, more on mobile
where the surrounding controls are taller. Overridden in the mobile
media block below. */
position: relative;
top: -1px;
box-sizing: border-box;
}
@media (max-width: 768px) {
.notes-pane-title { top: -4px; }
.notes-pane-title svg {
position: relative;
top: -2px;
}
/* Mobile header is tight — keep Archive / Toggle View icon-only. */
.notes-header-btn-label { display: none; }
.notes-header-text-btn { width: 32px !important; padding: 0 !important; }
}
.notes-header-btn-label {
font-size: 11px;
font-weight: 500;
line-height: 1;
letter-spacing: 0.2px;
white-space: nowrap;
}
.notes-header-text-btn {
width: auto !important;
padding: 0 8px !important;
}
.notes-pane-body {
flex: 1;
overflow-y: auto;
scroll-behavior: smooth;
padding: 10px 8px 8px;
display: flex;
flex-direction: column;
gap: 6px;
scrollbar-width: thin;
}
.notes-pane-body > .doclib-chip-scroll-frame:has(> .notes-labels-bar) {
/* The shared Library wrapper grows to fill available flex space. Notes
needs this to remain a content-height toolbar or it can create a huge
blank gap above the cards after filtering/re-rendering. */
flex: 0 0 auto;
width: 100%;
}
.note-card.note-card-sliding-out {
transform: translateX(120%);
opacity: 0;
transition: transform 0.32s ease-in, opacity 0.32s ease-in;
pointer-events: none;
}
/* ⋯ corner menu button (replaced the copy corner button) */
.note-card-corner-menu {
background: none;
border: none;
color: var(--fg);
cursor: pointer;
padding: 2px;
opacity: 0.5;
transition: opacity 0.15s;
display: inline-flex;
}
.note-card-corner-menu:hover { opacity: 0.9; }
/* Dropdown spawned by the ⋯ menu */
.note-corner-menu-dropdown {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0,0,0,0.3);
padding: 4px;
min-width: 168px;
font-size: 12px;
animation: ncm-pop 0.12s ease-out both;
}
@keyframes ncm-pop { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }
.note-corner-menu-dropdown .ncm-item {
display: flex;
align-items: center;
gap: 9px;
width: 100%;
background: none;
border: none;
color: var(--fg);
font-family: inherit;
font-size: 12px;
text-align: left;
padding: 8px 10px;
border-radius: 6px;
cursor: pointer;
}
.note-corner-menu-dropdown .ncm-item:hover {
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.note-card {
/* Same tint that .doclib-card uses so a default (uncolored) note
visually separates from the panel background it sits on. */
background: color-mix(in srgb, var(--fg) 3%, transparent);
border: 1px solid var(--border);
border-radius: 8px;
/* Extra right padding so the corner buttons (pin / edit / copy / done)
never collide with the title, and a min-height so an empty/short
note is still tall enough to fit that row of corner controls. */
padding: 10px 14px;
padding-right: 30px;
min-height: 64px;
cursor: default;
transition: background 0.15s;
display: flex;
flex-direction: column;
gap: 4px;
/* Keep drawings/images clipped to the card so they can't spill outside it —
but allow a small overhang (the pin bubble sits at right:-8px and would
otherwise be cropped). overflow-clip-margin extends the clip-box without
letting full images bleed past. */
overflow: clip;
overflow-clip-margin: 14px;
/* Don't shrink inside the flex-column list pane — otherwise cards squish to
fit (clipped/too short) and the container never overflows, so it won't
scroll. Keeping natural height makes the list overflow → scrollable. */
flex-shrink: 0;
}
.note-card:hover:not([class*="note-color-"]) {
background: color-mix(in srgb, var(--fg) 6%, transparent);
}
.note-card { position: relative; }
.note-card-image { max-width: 100%; }
/* Mobile: bigger, more legible note cards + a sticky quick-add bar so you can
add a to-do without scrolling back up; and keep media inside the card. */
@media (max-width: 768px) {
.note-card { padding: 14px 16px; padding-right: 34px; min-height: 84px; font-size: 15px; }
.note-card-image { max-height: 260px; max-width: 100%; object-fit: cover; }
/* Sticky create-todo bar — pin it to the top of the scrolling list pane.
Safe now that the cards no longer shrink (the list actually overflows). */
.notes-quick-add {
position: sticky;
top: -8px;
z-index: 12;
flex-shrink: 0;
margin-top: 0;
box-shadow: 0 -8px 0 8px var(--panel), 0 8px 0 0 var(--panel);
}
}
.note-card-pinned {
border-top: 2px solid color-mix(in srgb, var(--fg) 30%, transparent);
}
.note-card.note-card-reminder-fired {
animation: note-reminder-pulse 1.2s ease-in-out 2;
}
/* Sticky outside glow — applied to the exact note whose reminder fired.
It stays until the user interacts with that card. */
.note-card.note-card-reminder-fired-sticky {
position: relative;
outline: 1px solid color-mix(in srgb, var(--accent, var(--red)) 36%, transparent);
outline-offset: 2px;
animation: note-reminder-glow 1.6s ease-in-out infinite;
}
/* Hover-only buttons (Google Keep style) */
.note-card-select,
.note-card-pin {
position: absolute;
top: -8px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 50%;
width: 18px;
height: 18px;
padding: 0;
cursor: pointer;
display: none;
align-items: center;
justify-content: center;
color: var(--fg);
opacity: 0.7;
z-index: 2;
transition: opacity 0.15s, transform 0.15s, background 0.15s;
}
/* Pencil + checkmark glyphs both have empty space along the top of their
24×24 viewBox, so flex-centering leaves them sitting visually high.
Nudge the inner SVG down a touch to compensate. */
.note-card-edit-corner svg,
.note-card-done svg {
position: relative;
top: 0.5px;
}
.note-card-edit-corner {
position: absolute;
/* Both Edit + Done at top-right: Edit on the left (right:40px),
Done on the right (right:6px). */
top: 6px; right: 40px;
width: 28px; height: 28px;
/* Fully invisible at rest. Materializes (with a solid panel fill blocking
any text underneath) only when the card is hovered or the button is
focused. Same rule on touch — opacity stays 0 until the card is tapped
to expose actions. */
background: transparent;
border: 1px solid transparent;
border-radius: 50%;
cursor: pointer; padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--fg); opacity: 0;
touch-action: manipulation;
transition: opacity 0.15s, background 0.15s, border-color 0.15s;
z-index: 2;
}
.note-card:hover .note-card-edit-corner {
opacity: 0.85;
background: var(--panel);
border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
.note-card-edit-corner:hover,
.note-card-edit-corner:focus-visible {
opacity: 1 !important;
background: color-mix(in srgb, var(--accent) 18%, var(--panel)) !important;
border-color: color-mix(in srgb, var(--accent) 50%, var(--border)) !important;
}
@media (hover: none) {
.note-card-edit-corner {
opacity: 0.7;
background: var(--panel);
border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
}
/* Copy corner — bottom-right counterpart to the edit/done corner buttons.
Round, subtle, hover-reveal on desktop and always faintly visible on
touch so mobile users can tap it without needing hover. */
.note-card-copy-corner {
position: absolute;
bottom: 6px;
right: 6px;
top: auto;
width: 26px;
height: 26px;
background: transparent;
border: 1px solid transparent;
border-radius: 50%;
cursor: pointer;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--fg);
opacity: 0;
touch-action: manipulation;
transition: opacity 0.15s, background 0.15s, border-color 0.15s;
z-index: 2;
}
.note-card:hover .note-card-copy-corner {
opacity: 0.7;
background: var(--panel);
border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
.note-card-copy-corner:hover,
.note-card-copy-corner:focus-visible {
opacity: 1 !important;
background: color-mix(in srgb, var(--accent, var(--red)) 18%, var(--panel)) !important;
border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, var(--border)) !important;
}
@media (hover: none) {
.note-card-copy-corner {
opacity: 0.6;
background: var(--panel);
border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
}
/* Unarchive corner reuses the pencil's spot in archive view. */
.note-card-unarchive-corner:hover,
.note-card-unarchive-corner:focus-visible {
background: color-mix(in srgb, var(--accent) 22%, transparent) !important;
border-color: color-mix(in srgb, var(--accent) 50%, var(--border)) !important;
color: var(--accent);
}
/* "Done" pill in the bottom-right of every active note card. Visible-only on
hover (and always on touch) so it's clearly readable but doesn't clutter
the grid at rest. Distinct from the edit pencil so it's obvious what it
does — green checkmark + label, not just an icon. */
/* Matches .note-card-edit-corner footprint (22×22 circle), just anchored
to the bottom-right corner instead of top-right. */
.note-card-done {
position: absolute;
top: 6px;
right: 6px;
width: 28px;
height: 28px;
background: transparent;
border: 1px solid transparent;
border-radius: 50%;
cursor: pointer;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--green, #98c379);
opacity: 0;
touch-action: manipulation;
transition: opacity 0.15s, background 0.15s, border-color 0.15s;
z-index: 2;
}
.note-card:hover .note-card-done {
opacity: 0.85;
background: var(--panel);
border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
.note-card-done:hover,
.note-card-done:focus-visible {
opacity: 1 !important;
background: color-mix(in srgb, var(--green, #98c379) 22%, var(--panel)) !important;
border-color: color-mix(in srgb, var(--green, #98c379) 50%, var(--border)) !important;
}
@media (hover: none) {
.note-card-done {
opacity: 0.7;
background: var(--panel);
border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
}
/* Copy corner — bottom-right of the card. Same opaque-fill pattern as
trash/unarchive so the underlying text/image never bleeds through. */
.note-card-corner-copy {
position: absolute;
bottom: 6px;
right: 6px;
width: 28px;
height: 28px;
background: var(--panel);
border: 1px solid color-mix(in srgb, var(--fg) 18%, transparent);
border-radius: 50%;
cursor: pointer;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--fg);
visibility: hidden;
touch-action: manipulation;
transition: background 0.15s, border-color 0.15s, color 0.15s;
z-index: 2;
}
.note-card-corner-copy svg { position: relative; top: 1px; opacity: 0.85; transition: opacity 0.15s; }
.note-card-corner-copy:hover,
.note-card-corner-copy:focus-visible {
background: color-mix(in srgb, var(--accent) 18%, var(--panel));
border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
color: var(--accent);
}
.note-card-corner-copy:hover svg { opacity: 1; }
@media (hover: none) {
.note-card-corner-copy { visibility: visible; }
.note-card-corner-copy svg { opacity: 0.7; }
}
/* ── Archive view corners — trash (top-left) + unarchive (top-right) ── */
/* Background fill stays fully opaque (var(--panel)) so the note text or
image behind doesn't bleed through. Visibility is gated by `visibility`
+ a faded ICON instead of `opacity` on the whole button. */
.note-card-corner-trash,
.note-card-corner-unarchive {
position: absolute;
top: 6px;
width: 28px;
height: 28px;
background: var(--panel);
border: 1px solid color-mix(in srgb, var(--fg) 18%, transparent);
border-radius: 50%;
cursor: pointer;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--fg);
visibility: hidden;
touch-action: manipulation;
transition: background 0.15s, border-color 0.15s, color 0.15s;
z-index: 2;
}
.note-card-corner-trash svg,
.note-card-corner-unarchive svg { opacity: 0.85; transition: opacity 0.15s; }
.note-card-corner-trash:hover,
.note-card-corner-trash:focus-visible {
background: color-mix(in srgb, var(--red) 18%, var(--panel));
border-color: color-mix(in srgb, var(--red) 50%, var(--border));
color: var(--red);
}
.note-card-corner-trash:hover svg,
.note-card-corner-unarchive:hover svg { opacity: 1; }
.note-card-corner-unarchive:hover,
.note-card-corner-unarchive:focus-visible {
background: color-mix(in srgb, var(--accent) 18%, var(--panel));
border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
color: var(--accent);
}
@media (hover: none) {
.note-card-corner-trash,
.note-card-corner-unarchive { visibility: visible; }
.note-card-corner-trash svg,
.note-card-corner-unarchive svg { opacity: 0.7; }
}
/* ─── Auto-AI ✨ chip on note cards ─────────────────────────────────── */
.note-card-ai-chip {
display: inline-flex;
align-items: center;
gap: 4px;
margin-left: 6px;
padding: 2px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 500;
font-family: inherit;
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, transparent);
color: var(--accent, var(--red));
cursor: pointer;
transition: background 0.12s, transform 0.12s, box-shadow 0.12s;
vertical-align: middle;
}
.note-card-ai-chip:hover {
background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
transform: translateY(-1px);
box-shadow: 0 2px 6px color-mix(in srgb, var(--accent, var(--red)) 30%, transparent);
}
.note-card-ai-chip svg {
flex-shrink: 0;
animation: note-ai-shine 2.4s ease-in-out infinite;
}
.note-card-ai-solving {
cursor: default;
background: color-mix(in srgb, var(--fg) 8%, transparent);
border-color: color-mix(in srgb, var(--fg) 18%, transparent);
color: color-mix(in srgb, var(--fg) 70%, transparent);
}
.note-card-ai-solving svg { animation: spin 0.9s linear infinite; filter: none; }
.note-card-ai-done { background: color-mix(in srgb, var(--accent, var(--red)) 8%, transparent); }
.note-card-ai-done svg { animation: none; }
#notes-auto-ai-toggle.active { color: var(--accent, var(--red)); }
/* ─── AI improve button (note form header) ───────────────────────────── */
.note-form-improve-btn {
width: 38px !important;
height: 38px !important;
color: var(--accent, var(--red));
}
.note-form-improve-btn svg {
width: 22px !important;
height: 22px !important;
animation: note-ai-shine 2.4s ease-in-out infinite;
}
.note-form-improve-btn.busy svg {
animation: spin 0.9s linear infinite;
filter: none;
}
.note-card:hover .note-card-select,
.note-card:hover .note-card-pin,
.note-card-selected .note-card-select,
.note-card-pinned .note-card-pin {
display: flex;
}
.note-card-select:hover, .note-card-pin:hover {
opacity: 1;
transform: scale(1.15);
background: color-mix(in srgb, var(--accent) 15%, var(--bg));
}
.note-card-pin.active {
display: flex;
background: color-mix(in srgb, var(--accent, var(--red)) 12%, var(--bg));
border-color: color-mix(in srgb, var(--accent, var(--red)) 45%, var(--border));
color: var(--accent, var(--red));
opacity: 1;
}
.note-card-selected .note-card-select {
background: var(--accent);
border-color: var(--accent);
color: #fff;
opacity: 1;
}
/* Select-mode checkbox on cards (mirrors docs library) */
.note-card-cb {
position: absolute;
top: 6px;
left: 6px;
z-index: 3;
margin: 0;
}
.note-card-selectmode {
padding-left: 28px;
cursor: pointer;
}
.note-card-selectmode:hover { background: color-mix(in srgb, var(--fg) 3%, transparent) !important; }
@media (max-width: 768px) {
body.notes-mobile-mode .note-card-selectmode {
animation: notes-select-jiggle 0.34s cubic-bezier(0.42, 0, 0.58, 1) infinite;
transform-origin: 50% 50%;
}
body.notes-mobile-mode .note-card-selectmode:nth-child(2n) {
animation-delay: -0.08s;
}
/* Keep the subtle edit-mode cue only on Notes and Gallery. */
:is(
body.gallery-selecting .gallery-card
) {
animation: notes-select-jiggle 0.34s cubic-bezier(0.42, 0, 0.58, 1) infinite;
transform-origin: 50% 50%;
}
:is(
body.gallery-selecting .gallery-card:nth-child(2n)
) { animation-delay: -0.11s; }
}
@media (prefers-reduced-motion: reduce) {
body.notes-mobile-mode .note-card-selectmode,
body.gallery-selecting .gallery-card { animation: none; }
}
/* Bottom action row — hidden everywhere. The archive/delete actions and the
color picker now live inside the edit form (click the pencil corner). */
.note-card-actions { display: none; }
.note-agent-menu {
background: var(--panel, var(--bg));
border: 1px solid var(--border);
border-radius: 6px;
box-shadow: 0 4px 16px rgba(0,0,0,0.3);
padding: 4px 0;
min-width: 140px;
font-size: 12px;
}
.note-card-colors {
display: flex;
gap: 3px;
}
/* Re-asserted AFTER the base rule above so the mobile-hide actually
wins the cascade — earlier @media (hover:none) block was being
overridden because the unconditional display:flex rule came later. */
@media (hover: none) {
.note-card-colors { display: none; }
}
.note-card-color-dot {
width: 12px;
height: 12px;
border-radius: 50%;
cursor: pointer;
border: 1.5px solid color-mix(in srgb, var(--fg) 20%, transparent);
transition: transform 0.15s, border-color 0.15s;
}
.note-card-color-dot:hover {
transform: scale(1.25);
border-color: var(--fg);
}
.note-card-color-dot.active {
border-color: var(--fg);
border-width: 2px;
}
.note-card-action {
background: none;
border: none;
cursor: pointer;
padding: 3px;
color: var(--fg);
opacity: 0.4;
display: flex;
align-items: center;
border-radius: 3px;
transition: opacity 0.15s, background 0.15s;
}
/* Note image */
.note-card-image {
width: 100%;
max-height: 180px;
object-fit: cover;
border-radius: 4px;
margin: 4px 0;
display: block;
}
.note-form-image-wrap {
position: relative;
margin: 4px 0;
}
.note-form-image {
width: 100%;
max-height: 200px;
object-fit: cover;
border-radius: 4px;
display: block;
/* Images are draggable=true by default; that catches the mousedown
and beats the X button to the click. Disabling drag at the CSS
level (the -webkit-user-drag prop) keeps the X button reliably
clickable even if the JS forgets to set draggable="false" on the
element. Unprefixed user-drag was never standardized — Firefox
drops it with a warning. */
-webkit-user-drag: none;
user-select: none;
}
.note-form-image-rm {
position: absolute;
top: 6px;
right: 6px;
z-index: 5;
background: rgba(0,0,0,0.65);
border: none;
color: #fff;
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
}
.note-form-photo-btn {
flex: 0 0 auto;
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
cursor: pointer;
border-radius: 4px;
width: auto;
min-width: 32px;
height: 32px;
padding: 0 6px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 3px;
opacity: 1;
transition: opacity 0.15s, border-color 0.15s, background 0.15s;
}
.note-form-photo-btn:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
/* Quick-add bar (always at top) */
.notes-quick-add {
display: flex;
align-items: center;
transform: translateY(-2px);
gap: 4px;
background: var(--panel);
border: 1px solid color-mix(in srgb, var(--accent, var(--red)) 35%, var(--border));
border-radius: 6px;
/* Toggle pill sits at the very left of the bar — no inset padding. The
pill's own border provides visual breathing room from the wrapper edge. */
padding: 4px 6px 4px 4px;
margin-top: 2px;
margin-bottom: 8px;
transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
/* Subtle idle glow so the field reads as "you can type here" without
the user having to click it first. Fades out the moment the user
hovers or focuses it. */
animation: notes-quick-pulse 2.8s ease-in-out infinite;
}
.notes-quick-add:hover {
border-color: color-mix(in srgb, var(--accent, var(--red)) 55%, var(--border));
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
animation: none;
}
.notes-quick-add:focus-within {
border-color: var(--accent, var(--red));
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 25%, transparent);
background: color-mix(in srgb, var(--fg) 3%, var(--panel));
animation: none;
}
.notes-quick-input {
flex: 1;
background: transparent;
border: none;
outline: none;
color: var(--fg);
caret-color: var(--accent, var(--red));
font-family: inherit;
font-size: 12px;
padding: 6px 0;
}
.notes-quick-icon {
background: none;
border: none;
color: var(--fg);
opacity: 0.45;
cursor: pointer;
padding: 5px;
border-radius: 4px;
display: flex;
align-items: center;
transition: opacity 0.15s, background 0.15s;
}
.notes-quick-icon:hover {
opacity: 1;
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
/* 2-pill Note/Todo toggle in the quick-add bar — same look as the form's
type-seg but slimmer to fit the compact row. */
.notes-quick-type-seg {
display: inline-flex;
height: 28px;
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
position: relative;
flex-shrink: 0;
/* Negative order pushes the toggle BEFORE the wrapper's ::before caret
hint, so the blinking | sits between the toggle and the input rather
than at the far-left edge. */
order: -1;
}
.notes-quick-type-seg::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 50%;
height: 100%;
background: color-mix(in srgb, var(--fg) 10%, transparent);
border-radius: 6px;
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
z-index: 0;
}
.notes-quick-type-pill {
background: none;
border: none;
color: color-mix(in srgb, var(--fg) 40%, transparent);
cursor: pointer;
padding: 0 11px;
font-family: inherit;
transition: color 0.2s;
white-space: nowrap;
height: 100%;
display: inline-flex;
align-items: center;
position: relative;
z-index: 1;
}
.notes-quick-type-pill svg {
width: 16px;
height: 16px;
}
.notes-quick-type-pill:not(.active):hover {
color: color-mix(in srgb, var(--fg) 65%, transparent);
}
.notes-quick-type-pill.active,
.notes-quick-type-pill.active:hover,
.notes-quick-type-pill.active:focus {
color: var(--fg);
cursor: default;
}
/* Mobile: thumb-sized Note/Todo toggle. The 22px desktop pill is too
small to hit reliably on a phone. */
@media (max-width: 768px) {
.notes-quick-type-seg {
height: 36px;
border-radius: 10px;
}
.notes-quick-type-seg::before {
border-radius: 9px;
}
.notes-quick-type-pill {
padding: 0 14px;
}
.notes-quick-type-pill svg {
width: 17px;
height: 17px;
}
/* Match the input + photo button heights to the bigger toggle so the
row reads as one consistent bar. */
.notes-quick-input {
height: 36px;
font-size: 15px;
}
.notes-quick-icon {
padding: 9px;
}
.notes-quick-icon svg {
width: 18px;
height: 18px;
}
}
.notes-empty-msg {
text-align: center;
/* 0.4 dropped this empty-state text to ~2.8:1; 0.65 keeps it readable
(WCAG AA) while staying visibly secondary. */
opacity: 0.65;
padding: 30px 20px;
font-size: 11px;
}
/* Loading skeleton */
.notes-skeleton {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 4px;
}
.notes-skeleton-card {
height: 72px;
border-radius: 6px;
border: 1px solid var(--border);
background: linear-gradient(
90deg,
color-mix(in srgb, var(--fg) 4%, transparent) 0%,
color-mix(in srgb, var(--fg) 10%, transparent) 50%,
color-mix(in srgb, var(--fg) 4%, transparent) 100%
);
background-size: 200% 100%;
animation: notes-skeleton-shimmer 1.4s ease-in-out infinite;
}
/* Type toggle (note vs todo) */
.note-form-type-row {
display: flex;
gap: 4px;
margin-top: -2px;
}
/* Reminder bell button */
/* Mobile-only: bell icon in the note editor is accent-coloured so it pops as
the primary "set a reminder" affordance. The Archive button is hidden — the
Update (✓) button morphs into an Archive action when the user opens a note
and clicks without making any edits (see notes.js `archive-mode` toggle). */
@media (max-width: 768px) {
.note-form-remind-btn { color: var(--accent, var(--red)) !important; }
.note-form-remind-btn > svg { color: var(--accent, var(--red)); }
.note-form-archive-btn { display: none !important; }
.notes-pane #notes-archive-toggle { display: none !important; }
.note-form-save.archive-mode {
color: var(--accent, var(--red)) !important;
border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, transparent) !important;
background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent) !important;
}
}
.note-form-remind-btn {
flex: 0 0 auto;
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
border-radius: 4px;
width: 32px;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 1;
position: relative;
top: -2px;
transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
}
.note-form-remind-btn:hover { opacity: 1; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.note-form-remind-btn.has-date { color: var(--accent); border-color: var(--accent); }
/* Calendar event form + notes form: jingle the little bell when the user
picks a reminder. Anchored near the top of the bell so the swing reads
as the bell clanging side-to-side. */
.cal-remind-bell,
.note-form-remind-btn > svg {
transform-origin: 50% 4px;
transition: transform 0.1s ease;
}
.cal-remind-bell.jingling,
.note-form-remind-btn > svg.jingling {
animation: cal-bell-jingle 0.65s cubic-bezier(0.36, 0, 0.66, -0.56) both;
}
@media (prefers-reduced-motion: reduce) {
.cal-remind-bell.jingling,
.note-form-remind-btn > svg.jingling { animation: none; }
}
/* Reminder tag inside form */
.note-form-reminder-tags {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.note-reminder-tag {
display: inline-flex;
align-items: center;
gap: 6px;
background: color-mix(in srgb, var(--accent) 14%, transparent);
color: var(--accent);
border: none;
border-radius: 12px;
padding: 4px 4px 4px 9px;
font-size: 10px;
font-family: inherit;
cursor: pointer;
font-weight: 500;
}
.note-reminder-tag:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.note-reminder-tag-x {
display: inline-flex;
align-items: center;
justify-content: center;
width: 14px;
height: 14px;
border-radius: 50%;
background: color-mix(in srgb, var(--accent) 25%, transparent);
font-size: 12px;
line-height: 1;
position: relative;
top: -2px;
}
.note-reminder-tag-x:hover { background: var(--red); color: #fff; }
/* Reminder tag on card */
.note-card-reminder {
display: inline-flex;
align-items: center;
gap: 4px;
background: color-mix(in srgb, var(--accent) 12%, transparent);
color: var(--accent);
font-size: 9px;
font-weight: 500;
padding: 2px 7px;
border-radius: 10px;
align-self: flex-start;
margin-top: 2px;
}
.note-card-reminder.overdue {
background: color-mix(in srgb, var(--red) 18%, transparent);
color: var(--red);
}
/* Reminder dropdown menu */
.note-reminder-menu {
position: fixed;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 6px 24px rgba(0,0,0,0.25);
/* Above the fullscreen note overlay (z-index: 10500) — otherwise the
reminder picker opens hidden behind it on mobile and reads as
"broken". */
z-index: 12000;
min-width: 220px;
padding: 6px 0;
font-family: inherit;
font-size: 12px;
}
.note-reminder-menu-title {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
opacity: 0.5;
padding: 6px 14px 4px;
letter-spacing: 0.5px;
}
.note-reminder-menu-item {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
background: none;
border: none;
color: var(--fg);
font-family: inherit;
font-size: 12px;
padding: 7px 14px;
cursor: pointer;
text-align: left;
transition: background 0.1s;
}
.note-reminder-menu-item:hover { background: color-mix(in srgb, var(--fg) 6%, transparent); }
.note-reminder-menu-item.active { color: var(--accent); }
.note-reminder-menu-sub {
font-size: 10px;
opacity: 0.5;
margin-left: 12px;
}
.note-reminder-menu-check { color: var(--accent); font-size: 12px; }
.note-reminder-menu-divider {
height: 1px;
background: var(--border);
margin: 4px 0;
}
.note-reminder-menu-picker {
padding: 6px 14px 8px;
}
.note-reminder-date-input {
width: 100%;
background: color-mix(in srgb, var(--fg) 5%, transparent);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg);
font-family: inherit;
font-size: 12px;
padding: 6px 8px;
outline: none;
color-scheme: dark light;
box-sizing: border-box;
}
.note-reminder-date-input:focus { border-color: var(--accent); }
.note-reminder-menu-confirm {
font-weight: 600;
color: var(--accent);
justify-content: center;
}
.note-reminder-menu-confirm.disabled,
.note-reminder-menu-confirm[disabled] {
opacity: 0.4;
cursor: not-allowed;
color: var(--fg);
}
.note-reminder-menu-arrow {
opacity: 0.5;
margin-left: 8px;
font-size: 14px;
line-height: 1;
}
.note-reminder-menu-back {
display: flex;
align-items: center;
gap: 4px;
width: 100%;
background: none;
border: none;
color: var(--fg);
font-family: inherit;
font-size: 11px;
opacity: 0.7;
padding: 6px 12px;
cursor: pointer;
text-align: left;
transition: opacity 0.1s, background 0.1s;
}
.note-reminder-menu-back:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 6%, transparent); }
.note-reminder-menu-arrow-back {
font-size: 14px;
line-height: 1;
}
.note-reminder-menu-sublabel {
font-size: 10px;
opacity: 0.5;
padding: 6px 14px 2px;
letter-spacing: 0.3px;
}
.note-reminder-weekday-row {
display: flex;
gap: 4px;
padding: 4px 12px 8px;
flex-wrap: wrap;
}
.note-reminder-day-chip {
flex: 1 1 0;
min-width: 24px;
height: 28px;
background: color-mix(in srgb, var(--fg) 5%, transparent);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--fg);
font-family: inherit;
font-size: 11px;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.1s, border-color 0.1s, color 0.1s;
padding: 0;
}
.note-reminder-day-chip:hover {
background: color-mix(in srgb, var(--accent) 10%, transparent);
border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
}
.note-reminder-day-chip.active {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
.note-form-type-btn,
.note-form-type-toggle {
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
cursor: pointer;
border-radius: 4px;
width: 32px;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 1;
transition: opacity 0.15s, background 0.15s, border-color 0.15s;
}
.note-form-type-toggle:hover { opacity: 1; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.note-form-type-toggle[data-type="todo"] { color: var(--accent); }
/* Note | Todo | Draw segmented toggle — mirrors .mode-toggle (agent/chat) */
.note-form-type-seg {
display: inline-flex;
height: 32px;
border: 1px solid var(--border);
border-radius: 10px;
overflow: hidden;
position: relative;
flex-shrink: 0;
}
.note-form-type-pill {
background: none;
border: none;
color: color-mix(in srgb, var(--fg) 40%, transparent);
cursor: pointer;
padding: 0 12px;
font-size: 13px;
font-weight: 500;
font-family: inherit;
transition: color 0.2s;
white-space: nowrap;
height: 100%;
display: inline-flex;
align-items: center;
gap: 4px;
position: relative;
z-index: 1;
}
.note-form-type-pill:not(.active):hover { color: color-mix(in srgb, var(--fg) 60%, transparent); }
.note-form-type-pill.active,
.note-form-type-pill.active:hover,
.note-form-type-pill.active:focus {
color: var(--fg);
cursor: default;
}
@media (max-width: 480px) {
.note-form-type-pill span { display: none; }
.note-form-type-pill { padding: 0 8px; }
}
.note-card-action:hover {
opacity: 1;
background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.note-card-delete:hover { color: var(--red); }
/* Tag/label */
.note-card-label {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 2px;
}
.note-card-label-chip {
border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
background: color-mix(in srgb, var(--accent) 10%, transparent);
color: var(--accent);
border-radius: 999px;
padding: 2px 6px;
font: inherit;
font-size: 9px;
font-weight: 600;
line-height: 1.2;
cursor: pointer;
}
.note-card-label-chip:hover {
background: color-mix(in srgb, var(--accent) 18%, transparent);
border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
}
/* Search bar */
.notes-search-bar {
padding: 6px 8px;
display: flex;
align-items: center;
gap: 6px;
}
.notes-select-trigger {
position: relative;
top: 3px;
flex-shrink: 0;
background: var(--bg);
color: var(--fg);
border: 1px solid var(--border);
border-radius: 6px;
font-family: inherit;
font-size: 11px;
height: 30px;
padding: 0 12px;
cursor: pointer;
opacity: 0.75;
transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
}
.notes-select-trigger:hover { opacity: 1; border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); }
.notes-select-trigger.active {
opacity: 1;
background: color-mix(in srgb, var(--accent) 18%, transparent);
color: var(--accent);
border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}
.notes-search-bar .memory-search-input {
flex: 1;
min-width: 0;
height: 30px;
min-height: 30px;
font-size: 11px;
padding: 0 10px 0 28px;
background-color: var(--bg);
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23e06c75' stroke-opacity='0.85' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><line x1='21' y1='21' x2='16.65' y2='16.65'/></svg>");
background-repeat: no-repeat;
background-position: 9px center;
border: 1px solid var(--border);
border-radius: 6px;
color: var(--fg);
font-family: inherit;
outline: none;
transition: border-color 0.15s, background-color 0.15s;
}
.notes-search-bar .memory-search-input:focus {
border-color: var(--red);
}
/* Grid view: true CSS Grid plus JS-computed row spans, so cards masonry-pack
vertically without the jumpy rebalancing of CSS columns. */
.notes-pane.notes-view-grid .notes-pane-body {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
grid-auto-rows: 4px;
grid-auto-flow: dense;
gap: 0 8px;
padding: 10px 8px 8px;
overflow-x: hidden;
align-items: start;
align-content: start;
}
.notes-pane.notes-view-grid .note-card {
margin: 0 0 8px;
min-width: 0;
max-width: 100%;
overflow-wrap: anywhere;
word-break: break-word;
display: flex;
flex-direction: column;
gap: 4px;
}
.notes-pane.notes-view-grid .note-form,
.notes-pane.notes-view-grid .notes-quick-add {
grid-column: 1 / -1;
margin-bottom: 0;
grid-row-end: span 16;
}
/* Edit form in grid view: span the full row and size naturally — same shape
as the new-note form. The earlier max-width: 380px + max-height: 220px
inner-scroll combo read as a tiny popup floating in a wider panel and
didn't actually fit the form's content. */
.notes-pane.notes-view-grid .note-form {
width: 100%;
max-width: none;
margin-left: 0;
margin-right: 0;
box-sizing: border-box;
}
/* Pinned section break: the first unpinned card jumps to column 1, leaving
any leftover cell in the pinned row empty. Reads as a visual divider
between pinned and unpinned without needing a separator element. */
.notes-pane.notes-view-grid .note-card-pinned + .note-card:not(.note-card-pinned) {
grid-column-start: 1;
}
/* Mobile: 2-lane masonry via the CSS-Grid row-span trick. Each card spans
N tiny rows (4px each) based on its measured height; the grid auto-places
cards into the two columns so left/right lanes flow independently — no
row alignment between them. JS in notes.js sets `grid-row-end: span N`
on every card after render. Pure CSS multi-column was tried but its
`column-span: all` interaction with sticky positioning broke clicks on
the quick-add bar (Firefox mobile). */
@media (max-width: 768px) {
.notes-pane.notes-view-grid .notes-pane-body {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: 4px;
grid-auto-flow: dense;
gap: 0 8px;
align-content: start;
}
.notes-pane.notes-view-grid .note-card {
margin: -8px 0 6px;
transform: translateY(-12px);
/* grid-row-end set inline by JS (_applyMasonry) */
}
/* Full-width quick-add + edit form. Quick-add stays sticky at top so
the user can always reach it; it's NOT inside the masonry pack. */
.notes-pane.notes-view-grid .notes-label-toggle,
.notes-pane.notes-view-grid .notes-labels-bar,
.notes-pane.notes-view-grid .doclib-chip-scroll-frame:has(> .notes-labels-bar),
.notes-pane.notes-view-grid .notes-quick-add,
.notes-pane.notes-view-grid .note-form {
grid-column: 1 / -1;
}
.notes-pane.notes-view-grid .notes-label-toggle { grid-row: span 8; }
.notes-pane.notes-view-grid .notes-labels-bar { grid-row: span 16; }
.notes-pane.notes-view-grid .doclib-chip-scroll-frame:has(> .notes-labels-bar) { grid-row: span 16; }
.notes-pane.notes-view-grid .notes-quick-add { grid-row: span 12; }
.notes-pane.notes-view-grid .note-form {
width: 100%;
max-width: none;
margin-left: 0;
margin-right: 0;
box-sizing: border-box;
grid-row: auto / span 64;
}
.notes-pane.notes-view-grid .note-form:has(.note-form-type-seg.is-draw) {
grid-row: auto / span 152;
}
.notes-pane.notes-view-grid .note-form.note-form-new {
transform: translateY(-28px);
}
.notes-pane.notes-view-grid .notes-labels-bar {
padding-top: 0;
margin-top: -6px;
margin-bottom: 0;
}
.notes-pane.notes-view-grid .notes-quick-add {
margin-top: -32px;
margin-bottom: 4px;
}
/* Pinned-section break: first unpinned card jumps to column 1. */
.notes-pane.notes-view-grid .note-card-pinned + .note-card:not(.note-card-pinned) {
grid-column-start: 1;
}
}
/* Label filter bar */
.notes-label-toggle {
align-self: flex-start;
margin: -6px 8px 2px;
top: 0;
}
.notes-search-bar .notes-label-toggle {
align-self: center;
flex: 0 0 auto;
height: 30px;
margin: 0;
top: 4px;
padding: 0 10px;
border-radius: 6px;
font-size: 11px;
white-space: nowrap;
}
.notes-pane-body .doclib-chip-scroll-arrow.right {
top: calc(50% - 6px) !important;
}
.doclib-chip-scroll-frame:has(> #tasks-filter-chips) .doclib-chip-scroll-arrow {
top: calc(50% - 9px) !important;
}
@media (max-width: 768px) {
#doclib-modal .doclib-lang-chips {
position: relative;
top: -2px;
}
#doclib-chats-tidy-btn,
#doclib-chats-select-btn,
#doclib-arc-select-btn,
#doclib-research-tidy-btn,
#doclib-research-select-btn,
#doclib-tidy-btn,
#doclib-select-btn {
height: 30px !important;
min-height: 30px !important;
box-sizing: border-box;
}
}
/* Keep filter chips consistent across Skills, Tasks, and the document
library. Documents use the shared compact chip treatment as the baseline. */
.skills-summary-chip,
.memory-cat-chip,
.doclib-chip {
height: 23px;
padding: 0 8px;
border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent);
border-radius: 6px;
background: color-mix(in srgb, var(--fg) 4%, transparent);
color: color-mix(in srgb, var(--fg) 70%, transparent);
font-size: 10px;
line-height: 1;
font-family: inherit;
text-transform: none;
transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.skills-summary-chip:hover,
.memory-cat-chip:hover,
.doclib-chip:hover {
background: color-mix(in srgb, var(--fg) 8%, transparent);
border-color: color-mix(in srgb, var(--fg) 18%, transparent);
color: color-mix(in srgb, var(--fg) 70%, transparent);
}
.skills-summary-chip.active,
.memory-cat-chip.active,
.doclib-chip.active {
background: color-mix(in srgb, var(--accent, var(--red)) 13%, transparent);
border-color: color-mix(in srgb, var(--accent, var(--red)) 28%, transparent);
color: var(--fg);
}
.notes-labels-bar {
display: flex;
flex-wrap: wrap;
gap: 4px;
padding: 0 8px 0;
margin-top: -6px;
margin-bottom: -2px;
}
.notes-label-chip {
background: transparent;
border: 1px solid var(--border);
color: color-mix(in srgb, var(--fg) 60%, transparent);
font-size: 10px;
padding: 3px 10px;
border-radius: 10px;
cursor: pointer;
font-family: inherit;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.notes-label-chip:hover {
color: var(--fg);
background: color-mix(in srgb, var(--fg) 6%, transparent);
border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
}
.notes-label-chip.active {
background: color-mix(in srgb, var(--accent) 18%, transparent);
color: var(--accent);
border-color: color-mix(in srgb, var(--accent) 60%, transparent);
font-weight: 600;
}
.notes-label-chip-reminders {
display: inline-flex;
align-items: center;
gap: 2px;
}
.notes-label-chip-count {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 14px;
height: 14px;
padding: 0 4px;
border-radius: 7px;
background: color-mix(in srgb, var(--fg) 12%, transparent);
color: inherit;
font-size: 9px;
font-weight: 600;
margin-left: 3px;
}
.notes-label-chip-reminders.active .notes-label-chip-count {
background: color-mix(in srgb, var(--accent) 30%, transparent);
color: var(--accent);
}
.notes-label-chip-reminders.active.negated {
background: color-mix(in srgb, var(--red, #e55) 14%, transparent);
color: var(--red, #e55);
border-color: color-mix(in srgb, var(--red, #e55) 50%, transparent);
}
.notes-label-chip-reminders.active.negated .notes-label-chip-count {
background: color-mix(in srgb, var(--red, #e55) 25%, transparent);
color: var(--red, #e55);
}
.notes-label-clear-past {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--red, #e55);
border-color: color-mix(in srgb, var(--red, #e55) 45%, var(--border));
}
.notes-label-clear-past:hover {
color: var(--red, #e55);
background: color-mix(in srgb, var(--red, #e55) 12%, transparent);
border-color: color-mix(in srgb, var(--red, #e55) 65%, var(--border));
}
/* Today + Goals filter chips — share styling, tint with a warm accent so
they read as "long-term work" alongside the cool reminder chip. */
.notes-label-chip-today,
.notes-label-chip-goals {
display: inline-flex;
align-items: center;
gap: 2px;
}
.notes-label-chip-today.active,
.notes-label-chip-goals.active {
background: color-mix(in srgb, var(--accent-warm) 22%, transparent);
color: var(--accent-warm);
border-color: color-mix(in srgb, var(--accent-warm) 60%, transparent);
}
.notes-label-chip-today.active .notes-label-chip-count,
.notes-label-chip-goals.active .notes-label-chip-count {
background: color-mix(in srgb, var(--accent-warm) 30%, transparent);
color: var(--accent-warm);
}
/* ── Goal cards ──────────────────────────────────────────────────────── */
/* A goal note is structurally a checklist (note_type='goal') but visually
marked with a small Goal pill in the corner + a slightly warmer accent
so users can spot it at a glance among regular todos. */
.note-card-goal {
border-left: 3px solid color-mix(in srgb, var(--accent-warm) 80%, transparent);
}
.note-goal-pill {
position: absolute;
top: 6px;
left: 8px;
display: inline-flex;
align-items: center;
gap: 3px;
padding: 2px 7px 2px 5px;
background: color-mix(in srgb, var(--accent-warm) 18%, transparent);
color: var(--accent-warm);
border-radius: 999px;
font-size: 9px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
line-height: 1;
z-index: 2;
}
/* Description blurb above the checklist on goal cards */
.note-goal-desc {
font-size: 12px;
opacity: 0.7;
margin: 4px 0 6px;
white-space: pre-wrap;
line-height: 1.4;
}
/* ── Goal form (Break-down editor) ───────────────────────────────────── */
.note-form-goal {
display: flex;
flex-direction: column;
gap: 8px;
}
.note-form-goal-desc {
width: 100%;
box-sizing: border-box;
background: transparent;
border: 1px solid var(--border);
border-radius: 6px;
color: var(--fg);
font-family: inherit;
font-size: 13px;
padding: 8px 10px;
resize: vertical;
min-height: 60px;
}
.note-form-goal-desc:focus {
outline: none;
border-color: color-mix(in srgb, var(--accent-warm) 60%, var(--border));
}
.note-form-goal-actions {
display: flex;
align-items: center;
gap: 8px;
}
.note-form-goal-ai {
display: inline-flex;
align-items: center;
background: color-mix(in srgb, var(--accent-warm) 16%, transparent);
color: var(--accent-warm);
border: 1px solid color-mix(in srgb, var(--accent-warm) 45%, transparent);
border-radius: 6px;
padding: 5px 10px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: background 0.15s, border-color 0.15s;
}
.note-form-goal-ai:hover:not(:disabled) {
background: color-mix(in srgb, var(--accent-warm) 26%, transparent);
border-color: color-mix(in srgb, var(--accent-warm) 65%, transparent);
}
.note-form-goal-hint {
font-size: 11px;
opacity: 0.55;
}
/* ── Today view ──────────────────────────────────────────────────────── */
.notes-today-wrap {
display: flex;
flex-direction: column;
gap: 4px;
margin: 4px 0 12px;
}
.notes-today-header {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--accent-warm);
padding: 6px 10px 4px;
}
.notes-today-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.notes-today-row {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
background: var(--panel, var(--bg));
border: 1px solid var(--border);
border-left: 3px solid color-mix(in srgb, var(--accent-warm) 70%, transparent);
border-radius: 8px;
transition: opacity 0.2s, transform 0.2s;
}
.notes-today-row .note-check-dot {
flex-shrink: 0;
width: 18px;
height: 18px;
border-radius: 50%;
border: 2px solid color-mix(in srgb, var(--fg) 40%, transparent);
cursor: pointer;
transition: background 0.15s, border-color 0.15s;
}
.notes-today-row .note-check-dot:hover {
border-color: var(--accent-warm);
background: color-mix(in srgb, var(--accent-warm) 20%, transparent);
}
.notes-today-row.done .note-check-dot {
background: var(--accent-warm);
border-color: var(--accent-warm);
}
.notes-today-text {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.notes-today-title {
font-size: 11px;
font-weight: 600;
color: var(--accent-warm);
letter-spacing: 0.03em;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.notes-today-step {
font-size: 14px;
color: var(--fg);
line-height: 1.35;
}
.notes-today-progress {
flex-shrink: 0;
font-size: 10px;
font-weight: 600;
opacity: 0.6;
font-variant-numeric: tabular-nums;
}
.notes-empty {
text-align: center;
padding: 32px 16px;
font-size: 13px;
opacity: 0.55;
font-style: italic;
}
/* Bulk action bar */
.notes-bulk-info {
font-size: 10px;
opacity: 0.6;
margin-left: 4px;
}
/* Form label input */
.note-form-label {
background: transparent;
border: 1px dashed color-mix(in srgb, var(--border) 60%, transparent);
color: var(--fg);
font-size: 11px;
padding: 3px 6px;
border-radius: 4px;
outline: none;
width: 80px;
font-family: inherit;
opacity: 0.6;
transition: opacity 0.15s, border-color 0.15s;
}
.note-form-label:hover, .note-form-label:focus { opacity: 1; border-color: var(--border); }
@keyframes noteLabelFlash {
0% { background: color-mix(in srgb, var(--accent) 35%, transparent); }
100% { background: transparent; }
}
.note-card-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 6px;
}
.note-card-title {
font-size: 13px;
font-weight: 600;
cursor: pointer;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.note-content-preview {
font-size: 12px;
opacity: 0.6;
line-height: 1.4;
/* Roomy preview: up to ~14 lines. Content is also sliced to 600 chars
client-side, so cards still stay reasonable in the list. */
max-height: 240px;
overflow: hidden;
cursor: pointer;
white-space: pre-wrap;
word-break: break-word;
}
.note-pin-btn {
background: none;
border: none;
cursor: pointer;
padding: 2px;
flex-shrink: 0;
display: flex;
align-items: center;
}
.note-pin-dot {
width: 7px;
height: 7px;
border-radius: 50%;
border: 1.5px solid color-mix(in srgb, var(--fg) 25%, transparent);
transition: all 0.15s;
}
.note-pin-btn:hover .note-pin-dot { border-color: var(--fg); }
.note-pin-btn.active .note-pin-dot {
background: var(--accent);
border-color: var(--accent);
}
.note-x-btn {
background: none;
border: none;
cursor: pointer;
padding: 2px;
flex-shrink: 0;
color: var(--fg);
opacity: 0.25;
display: flex;
align-items: center;
transition: opacity 0.15s, color 0.15s;
}
.note-x-btn:hover {
opacity: 0.9;
color: var(--red);
}
/* Inline due date next to title */
.note-due-inline {
font-size: 9px;
padding: 1px 6px;
border-radius: 3px;
background: color-mix(in srgb, var(--fg) 8%, transparent);
color: color-mix(in srgb, var(--fg) 60%, transparent);
opacity: 0.7;
white-space: nowrap;
flex-shrink: 0;
align-self: center;
}
.note-due-inline.note-due-overdue {
background: color-mix(in srgb, var(--red) 18%, transparent);
color: var(--red);
opacity: 1;
font-weight: 600;
}
.note-card:active { cursor: grabbing; }
/* Dragged card = "drop preview" — it already sits at the swap-target slot,
so making it the most visible card on screen tells the user exactly where
the note will land on release. */
.note-card.dragging {
opacity: 0.92;
cursor: grabbing;
transform: scale(1.03) rotate(-0.6deg);
box-shadow: 0 14px 32px rgba(0,0,0,0.4), 0 0 0 2px var(--accent, var(--red));
border-color: var(--accent, var(--red)) !important;
background: color-mix(in srgb, var(--accent, var(--red)) 6%, var(--panel)) !important;
z-index: 10;
position: relative;
/* Let elementFromPoint see THROUGH the dragged card so the swap detector
can find the sibling underneath the finger. Without this, in single-row
(list view) the dragged card follows the finger and forever occludes
anything else, so no swap ever fires. The body-level touchmove listener
still receives events regardless. */
pointer-events: none;
}
.notes-pane-body.drag-active .note-card:not(.dragging) {
transition: transform 0.22s cubic-bezier(0.34, 1.2, 0.64, 1), border-color 0.15s, opacity 0.15s;
opacity: 0.78;
}
.notes-pane-body.drag-active .note-card:not(.dragging):hover {
border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, var(--border));
opacity: 1;
}
/* Checklist drag grip */
.note-cl-grip {
cursor: grab;
opacity: 0.2;
font-size: 9px;
letter-spacing: -2px;
user-select: none;
flex-shrink: 0;
width: 14px;
height: 14px;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.15s;
}
.note-cl-row.dragging {
opacity: 0.4;
background: color-mix(in srgb, var(--accent) 8%, transparent);
border-radius: 4px;
}
.note-cl-row.drop-before {
box-shadow: 0 -2px 0 0 var(--accent);
}
.note-cl-row.drop-after {
box-shadow: 0 2px 0 0 var(--accent);
}
.note-card-footer {
display: flex;
align-items: center;
gap: 6px;
margin-top: 2px;
}
.note-delete-btn {
background: none;
border: none;
color: var(--fg);
opacity: 0.2;
cursor: pointer;
padding: 2px;
transition: opacity 0.15s;
}
.note-delete-btn:hover { opacity: 0.8; color: var(--red); }
.note-archive-btn {
background: none; border: none; color: var(--fg); opacity: 0.2; cursor: pointer; padding: 2px; transition: opacity 0.15s;
}
/* Checklist preview — cap height so cards with 30 items don't push the
grid layout into oblivion; the inner list scrolls inside the card. */
.note-checklist-preview {
display: flex;
flex-direction: column;
gap: 0;
min-height: 18px;
max-height: 240px;
overflow-y: auto;
/* Let wheel/touch scrolling continue into the Notes pane when this nested
checklist is already at its top or bottom. */
overscroll-behavior-y: auto;
/* Card has padding-right:30px reserved for the top-corner edit/done/pin
buttons — but those sit above the checklist, not next to it. Pull the
checklist back into that reserved gutter so todo text can extend
further right. The copy button (bottom-right, 28×28) only appears on
hover; leave just enough room (last row) so it doesn't hide the final
item's tail. */
margin-right: -22px;
padding-right: 4px;
}
.note-cl-quickadd {
/* In flow now (was position:absolute), rendered AFTER the reminder badge
so the "+ Add item" input sits underneath the reminder instead of
overlapping it. Slight right inset keeps clear of the bottom-right
copy button on hover. */
display: block;
padding-top: 2px;
padding-right: 32px;
margin-top: 2px;
opacity: 0;
pointer-events: none;
transition: opacity 0.15s;
}
.note-cl-quickadd-input {
width: 100%;
background: transparent;
border: none;
border-top: 1px dashed color-mix(in srgb, var(--border) 60%, transparent);
color: var(--fg);
font-size: 11px;
font-family: inherit;
padding: 4px 2px 2px;
outline: none;
opacity: 0.6;
transition: opacity 0.15s, border-color 0.15s;
}
.note-cl-quickadd-input:focus { opacity: 1; border-top-color: var(--border); }
.note-checkbox {
display: flex;
align-items: center;
gap: 8px;
font-size: 11px;
cursor: pointer;
/* Vertical padding stays 0 so short single-line rows pack tightly.
Left padding bumped so the dot's hover-scale (1.15x) doesn't clip
against the card edge. */
padding: 0 4px 0 8px;
line-height: 1.25;
border-radius: 4px;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.note-checkbox:hover {
background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.note-check-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.note-link {
color: var(--accent-primary, var(--red));
text-decoration: underline;
word-break: break-all;
}
.note-link:hover { opacity: 0.8; }
.note-checkbox-edit, .note-checkbox-rm {
flex: 0 0 auto;
background: transparent;
border: none;
color: var(--fg);
opacity: 0;
cursor: pointer;
padding: 2px;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
margin-left: 2px;
transition: opacity 0.12s, background 0.12s, color 0.12s;
}
.note-checkbox-rm:hover { opacity: 1 !important; color: var(--red); background: color-mix(in srgb, var(--red) 12%, transparent); }
.note-checkbox-edit:hover { opacity: 1 !important; color: var(--accent, var(--blue)); background: color-mix(in srgb, var(--accent, var(--blue)) 12%, transparent); }
.note-check-text[contenteditable="true"] {
background: color-mix(in srgb, var(--fg) 8%, transparent);
outline: 1px solid var(--accent, var(--blue));
border-radius: 2px;
cursor: text;
padding: 0 2px;
margin: 0 -2px;
}
.note-check-dot {
width: 16px;
height: 16px;
border-radius: 50%;
border: 1.75px solid color-mix(in srgb, var(--fg) 35%, transparent);
/* Solid panel fill so the dot reads as a tappable target even when an
image or colored card background sits behind it. */
background: var(--panel);
flex-shrink: 0;
position: relative;
/* Anchor the hover/active scale to the LEFT edge so the dot grows
rightward only. Default center origin pushed the scaled dot off the
left of the card (the parent .note-checklist-preview has overflow:auto
and clips horizontally as a side-effect of overflow-y:auto). */
transform-origin: left center;
transition: background 0.2s, border-color 0.2s, transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.notes-select-mode .note-checkbox:hover { background: transparent; }
.notes-select-mode .note-checkbox:hover .note-check-dot { transform: none; border-color: color-mix(in srgb, var(--fg) 35%, transparent); }
.note-checkbox:hover .note-check-dot {
border-color: var(--accent);
transform: scale(1.15);
}
.note-checkbox.done .note-check-dot {
background: var(--accent);
border-color: var(--accent);
animation: note-check-pop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.note-check-text {
transition: opacity 0.25s;
line-height: 1.3;
padding-right: 4px;
word-break: break-word;
}
/* Due date badge */
.note-due-badge {
font-size: 10px;
padding: 1px 6px;
border-radius: 4px;
background: color-mix(in srgb, var(--fg) 10%, transparent);
color: var(--fg);
opacity: 0.6;
}
.note-due-overdue {
background: color-mix(in srgb, var(--red) 20%, transparent);
color: var(--red);
opacity: 1;
font-weight: 600;
}
/* Color classes — theme-aware via color-mix with --panel */
.note-color-red { background: color-mix(in srgb, var(--red) 18%, var(--panel)); border-color: color-mix(in srgb, var(--red) 30%, var(--border)); }
.note-color-orange { background: color-mix(in srgb, #d19a66 18%, var(--panel)); border-color: color-mix(in srgb, #d19a66 30%, var(--border)); }
.note-color-yellow { background: color-mix(in srgb, var(--hl-string) 18%, var(--panel)); border-color: color-mix(in srgb, var(--hl-string) 30%, var(--border)); }
.note-color-green { background: color-mix(in srgb, #98c379 18%, var(--panel)); border-color: color-mix(in srgb, #98c379 30%, var(--border)); }
.note-color-blue { background: color-mix(in srgb, var(--hl-function) 18%, var(--panel)); border-color: color-mix(in srgb, var(--hl-function) 30%, var(--border)); }
.note-color-purple { background: color-mix(in srgb, var(--hl-keyword) 18%, var(--panel)); border-color: color-mix(in srgb, var(--hl-keyword) 30%, var(--border)); }
/* Standard notes use a small identity palette when no color was selected.
The variation is deliberately limited to the card tint, top edge, and
title accent so the notes still read as one collection. */
.note-card.note-standard {
border-top-width: 3px;
background: color-mix(in srgb, var(--note-accent) 7%, var(--panel));
border-color: color-mix(in srgb, var(--note-accent) 24%, var(--border));
box-shadow: inset 0 2px 0 0 color-mix(in srgb, var(--note-accent) 72%, transparent);
}
.note-card.note-standard:hover {
background: color-mix(in srgb, var(--note-accent) 11%, var(--panel));
}
.note-card.note-standard .note-card-title {
color: color-mix(in srgb, var(--note-accent) 82%, var(--fg));
}
.note-card.note-standard .note-card-label-chip {
border-color: color-mix(in srgb, var(--note-accent) 38%, var(--border));
background: color-mix(in srgb, var(--note-accent) 10%, transparent);
color: color-mix(in srgb, var(--note-accent) 86%, var(--fg));
}
/* Color picker dots */
.note-color-picker {
display: flex;
gap: 5px;
align-items: center;
}
.note-color-dot {
width: 16px;
height: 16px;
border-radius: 50%;
cursor: pointer;
border: 2px solid transparent;
transition: border-color 0.15s, transform 0.15s;
flex-shrink: 0;
}
.note-color-dot:hover { transform: scale(1.15); }
.note-color-dot.active {
border-color: var(--fg);
}
/* Note form */
.note-form {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 10px;
transition: background 0.2s, border-color 0.2s;
/* Container so the action buttons can collapse to icons when the note
card is narrow (see .note-form-collapsible). */
container-type: inline-size;
container-name: noteform;
}
@media (max-width: 768px) {
.notes-pane:not(.notes-view-grid) .note-form.note-form-new {
transform: translateY(6px);
}
}
/* Label sits after the icon in each action button */
.note-form-text-btn .nft-label { margin-left: 5px; }
/* Never let the action buttons spill outside the card */
.note-form-actions-group { flex-wrap: wrap; row-gap: 6px; }
/* When the note card is narrow, collapse Archive / Delete / Cancel to
icon-only (their text label hides; tooltip via title= remains). The
Save/Update button is NOT collapsible — it always keeps its label and
stays last. */
@container noteform (max-width: 360px) {
.note-form-collapsible .nft-label { display: none; }
.note-form-collapsible { padding-left: 9px; padding-right: 9px; }
}
.note-form.note-color-red { background: color-mix(in srgb, var(--red) 18%, var(--panel)) !important; border-color: color-mix(in srgb, var(--red) 30%, var(--border)) !important; }
.note-form.note-color-orange { background: color-mix(in srgb, #d19a66 18%, var(--panel)) !important; border-color: color-mix(in srgb, #d19a66 30%, var(--border)) !important; }
.note-form.note-color-yellow { background: color-mix(in srgb, var(--hl-string) 18%, var(--panel)) !important; border-color: color-mix(in srgb, var(--hl-string) 30%, var(--border)) !important; }
.note-form.note-color-green { background: color-mix(in srgb, #98c379 18%, var(--panel)) !important; border-color: color-mix(in srgb, #98c379 30%, var(--border)) !important; }
.note-form.note-color-blue { background: color-mix(in srgb, var(--hl-function) 18%, var(--panel)) !important; border-color: color-mix(in srgb, var(--hl-function) 30%, var(--border)) !important; }
.note-form.note-color-purple { background: color-mix(in srgb, var(--hl-keyword) 18%, var(--panel)) !important; border-color: color-mix(in srgb, var(--hl-keyword) 30%, var(--border)) !important; }
.note-form-header {
display: flex;
align-items: center;
gap: 8px;
padding-right: 0;
}
.note-form-header .note-form-remind-btn {
margin-right: -7px; /* align bell center with X-button column on todo rows */
position: relative;
top: -2px;
}
.note-form-title {
flex: 1;
min-width: 0;
background: transparent;
border: none;
border-bottom: 1px solid var(--border);
color: var(--fg);
font-size: 14px;
font-weight: 600;
padding: 4px 0;
outline: none;
}
.note-form-title:focus { border-color: var(--fg); }
.note-form-header { position: relative; }
.note-form-header .note-form-due {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
pointer-events: none;
border: 0;
padding: 0;
margin: 0;
right: 0;
bottom: 0;
}
.note-form-due-btn {
flex: 0 0 auto;
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
border-radius: 4px;
width: 26px;
height: 26px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0.85;
transition: opacity 0.15s, border-color 0.15s, background 0.15s;
}
.note-form-due-btn:hover { opacity: 1; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.note-form-due-btn.has-date {
opacity: 1;
border-style: solid;
background: color-mix(in srgb, var(--accent) 18%, transparent);
border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}
.note-type-toggle {
display: flex;
gap: 4px;
}
.note-type-btn {
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
font-size: 11px;
padding: 3px 10px;
border-radius: 4px;
cursor: pointer;
opacity: 0.5;
transition: opacity 0.15s, background 0.15s;
}
.note-type-btn.active {
opacity: 1;
background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.note-form-content {
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
font-size: 12px;
padding: 8px;
border-radius: 4px;
resize: vertical;
/* Roomier default so editing a note isn't cramped; JS auto-grow takes
it further as you type. */
min-height: 120px;
line-height: 1.5;
font-family: inherit;
outline: none;
}
.note-form-content:focus { border-color: var(--fg); }
.note-form-meta {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.note-form-meta .note-color-picker { gap: 3px; }
.note-form-actions-group {
display: flex;
gap: 6px;
align-items: center;
/* Take the full width of the meta row so Archive/Delete can sit on the
LEFT side and the spacer pushes Cancel + Update to the right edge. */
flex: 1 1 auto;
flex-shrink: 0;
flex-wrap: nowrap;
}
.note-form-actions-spacer { flex: 1 1 auto; }
/* Unified text+icon action button — used for Archive, Delete, Cancel, Update
so all 4 read as a matched set. Matches the existing .note-form-save
sizing/padding via the rules at ~25023. */
.note-form-text-btn {
display: inline-flex;
align-items: center;
justify-content: center;
background: color-mix(in srgb, var(--fg) 6%, transparent);
border: 1px solid var(--border);
color: var(--fg);
border-radius: 6px;
padding: 5px 12px;
font-size: 12px;
font-family: inherit;
font-weight: 500;
cursor: pointer;
transition: background 0.12s, border-color 0.12s, color 0.12s;
white-space: nowrap;
}
.note-form-text-btn:hover {
background: color-mix(in srgb, var(--fg) 14%, transparent);
border-color: color-mix(in srgb, var(--fg) 28%, var(--border));
}
.note-form-text-btn.note-form-save {
background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
border-color: color-mix(in srgb, var(--accent, var(--red)) 45%, var(--border));
color: var(--accent, var(--red));
font-weight: 600;
}
.note-form-text-btn.note-form-save:hover {
background: color-mix(in srgb, var(--accent, var(--red)) 28%, transparent);
}
.note-form-text-btn.danger:hover {
background: color-mix(in srgb, var(--red) 14%, transparent);
border-color: color-mix(in srgb, var(--red) 45%, var(--border));
color: var(--red);
}
.note-form-icon-btn {
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
cursor: pointer;
border-radius: 4px;
width: 38px;
height: 38px;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 0.65;
transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
}
/* Force the inner icons to render at the inline-attribute size — some other
stylesheets clamp `svg { width: ... }` globally and that shrinks them. */
.note-form-icon-btn svg { width: 31px !important; height: 31px !important; }
.note-form-icon-btn:hover { opacity: 1; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.note-form-delete-btn:hover { color: var(--red); border-color: color-mix(in srgb, var(--red) 50%, var(--border)); background: color-mix(in srgb, var(--red) 8%, transparent); }
/* Draw mode — canvas + small toolbar */
.note-form-draw-wrap {
display: flex;
flex-direction: column;
gap: 6px;
position: relative;
}
/* X overlay on the canvas — appears only when a photo is loaded as the
background (via _wireCanvas). Clicking it restores the theme surface. */
.note-form-draw-bg-rm {
position: absolute;
top: 8px;
right: 8px;
z-index: 3;
width: 28px;
height: 28px;
border: none;
border-radius: 50%;
background: rgba(0, 0, 0, 0.6);
color: #fff;
font-size: 18px;
line-height: 1;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.note-form-draw-bg-rm:hover { background: rgba(0, 0, 0, 0.8); }
/* Draw mode hides the note's background-color picker and the standalone
image preview — they don't make sense alongside a canvas. Uses :has() so
it kicks in whenever the type-seg flips to .is-draw, in addition to the
JS that sets display:none. */
.note-form:has(.note-form-type-seg.is-draw) .note-color-picker,
.note-form:has(.note-form-type-seg.is-draw) .note-form-image-wrap {
display: none !important;
}
.note-form-canvas {
background: var(--panel, var(--bg));
border: 1px solid var(--border);
border-radius: 6px;
touch-action: none;
max-width: 100%;
cursor: crosshair;
}
.note-form-draw-toolbar {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.note-form-draw-tool {
display: inline-flex;
align-items: center;
height: 26px;
}
/* Color picker rendered as a flat circular swatch — the native chrome around
<input type=color> would clash with the rest of the toolbar. */
.note-form-draw-color {
appearance: none;
-webkit-appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
border: 1px solid color-mix(in srgb, var(--fg) 25%, transparent);
background: none;
cursor: pointer;
padding: 0;
overflow: hidden;
box-sizing: border-box;
flex: 0 0 24px;
}
.note-form-draw-size-wrap { width: 110px; display: inline-flex; align-items: center; }
.note-form-photo-plus {
font-size: 16px;
line-height: 1;
font-weight: 600;
opacity: 0.8;
}
/* Mirrors .gallery-editor-container input[type=range] — slim pill track that
grows on interaction, red circular thumb. */
.note-form-draw-size {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 4px;
background: color-mix(in srgb, var(--fg) 25%, transparent);
border-radius: 999px;
accent-color: var(--red);
cursor: pointer;
transition: height 0.15s ease;
}
/* Brush | Eraser segmented toggle — sliding pill, same recipe as Note/Todo. */
.note-form-draw-be {
display: inline-flex;
height: 32px;
/* Accent-colored box around the whole control so it's instantly readable
as "switch is on", and the sliding pill inside indicates which side. */
border: 2px solid var(--accent);
border-radius: 10px;
overflow: hidden;
position: relative;
flex-shrink: 0;
background: color-mix(in srgb, var(--fg) 5%, transparent);
}
.note-form-draw-be-btn {
background: none;
border: none;
color: color-mix(in srgb, var(--fg) 50%, transparent);
cursor: pointer;
padding: 0 8px;
width: 34px;
height: 100%;
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
z-index: 1;
transition: color 0.18s;
}
.note-form-draw-be-btn:hover { color: var(--fg); }
/* Active side picks up the theme accent so brush vs eraser is unmistakable. */
.note-form-draw-be-btn.active { color: var(--accent-primary, var(--red)); }
.note-form-draw-text,
.note-form-draw-line,
.note-form-draw-circle,
.note-form-draw-undo {
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
cursor: pointer;
border-radius: 4px;
height: 32px;
width: 34px;
font-family: inherit;
font-size: 14px;
display: inline-flex;
align-items: center;
justify-content: center;
opacity: 0.7;
position: relative;
transition: opacity 0.15s, border-color 0.15s, background 0.15s;
}
.note-form-draw-text { font-weight: 700; font-family: Georgia, serif; font-size: 16px; }
.note-form-draw-undo.has-undo {
opacity: 1;
color: var(--accent-primary, var(--red));
border-color: color-mix(in srgb, var(--accent-primary, var(--red)) 55%, var(--border));
background: color-mix(in srgb, var(--accent-primary, var(--red)) 12%, transparent);
}
.note-form-draw-undo:disabled {
opacity: 0.35;
cursor: default;
}
/* Size badge in the bottom-right of T — empty until a size is chosen. */
.note-form-draw-text-badge {
position: absolute;
right: 3px;
bottom: 1px;
font-family: inherit;
font-weight: 700;
font-size: 8px;
line-height: 1;
letter-spacing: 0.3px;
/* Bare --accent is undefined here, was rendering invisible — use the
defined accent so the S/M/L size badge actually shows in theme colour. */
color: var(--accent-primary, var(--red));
}
.note-form-draw-shape-badge {
position: absolute;
right: 3px;
bottom: 1px;
font-family: inherit;
font-weight: 700;
font-size: 8px;
line-height: 1;
letter-spacing: 0.3px;
color: var(--accent-primary, var(--red));
}
.note-form-draw-text:hover,
.note-form-draw-line:hover,
.note-form-draw-circle:hover,
.note-form-draw-undo:hover {
opacity: 1;
border-color: var(--accent);
background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.note-form-draw-text.active,
.note-form-draw-line.active,
.note-form-draw-circle.active {
opacity: 1;
background: color-mix(in srgb, var(--accent) 18%, transparent);
color: var(--accent);
border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}
.note-form-due {
background: transparent;
border: 1px dashed color-mix(in srgb, var(--border) 60%, transparent);
color: var(--fg);
font-size: 11px;
padding: 3px 6px;
border-radius: 4px;
outline: none;
opacity: 0.4;
transition: opacity 0.15s, border-color 0.15s;
}
.note-form-due:hover, .note-form-due:focus { opacity: 0.9; border-color: var(--border); }
.note-form-actions {
display: flex;
gap: 6px;
justify-content: flex-end;
}
.note-form-save,
.note-form-cancel,
.note-form-archive {
background: transparent;
border: 1px solid var(--border);
color: var(--fg);
font-size: 11px;
padding: 4px 12px;
border-radius: 4px;
cursor: pointer;
transition: background 0.15s;
}
.note-form-save { background: color-mix(in srgb, var(--fg) 12%, transparent); font-weight: 600; }
.note-form-save:hover { background: color-mix(in srgb, var(--fg) 20%, transparent); }
.note-form-cancel:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }
.note-form-archive:hover { background: color-mix(in srgb, var(--fg) 8%, transparent); }
/* Checklist inputs in form */
.note-checklist-inputs {
display: flex;
flex-direction: column;
gap: 4px;
}
.note-cl-row {
display: flex;
align-items: center;
gap: 6px;
}
.note-cl-dot {
width: 8px;
height: 8px;
border-radius: 50%;
border: 1.5px solid color-mix(in srgb, var(--fg) 30%, transparent);
flex-shrink: 0;
cursor: pointer;
transition: all 0.15s;
}
.note-cl-dot:hover { border-color: var(--fg); transform: scale(1.2); }
.note-cl-row.done .note-cl-dot {
background: var(--accent);
border-color: var(--accent);
}
.note-cl-row.done .note-cl-text {
opacity: 0.4;
background: linear-gradient(currentColor, currentColor) no-repeat;
background-size: 0 1px;
background-position: 0 calc(50% - 1px);
animation: cl-strike 0.32s ease-out forwards;
transition: opacity 0.2s ease;
}
/* Draws the strikethrough line left-to-right when the row is marked
done, instead of snapping it in full-width on the same frame. */
@keyframes cl-strike {
to { background-size: 100% 1px; }
}
.note-cl-text {
background: transparent;
border: none;
border-bottom: 1px solid var(--border);
color: var(--fg);
font-size: 12px;
padding: 3px 0 3px 3px;
flex: 1 1 auto;
width: auto;
min-width: 0;
outline: none;
}
.note-cl-text:focus { border-color: var(--fg); }
.note-cl-rm {
background: none;
border: none;
color: var(--fg);
opacity: 0.3;
cursor: pointer;
font-size: 14px;
padding: 0;
width: 24px;
height: 14px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: flex-end;
line-height: 1;
}
.note-cl-rm:hover { opacity: 0.8; color: var(--red); }
.note-cl-add {
background: none;
border: none;
color: var(--fg);
opacity: 0.4;
cursor: pointer;
font-size: 11px;
padding: 4px 0;
text-align: left;
}
/* ── Calendar ── */
.cal-modal-content { width:min(680px, 94vw); max-height:88vh; overflow-x:hidden; position:relative; }
#calendar-modal .cal-modal-content { animation: none; }
/* Was overflow-y:auto with grid + day-detail flowing naturally. Now uses a
true splitter layout: the body is a flex column that doesn't scroll
itself; the grid takes the remaining space and scrolls if needed; the
day-detail has an explicit height (driven by the splitter drag via
--cal-detail-h) and shrinks the grid visually as it grows. */
#cal-body { display:flex; flex-direction:column; gap:8px; overflow:hidden; padding:0; min-height:0; }
#cal-body > .cal-grid { flex: 1 1 auto; min-height: 120px; overflow-y: auto; overflow-x: hidden; }
@media (max-width: 768px) {
/* Let the calendar pane shrink to nothing on mobile so the day-detail
splitter can be dragged all the way to the top, hiding the calendar
entirely. Without min-height:0 the grid (or the week-wrap below)
refuses to shrink past its built-in floor. */
#cal-body > .cal-grid,
#cal-body > .cal-wk-wrap {
min-height: 0;
max-height: none;
}
/* Let the week grid fill the calendar pane and scroll its hours internally
instead of overflowing #cal-body (which clips it and feels cramped). */
#cal-body > .cal-wk-wrap {
flex: 1 1 auto;
overflow: auto;
}
}
.cal-toolbar { display:flex; align-items:center; gap:6px; margin-bottom:7px; flex-wrap:wrap; line-height:1; max-width:100%; row-gap:6px; }
/* Quick-add bar: natural-language event creation. Sits directly under
the toolbar; focused with `Q`; opens the bespoke form pre-filled. */
.cal-quickadd-row {
display: flex;
align-items: center;
gap: 8px;
margin: 0 0 8px;
padding: 1px 10px;
border: 1px solid var(--border);
border-radius: 8px;
background: color-mix(in srgb, var(--fg) 3%, var(--bg));
transition: border-color 0.15s, background 0.15s;
position: relative;
}
/* Two-tone placeholder hint: "Quick add" in accent, the example in grey, both
at one low opacity. Overlays the (empty) input; hidden once the user types. */
.cal-quickadd-hint {
position: absolute;
left: 10px;
right: 10px;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: var(--fg);
opacity: 0.5;
pointer-events: none;
white-space: nowrap;
overflow: hidden;
}
.cal-quickadd-hint .qa-hint-accent { color: var(--accent, var(--red)); }
/* ↵ enter glyph in the hint — makes it obvious the field submits on Enter. */
.cal-quickadd-hint .qa-hint-enter { margin-left: auto; flex: 0 0 auto; color: var(--accent, var(--red)); opacity: 0.8; }
/* Cycling example text fades out before swap, then fades back in. */
.cal-quickadd-hint .qa-hint-example {
display: block;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
font-style: italic;
transition: opacity 0.18s ease;
}
.cal-quickadd-hint .qa-hint-example.qa-hint-fade { opacity: 0; }
.cal-title-hint {
position: absolute;
left: 0;
top: 50%;
transform: translateY(calc(-50% - 2px));
display: none;
align-items: center;
gap: 7px;
font-size: 18px;
line-height: 1.2;
white-space: nowrap;
pointer-events: none;
color: color-mix(in srgb, var(--fg) 42%, transparent);
}
.cal-hero-title:placeholder-shown ~ .cal-title-hint { display: inline-flex; }
.cal-title-hint .cal-title-enter-ico { color: var(--accent, var(--red)); flex-shrink: 0; }
/* Pure-CSS toggle: hide the hint as soon as the field has real text (no
per-keystroke JS, so typing never triggers a re-render / focus loss). */
.cal-quickadd-input:not(:placeholder-shown) ~ .cal-quickadd-hint { display: none; }
.cal-quickadd-row:focus-within {
border-color: color-mix(in srgb, var(--accent, var(--fg)) 55%, var(--border));
background: color-mix(in srgb, var(--fg) 5%, var(--bg));
}
.cal-quickadd-icon {
color: color-mix(in srgb, var(--accent, var(--fg)) 80%, transparent);
display: inline-flex;
align-items: center;
flex-shrink: 0;
opacity: 0.7;
}
.cal-quickadd-input {
flex: 1;
min-width: 0;
background: transparent;
border: none;
outline: none;
color: var(--fg);
font-family: inherit;
font-size: 12px;
height: 22px;
padding: 0;
}
.cal-quickadd-status {
font-size: 10px;
opacity: 0.55;
font-variant-numeric: tabular-nums;
}
.cal-toolbar > *, .cal-toolbar-nav > *, .cal-toolbar-right > * { vertical-align:middle; }
.cal-toolbar-nav { display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; }
.cal-toolbar-right {
display: inline-flex;
align-items: center;
justify-content: flex-start;
gap: 6px;
flex: 1 1 100%;
width: 100%;
margin-left: 0;
margin-top: 6px;
flex-wrap: wrap;
}
.cal-title { font-size:13px; font-weight:600; white-space:nowrap; height:24px; line-height:30px; padding:0 6px; display:inline-block; vertical-align:middle; box-sizing:border-box; }
.cal-toolbar-clock {
margin-left: 8px;
color: var(--accent, var(--red));
font-size: 11px;
font-weight: 600;
font-variant-numeric: tabular-nums;
}
button.cal-nav { background:color-mix(in srgb, var(--fg) 6%, transparent); border:1px solid var(--border); color:var(--fg); border-radius:5px; padding:0 8px; height:24px; line-height:22px; cursor:pointer; font-size:11px; font-family:inherit; box-sizing:border-box; vertical-align:middle; }
button.cal-nav:hover { background:color-mix(in srgb, var(--fg) 12%, transparent); }
button.cal-nav.cal-toolbar-arrow {
display: none;
align-items: center;
justify-content: center;
width: 24px;
padding: 0;
border: 1px solid color-mix(in srgb, var(--fg) 14%, transparent);
border-radius: 50%;
background: color-mix(in srgb, var(--bg) 86%, var(--fg) 14%);
color: var(--accent, var(--red));
box-shadow: 0 6px 18px rgba(0,0,0,0.16);
font-size: 18px;
line-height: 1;
opacity: 0.84;
}
.cal-toolbar-arrow-glyph {
position: relative;
top: -1px;
line-height: 1;
}
@media (max-width: 768px) {
button.cal-nav.cal-toolbar-arrow { display: inline-flex !important; }
.cal-toolbar-nav {
flex: 1 1 100%;
width: 100%;
min-width: 0;
}
#cal-next { margin-left: auto; }
}
.cal-week-start-toggle {
display: inline-flex;
align-items: center;
gap: 0;
width: max-content;
padding: 0;
height: 24px;
box-sizing: border-box;
border: 1px solid var(--border);
border-radius: 5px;
background: color-mix(in srgb, var(--fg) 4%, transparent);
}
.cal-week-start-btn {
box-sizing: border-box;
height: 22px !important;
min-height: 22px !important;
margin-top: 0 !important;
padding: 0 7px;
border: 1px solid transparent;
border-radius: 4px;
background: transparent;
color: color-mix(in srgb, var(--fg) 68%, transparent);
cursor: pointer;
font-family: inherit;
font-size: 10px;
line-height: 20px;
transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.cal-settings-actions {
display: flex;
justify-content: flex-end;
width: 100%;
}
#cal-settings-panel .cal-week-start-toggle {
display: flex;
margin-left: auto;
}
.cal-settings-sync-status {
position: relative;
top: 2px;
font-size: 11px;
opacity: 0.6;
}
.cal-settings-sync-status.is-error {
color: var(--red);
opacity: 1;
}
.cal-week-start-btn:hover {
color: var(--fg);
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.cal-week-start-btn.active,
.cal-week-start-btn.active:hover {
color: var(--fg);
border-color: color-mix(in srgb, var(--accent, var(--red)) 40%, var(--border));
background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent);
}
.cal-side-nav {
position: absolute;
top: var(--cal-side-nav-y, 50%);
transform: translateY(-50%);
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid color-mix(in srgb, var(--fg) 14%, transparent);
background: color-mix(in srgb, var(--bg) 86%, var(--fg) 14%);
color: var(--accent, var(--red));
box-shadow: 0 6px 18px rgba(0,0,0,0.16);
cursor: pointer;
z-index: 6;
opacity: 0;
pointer-events: none;
font-family: inherit;
font-size: 24px;
line-height: 28px;
padding: 0 0 3px;
transition: opacity 0.12s ease, background 0.12s ease, transform 0.12s ease;
}
.cal-modal-content:hover .cal-side-nav,
.cal-side-nav:focus-visible {
opacity: 0.84;
pointer-events: auto;
}
.cal-side-nav:hover {
opacity: 1;
background: color-mix(in srgb, var(--bg) 76%, var(--fg) 24%);
transform: translateY(-50%) scale(1.04);
}
button.cal-today-btn { font-size:10px; opacity:0.5; }
button.cal-add-btn { background:var(--accent); color:#fff; border:none; border-radius:50%; width:24px; height:24px; line-height:22px; font-size:18px; cursor:pointer; flex-shrink:0; padding:0; box-sizing:border-box; font-family:inherit; vertical-align:middle; text-align:center; }
button.cal-add-btn.cal-add-btn-text {
width:auto; min-width:0;
background: color-mix(in srgb, var(--fg) 8%, transparent);
color: var(--fg);
border: 1px solid var(--border);
border-radius: 12px;
padding: 0 10px 0 6px;
display: inline-flex;
align-items: center;
gap: 4px;
height: 24px;
line-height: 1;
margin-top: 0;
transition: background 0.15s, border-color 0.15s;
}
button.cal-add-btn.cal-add-btn-text:hover {
background: color-mix(in srgb, var(--fg) 14%, transparent);
border-color: var(--accent);
opacity: 1;
}
.cal-add-plus {
font-size: 16px;
line-height: 1;
color: var(--accent, var(--red));
font-weight: 500;
display: inline-block;
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
padding-bottom: 2px;
}
button.cal-add-btn.cal-add-btn-text:hover .cal-add-plus {
transform: rotate(180deg);
}
.cal-add-label { font-size:11px; font-weight:600; line-height:1; opacity:0.85; }
/* Small variant of the toolbar's +New button. Inherits the .cal-add-btn-text
styles (pill with "+" + "New" + spring-rotate on the +), so the
animation is identical. Just shrunk a bit. */
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm {
height: 20px;
border-radius: 10px;
/* At rest: tight, equal padding around the "+" so it sits centred
with no trailing gap. On hover the right pad and the flex gap grow
to make room for the revealed "New". */
padding: 0 5px;
gap: 0;
transition: padding 0.25s ease, gap 0.25s ease, background 0.15s, border-color 0.15s;
}
/* Don't touch .cal-add-plus on the small variant — the toolbar +New is
the reference and we want the same rotation centre / motion. The label
is shrunk and hidden until hover so the pill is compact at rest. */
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm .cal-add-label {
font-size: 10px;
max-width: 0;
margin-left: 0;
opacity: 0;
overflow: hidden;
white-space: nowrap;
transition: max-width 0.25s ease, opacity 0.18s ease, margin-left 0.25s ease;
}
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm:hover .cal-add-label {
max-width: 40px;
margin-left: 3px;
opacity: 0.85;
}
.cal-add-btn:hover { opacity:0.85; }
/* Mobile: bump the +New pill and the +/- zoom buttons up to a tap-friendly
size so they're easy to hit with a thumb. */
@media (max-width: 768px) {
/* Toolbar +New (relocated by JS into the quickadd row on mobile) */
button.cal-add-btn.cal-add-btn-text {
height: 36px;
border-radius: 18px;
padding: 0 14px 0 12px;
gap: 6px;
flex-shrink: 0;
}
/* Day-detail +New: just a "+" on mobile — drop the label entirely. */
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm {
width: 28px;
height: 28px;
border-radius: 50%;
padding: 0;
gap: 0;
justify-content: center;
}
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm .cal-add-label {
display: none;
}
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm .cal-add-plus {
font-size: 18px;
padding-bottom: 0;
}
.cal-add-plus { font-size: 22px; padding-bottom: 1px; }
.cal-add-label { font-size: 13px; }
.cal-wk-zoom {
width: 36px;
height: 36px;
font-size: 20px;
border-radius: 8px;
opacity: 0.85;
}
/* The +New button sits as a sibling of the quickadd row inside a flex
wrapper, so it's clearly outside the input's bordered box. The row
itself keeps its own styling. */
.cal-quickadd-wrap {
display: flex;
align-items: stretch;
gap: 8px;
margin: 0 0 8px;
}
.cal-quickadd-wrap > #cal-quickadd-row {
flex: 1;
min-width: 0;
margin: 0;
}
.cal-quickadd-wrap > .cal-add-btn-text {
align-self: stretch;
flex-shrink: 0;
}
/* Nudge the "+" glyph up one pixel on the round day-detail button so it
sits visually centred inside the circle. */
button.cal-add-btn.cal-add-btn-text.cal-add-btn-sm .cal-add-plus {
transform: translateY(-1px);
}
/* Event form: bigger Create / Cancel buttons so they're easy to tap. */
.cal-form-actions button.cal-btn {
height: 44px;
padding: 0 20px;
font-size: 14px;
border-radius: 8px;
min-width: 96px;
}
/* Left-align the day/month + tags row in the day-detail panel and
the calendar/category chip row so they sit flush against the
left edge on a narrow screen. */
.cal-detail-header {
justify-content: flex-start;
gap: 12px;
}
.cal-filters {
justify-content: flex-start;
}
}
/* Refresh button spin — driven by JS toggling .cal-syncing on the button.
transform-box + transform-origin keep the rotation pivoted at the SVG's
own centre so it spins in place instead of orbiting / drifting up. */
#cal-sync svg,
#email-lib-refresh-btn svg { display: block; transform-box: fill-box; transform-origin: 50% 50%; }
/* Brief checkmark flash after a refresh completes. Lasts ~900ms in JS;
the small bounce + accent color give a clear "done" cue. */
#cal-sync.cal-sync-done svg,
#email-lib-refresh-btn.email-lib-refresh-done svg {
color: var(--accent, #2ea043);
animation: refresh-check-pop 0.32s ease-out;
}
@keyframes refresh-check-pop {
0% { transform: scale(0.55); opacity: 0.4; }
60% { transform: scale(1.18); opacity: 1; }
100% { transform: scale(1); opacity: 1; }
}
#cal-sync.cal-syncing svg,
#email-lib-refresh-btn.email-lib-refreshing svg { animation: spin 0.6s linear infinite; }
.email-undone-toggle.active,
.email-attach-toggle.active {
background: color-mix(in srgb, var(--accent, var(--red)) 18%, transparent);
border-color: color-mix(in srgb, var(--accent, var(--red)) 50%, transparent);
color: var(--accent, var(--red));
font-weight: 600;
}
/* Hide the gallery-style reader-btn labels on desktop — only the mobile
@media block re-displays them under each icon. Desktop keeps compact
icon-only reader buttons. */
.reader-btn-label {
display: inline-block;
font-size: 9px;
font-weight: 500;
line-height: 1;
letter-spacing: 0.02em;
white-space: nowrap;
opacity: 0.85;
}
/* Inline attachment-filter toggle nested inside the search input. The
input has padding-right:34px set inline so its text doesn't slide
under the button. */
.email-search-wrap { display: flex; align-items: center; }
/* Keep the email search input and the adjacent "Select" button identical in
height regardless of base-vs-mobile overrides — pin both explicitly. */
.email-search-row .memory-search-input {
height: 32px;
min-height: 32px;
box-sizing: border-box;
}
.email-search-row .email-search-select-btn {
height: 32px;
min-height: 32px;
box-sizing: border-box;
margin-top: 6px;
flex-shrink: 0;
}
/* Select moved into the dropdown row — dock it to the right and match the
selects' vertical nudge (.memory-sort-select has top:3px). */
.memory-category-filters .email-filter-select-btn,
.memory-category-filters .email-filter-refresh-btn {
position: relative;
top: 3px;
flex-shrink: 0;
}
.memory-category-filters .email-filter-refresh-btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 8px;
}
.memory-category-filters .email-reminders-clear-btn {
position: relative;
top: -2px;
flex-shrink: 0;
}
.email-attach-toggle-inline {
position: absolute !important;
right: 4px !important;
top: 50%;
transform: translateY(calc(-50% - 5px));
width: 26px !important;
height: 26px !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
border-radius: 6px !important;
background: transparent !important;
border: none !important;
opacity: 0.55;
}
.email-attach-toggle-inline:hover {
opacity: 1;
background: color-mix(in srgb, var(--fg) 10%, transparent) !important;
}
.email-attach-toggle-inline.active {
opacity: 1;
background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent) !important;
color: var(--accent, var(--red)) !important;
}
/* Inline undone-toggle nested inside the search input, sits LEFT of the
attach toggle. Same visual treatment. */
.email-undone-toggle-inline {
position: absolute !important;
right: 34px !important;
top: 50%;
transform: translateY(calc(-50% - 3px));
width: 26px !important;
height: 26px !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
border-radius: 6px !important;
background: transparent !important;
border: none !important;
opacity: 0.55;
}
.email-reminder-toggle-inline {
position: absolute !important;
right: 64px !important;
top: 50%;
transform: translateY(calc(-50% - 3px));
width: 26px !important;
height: 26px !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
border-radius: 6px !important;
background: transparent !important;
border: none !important;
opacity: 0.55;
}
.email-reminder-toggle-inline:hover {
opacity: 1;
background: color-mix(in srgb, var(--fg) 10%, transparent) !important;
}
.email-reminder-toggle-inline.active {
opacity: 1;
background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent) !important;
color: var(--accent, var(--red)) !important;
}
.email-undone-toggle-inline:hover {
opacity: 1;
background: color-mix(in srgb, var(--fg) 10%, transparent) !important;
}
.email-undone-toggle-inline.active {
opacity: 1;
background: color-mix(in srgb, var(--accent, var(--red)) 16%, transparent) !important;
color: var(--accent, var(--red)) !important;
}
/* Round (circular) toggles in the search bar, matching the app's default icon
buttons. Always fully visible (not dimmed until hover). */
.email-attach-toggle-inline,
.email-undone-toggle-inline,
.email-reminder-toggle-inline { border-radius: 50% !important; opacity: 1 !important; }
.email-search-options-btn {
position: absolute !important;
right: 5px !important;
top: 50% !important;
transform: translateY(calc(-50% - 5px));
width: 26px !important;
height: 26px !important;
min-width: 26px !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
border-radius: 999px !important;
background: transparent !important;
border: 0 !important;
color: inherit !important;
opacity: 0.7;
z-index: 2;
}
.email-search-options-btn:hover,
.email-search-options-btn.active {
opacity: 1;
background: color-mix(in srgb, var(--accent, var(--red)) 14%, transparent) !important;
color: var(--accent, var(--red)) !important;
}
.email-search-options-menu {
position: absolute;
top: calc(100% + 2px);
right: 0;
z-index: 75;
min-width: 140px;
padding: 4px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--panel, var(--bg));
box-shadow: 0 8px 24px rgba(0,0,0,0.3);
}
.email-search-options-title {
padding: 5px 9px 4px;
color: var(--fg-muted, var(--fg));
font-size: 10px;
font-weight: 600;
opacity: 0.65;
white-space: nowrap;
}
.email-search-options-menu button {
width: 100%;
border: 0;
background: transparent;
color: var(--fg);
display: flex;
align-items: center;
gap: 10px;
height: auto;
min-height: 0;
padding: 6px 8px;
border-radius: 8px;
font-family: inherit;
font-size: 11px;
font-weight: 400;
line-height: normal;
letter-spacing: normal;
text-align: left;
cursor: pointer;
}
.email-search-options-menu button:hover,
.email-search-options-menu button.active {
background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent);
color: var(--accent, var(--red));
}
.email-search-option-icon {
width: 14px;
height: 14px;
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
opacity: 0.5;
}
.email-search-option-icon svg { width: 14px; height: 14px; }
.email-search-date-panel {
width: 220px;
padding: 6px 7px 5px;
border-top: 1px solid color-mix(in srgb, var(--border) 65%, transparent);
}
.email-search-date-panel input {
width: 100%;
height: 26px;
box-sizing: border-box;
padding: 2px 6px;
border: 1px solid var(--border);
border-radius: 5px;
outline: none;
background: var(--bg);
color: var(--fg);
font: inherit;
}
.email-search-date-panel input:focus { border-color: var(--accent, var(--red)); }
.email-search-date-actions { display: flex; justify-content: flex-end; gap: 5px; }
.email-search-options-menu .email-search-date-actions [data-email-date-apply] {
color: var(--accent, var(--red));
background: color-mix(in srgb, var(--accent, var(--red)) 12%, transparent);
}
@media (max-width: 768px) {
.email-search-options-menu button {
min-height: 44px;
padding: 12px 12px !important;
font-size: 14px !important;
gap: 10px !important;
}
.email-search-options-menu .email-search-option-icon {
width: 18px;
height: 18px;
}
.email-search-options-menu .email-search-option-icon svg {
width: 16px;
height: 16px;
}
}
/* Mobile: enlarge the icons inside the inline search-bar toggles
(done / attachment / reminders) — buttons themselves stay the same,
only the SVG glyph scales up so it's tappable + visible. */
@media (max-width: 768px) {
.email-filter-btn {
height: 30px;
min-height: 30px;
font-size: 12px;
top: -6px;
padding: 0 9px;
}
.email-folder-btn {
top: -4px;
}
#email-lib-folder,
.memory-category-filters .email-filter-select-btn,
.memory-category-filters .email-filter-refresh-btn,
.memory-category-filters .email-filter-settings-btn {
height: 30px !important;
min-height: 30px !important;
box-sizing: border-box;
}
.memory-category-filters .email-filter-refresh-btn,
.memory-category-filters .email-filter-settings-btn {
width: 30px;
padding: 0 !important;
}
.memory-category-filters .email-filter-settings-btn > svg {
width: 15px;
height: 15px;
}
.email-search-row .memory-search-input {
height: 38px;
min-height: 38px;
}
#email-lib-chip-bar {
padding-right: 42px !important;
padding-left: 30px !important;
gap: 5px !important;
}
.email-lib-chip-bar-icon {
width: 14px !important;
height: 14px !important;
left: 9px !important;
}
#email-lib-search {
font-size: 14px !important;
min-width: 88px !important;
top: 0 !important;
}
#email-lib-search::placeholder {
font-size: 14px !important;
}
#email-lib-chip-bar.has-email-lib-pills #email-lib-search {
min-width: 24px !important;
flex: 1 1 24px !important;
}
.email-lib-pill {
min-height: 24px !important;
height: 24px !important;
line-height: 24px !important;
font-size: 12px !important;
padding: 0 6px 0 8px !important;
max-width: 180px !important;
}
.email-lib-pill svg {
width: 14px !important;
height: 14px !important;
}
.email-lib-pill-x {
font-size: 13px !important;
top: -4.5px !important;
}
.email-lib-filter-pill {
min-height: 24px !important;
height: 24px !important;
line-height: 24px !important;
gap: 4px !important;
padding: 0 6px 0 8px !important;
}
.email-lib-filter-pill .email-lib-pill-icon {
width: 17px !important;
height: 17px !important;
}
.email-lib-filter-pill svg {
width: 18px !important;
height: 18px !important;
}
.email-lib-filter-pill .email-lib-pill-x {
font-size: 13px !important;
top: -4.5px !important;
}
.email-tags-toggle-inline {
right: 6px;
height: 28px !important;
min-width: 42px;
padding: 0 8px !important;
font-size: 11px !important;
transform: translateY(calc(-50% - 3px));
}
.email-attach-toggle-inline {
right: 56px !important;
width: 32px !important;
height: 32px !important;
transform: translateY(calc(-50% - 3px));
}
.email-undone-toggle-inline {
right: 92px !important;
width: 32px !important;
height: 32px !important;
transform: translateY(calc(-50% - 3px));
}
.email-reminder-toggle-inline {
right: 128px !important;
width: 32px !important;
height: 32px !important;
transform: translateY(calc(-50% - 3px));
}
.email-attach-toggle-inline svg,
.email-undone-toggle-inline svg,
.email-reminder-toggle-inline svg,
.email-filter-refresh-btn svg {
width: 16px !important;
height: 16px !important;
}
}
.email-accounts-row {
display: flex;
align-items: center;
gap: 10px;
padding: 0 0 6px;
}
.email-accounts-row .doclib-desc { flex-shrink: 0; }
/* Only the direct-child compose button gets pushed right; nested chips
inside #email-lib-accounts pack to the left as normal flex items. */
.email-accounts-row > .memory-toolbar-btn { flex-shrink: 0; margin-left: auto; }
.email-account-chip {
display: inline-flex;
align-items: center;
gap: 5px;
}
.email-account-chip.email-account-has-unread {
border-color: color-mix(in srgb, var(--accent, var(--red)) 42%, var(--border));
background: color-mix(in srgb, var(--accent, var(--red)) 10%, transparent);
color: color-mix(in srgb, var(--accent, var(--red)) 86%, var(--fg));
}
.email-account-unread-dot {
width: 6px;
height: 6px;
border-radius: 999px;
background: var(--accent, var(--red));
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, var(--red)) 14%, transparent);
flex: 0 0 auto;
}
.email-account-chip-label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.email-account-unread-count {
font-size: 9px;
line-height: 1;
font-weight: 700;
color: var(--accent, var(--red));
opacity: 0.9;
position: relative;
top: 0;
}
.email-account-away-label {
color: var(--accent, var(--red));
font-size: 9px;
font-weight: 700;
line-height: 1;
flex: 0 0 auto;
}
.email-accounts-loading-whirlpool {
width: 14px;
height: 14px;
margin: 3px 4px 0 1px;
display: inline-flex;
flex: 0 0 auto;
}
.email-accounts-loading-label {
font-size: 10px;
opacity: 0.55;
position: relative;
top: 2px;
white-space: nowrap;
}
.email-loading-with-label {
/* Fill the email list grid so the spinner lands in the middle of the
email window rather than the top of the empty list section. */
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
text-align: center;
pointer-events: none;
}
.date-section-header {
display: flex;
align-items: center;
gap: 7px;
padding: 8px 9px 4px;
color: color-mix(in srgb, var(--fg) 58%, transparent);
font-size: 10px;
font-weight: 700;
letter-spacing: 0;
line-height: 1.2;
text-transform: uppercase;
user-select: none;
}
.date-section-header::after {
content: '';
flex: 1 1 auto;
height: 1px;
background: color-mix(in srgb, var(--border) 72%, transparent);
}
#session-list .date-section-header {
padding-left: 10px;
padding-right: 8px;
cursor: pointer;
}
#session-list .date-section-header.collapsed {
color: color-mix(in srgb, var(--fg) 36%, transparent);
}
#session-list .date-section-header.collapsed::after {
opacity: 0.45;
}
#session-list .date-section-header:focus-visible {
outline: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
outline-offset: 2px;
border-radius: 5px;
}
/* Bootstrap row shown while the session list hydrates, and on load failure.
Reads as a normal list row but is not selectable. */
.session-list-bootstrap {
cursor: default;
pointer-events: none;
}
#email-lib-grid .date-section-header {
padding: 10px 5px 3px;
}
#email-lib-modal .email-lib-sync-status {
flex: 0 0 auto;
min-height: 14px;
padding: 1px 4px 0;
font-size: 10.5px;
line-height: 1.2;
text-align: center;
opacity: 0.58;
color: var(--muted-text, currentColor);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
top: 4px;
}