/* ===================================================================
   Agenda — design system
   Black/grey palette, three type choices, three density levels.
   =================================================================== */

/* Self-hosted rather than a Google Fonts <link> — no runtime dependency
   on an external CDN for a personal, self-contained app. Single variable-
   font file covers the whole weight axis the app actually uses (400/600/
   700/800), same file Google's own CSS2 API serves for all four — see
   assets/fonts/README.md for how to refresh it. */
@font-face {
  font-family: 'JetBrains Mono';
  src: url('../fonts/JetBrainsMono.woff2') format('woff2');
  font-weight: 400 800; /* the file's actual variable-font wght axis range */
  font-style: normal;
  font-display: swap;
}
/* Same self-hosting approach as JetBrains Mono above — see there. */
@font-face {
  font-family: 'Labrada';
  src: url('../fonts/Labrada.woff2') format('woff2');
  font-weight: 100 900; /* the file's actual variable-font wght axis range */
  font-style: normal;
  font-display: swap;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
/* Author rules like .app{display:flex} normally beat the UA's [hidden]
   rule (both are 0-1-0 specificity, but author origin wins) — without
   this, a hidden .app / .auth-screen still renders at full height and the
   two screens stack instead of only one showing. */
[hidden] { display: none !important; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-current);
  font-size: var(--fs-base);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
button, input { font-family: inherit; font-size: inherit; color: inherit; }

/* ===================================================================
   Icons — Griddy Icons (github.com/griddy-icons/griddy-icons), regular
   style, stored as-is under assets/icons/*.svg. Rendered via CSS mask
   rather than <img> so they recolor with currentColor like the old
   inline SVGs did (fill/stroke color is irrelevant for a mask — only
   each icon's silhouette matters).
   =================================================================== */
.icon {
  display: block; flex: none; width: 18px; height: 18px;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
}
.icon-settings        { -webkit-mask-image: url(../icons/settings.svg);        mask-image: url(../icons/settings.svg); }
.icon-plus            { -webkit-mask-image: url(../icons/plus.svg);            mask-image: url(../icons/plus.svg); }
.icon-close           { -webkit-mask-image: url(../icons/close.svg);           mask-image: url(../icons/close.svg); }
.icon-link-alt-02            { -webkit-mask-image: url(../icons/link-alt-02.svg);            mask-image: url(../icons/link-alt-02.svg); }
.icon-clipboard-list  { -webkit-mask-image: url(../icons/clipboard-list.svg);  mask-image: url(../icons/clipboard-list.svg); }
.icon-image           { -webkit-mask-image: url(../icons/image.svg);           mask-image: url(../icons/image.svg); }
.icon-check           { -webkit-mask-image: url(../icons/check.svg);           mask-image: url(../icons/check.svg); }
.icon-align-text-justify-left      { -webkit-mask-image: url(../icons/align-text-justify-left.svg);      mask-image: url(../icons/align-text-justify-left.svg); }
.icon-trash-alt       { -webkit-mask-image: url(../icons/trash-alt.svg);       mask-image: url(../icons/trash-alt.svg); }
.icon-text-bold       { -webkit-mask-image: url(../icons/text-bold.svg);       mask-image: url(../icons/text-bold.svg); }
.icon-text-italic     { -webkit-mask-image: url(../icons/text-italic.svg);     mask-image: url(../icons/text-italic.svg); }
.icon-help-circle     { -webkit-mask-image: url(../icons/help-circle.svg);     mask-image: url(../icons/help-circle.svg); }
.icon-calendar-dates  { -webkit-mask-image: url(../icons/calendar-dates.svg);  mask-image: url(../icons/calendar-dates.svg); }

:root {
  /* Palette */
  --bg: #ffffff;
  --bg-elevated: #ffffff;
  --bg-subtle: #fafafa;
  --bg-hover: #f2f2f2;
  --bg-active: #ececec;
  --border: #e6e6e6;
  --border-strong: #d4d4d4;
  --text: #171717;
  --text-secondary: #6b6b6b;
  --text-muted: #adadad;
  --danger: #b91c1c;
  --focus: #171717;
  --shadow-pop: 0 2px 10px rgba(0,0,0,.12), 0 1px 2px rgba(0,0,0,.06);
  --shadow-panel: -8px 0 24px rgba(0,0,0,.08);

  /* Typography choices */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-serif: 'Labrada', ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
  --font-mono: 'JetBrains Mono', ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", Consolas, "Liberation Mono", monospace;
  --font-current: var(--font-sans);

  /* Size scale */
  --fs-base: 15px;
  --fs-sm: .8em;
  --fs-lg: 1.28em;

  /* Density (row padding / min-height) */
  --row-pad-y: 9px;
  --row-pad-x: 14px;
  --row-gap: 0;
  --col-gap: 1px;
  --header-pad: 18px;

  /* One task-row's actual rendered height (checkbox/border included), so
     the ruled-line background lines up with real rows. This is a rough
     fallback for the instant before JS measures and overrides it —
     computing it precisely in pure CSS drifts a px or two off real
     rendering depending on font metrics, which reads as visibly broken. */
  --row-h: calc(1.3em + (var(--row-pad-y) * 2) + 1px);

  /* Space reserved to the left of each row for the checkbox — it floats
     in this gutter, outside the ruled-line/border-bottom box, rather
     than inside the row's own padding. */
  --checkbox-gutter: 26px;

  /* Breathing room between row content and the column's true right edge.
     Zero by default — with no today-highlight "card" behind it, that gap
     was just wasted space; .columns' own gap already separates columns.
     Restored when the highlight is on, so content doesn't crowd its edge. */
  --col-end-pad: 0px;
}

html[data-days="7"] { --checkbox-gutter: 20px; }
html[data-highlight="on"] { --col-end-pad: var(--row-pad-x); }

html[data-font="sans"]  { --font-current: var(--font-sans); }
html[data-font="serif"] { --font-current: var(--font-serif); }
html[data-font="mono"]  { --font-current: var(--font-mono); }

html[data-size="sm"] { --fs-base: 13px; }
html[data-size="md"] { --fs-base: 15px; }
html[data-size="lg"] { --fs-base: 17px; }

html[data-density="compact"]     { --row-pad-y: 4px;  --row-pad-x: 12px; --header-pad: 12px; }
html[data-density="cozy"]        { --row-pad-y: 7px;  --row-pad-x: 13px; --header-pad: 15px; }
html[data-density="comfortable"] { --row-pad-y: 11px; --row-pad-x: 14px; --header-pad: 20px; }

@media (max-width: 640px) {
  :root { --row-pad-y: 6px; --row-pad-x: 12px; --header-pad: 12px; }
}

/* ---------- generic ---------- */

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 8px;
  background: transparent; border: none; color: var(--text-secondary);
  cursor: pointer; transition: background .12s ease, color .12s ease;
}
.icon-btn:hover { background: var(--bg-hover); color: var(--text); }
.icon-btn.small { width: 28px; height: 28px; }
.icon-btn.small .icon { width: 15px; height: 15px; }
.icon-btn:disabled { opacity: .3; cursor: default; }
.icon-btn:disabled:hover { background: transparent; }

.btn {
  border: 1px solid var(--border-strong); background: var(--bg);
  padding: 9px 16px; border-radius: 8px; cursor: pointer; font-weight: 600;
}
.btn-primary { background: var(--text); color: #fff; border-color: var(--text); }
.btn-primary:hover { opacity: .88; }
.btn:disabled { opacity: .5; cursor: default; }

/* ===================================================================
   Auth screen
   =================================================================== */

.auth-screen {
  min-height: 100%; display: flex; align-items: center; justify-content: center;
  padding: 24px; background: var(--bg-subtle);
}
.auth-card {
  width: 100%; max-width: 360px; background: var(--bg-elevated);
  border: 1px solid var(--border); border-radius: 16px; padding: 32px 28px;
  box-shadow: var(--shadow-pop);
}
.auth-mark { font-weight: 800; letter-spacing: .06em; font-size: 13px; color: var(--text-muted); text-transform: uppercase; margin-bottom: 18px; }
.auth-form h1 { font-size: 1.3em; margin: 0 0 6px; }
.auth-hint { color: var(--text-secondary); font-size: .92em; margin: 0 0 20px; line-height: 1.5; }
.field { display: block; margin-bottom: 14px; }
.field span { display: block; font-size: .82em; color: var(--text-secondary); margin-bottom: 5px; font-weight: 600; }
.field input {
  width: 100%; padding: 10px 12px; border: 1px solid var(--border-strong); border-radius: 8px;
  background: var(--bg); outline: none;
}
.field input:focus { border-color: var(--focus); box-shadow: 0 0 0 3px rgba(23,23,23,.08); }
.field-hint { margin: -8px 0 16px; font-size: .78em; color: var(--text-muted); }
.auth-error { color: var(--danger); font-size: .85em; margin: -4px 0 14px; }
.auth-form .btn { width: 100%; margin-top: 4px; }

/* ===================================================================
   App shell
   =================================================================== */

.app { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }

.topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 18px; flex: none;
  gap: 12px;
}
/* Content hides by default and only fades in on hover/focus — the topbar
   itself keeps its footprint (an invisible hover strip along the top
   edge) so there's still something to hover to bring it back. */
.wordmark, .topbar-right { opacity: 0; transition: opacity .15s ease; }
.topbar:hover .wordmark, .topbar:hover .topbar-right,
.topbar:focus-within .wordmark, .topbar:focus-within .topbar-right { opacity: 1; }
.wordmark {
  display: flex; align-items: center; gap: 6px;
  background: none; border: 1px solid var(--border-strong); cursor: pointer; font-weight: 700;
  font-size: 15px; color: var(--text); padding: 6px 12px; border-radius: 9px;
  font-family: var(--font-sans);
}
.wordmark:hover { background: var(--bg-hover); }
.topbar-right { display: flex; align-items: center; gap: 6px; }
/* The settings icon button is the topbar's other outline button — every
   other .icon-btn elsewhere (modal close, list delete, ...) stays borderless. */
#toggle-settings { border: 1px solid var(--border-strong); }

/* ===================================================================
   Board / columns
   =================================================================== */

.board { flex: 1 1 auto; overflow: auto; min-height: 0; }
.columns {
  display: flex; height: 100%; min-height: 0;
  gap: 28px; padding: 0 28px;
}
.column {
  flex: 1 1 0; min-width: 190px;
  display: flex; flex-direction: column; min-height: 0; background: transparent;
}
/* 7 columns at the normal gap/min-width doesn't fit most laptop screens
   (7*190 + gaps + padding is comfortably over 1500px) — tighten up. */
html[data-days="7"] .columns { gap: 12px; padding: 0 16px; }
html[data-days="7"] .column { min-width: 118px; }
/* Off by default (see --highlight-today setting) */
html[data-highlight="on"] .column.is-today { background: var(--bg-subtle); }
.column-header {
  /* No top padding — the topbar is hidden until hovered now, so its
     reserved strip already provides the breathing room above this.
     Left padding matches --checkbox-gutter (not --row-pad-x) so the
     date/weekday align with the task text below, not the checkbox gutter. */
  padding: 0 var(--col-end-pad) calc(var(--header-pad) * .55) var(--checkbox-gutter);
  flex: none; position: sticky; top: 0; background: inherit; z-index: 2;
}
.column-date { font-size: .78em; color: var(--text-muted); font-weight: 600; letter-spacing: .04em; }
.column-weekday { font-size: 2em; font-weight: 600; letter-spacing: -.01em; margin-top: 1px; }
/* 2em suits 1/2/3 columns; narrower more-columns layouts need it dialed back. */
html[data-days="4"] .column-weekday { font-size: 1.85em; }
html[data-days="5"] .column-weekday { font-size: 1.7em; }
html[data-days="7"] .column-weekday { font-size: 1.35em; }
.weekday-short { display: none; }
/* 7 narrow columns can't afford full weekday names — swap to a two-letter
   form (Mo, Tu, ...) once the viewport gets tight. */
@media (max-width: 700px) {
  html[data-days="7"] .weekday-full { display: none; }
  html[data-days="7"] .weekday-short { display: inline; }
}
.column-name-input {
  font-size: 1.1em; font-weight: 800; border: none; background: transparent; width: 100%;
  padding: 2px 0; outline: none; border-radius: 4px;
}
.column-name-input:focus { box-shadow: 0 0 0 2px rgba(23,23,23,.08); padding-left: 4px; margin-left: -4px; }
.column-tasks {
  flex: 1 1 auto; overflow-y: auto; overflow-x: visible;
  min-height: 60px;
  /* Containing block for .drop-line, which is positioned absolutely so it
     never affects any row's actual layout — see .drop-line below. */
  position: relative;
  /* Horizontal inset lives here now, not on .task-row — the checkbox
     floats in the left gutter this padding creates, outside the row's
     own border-bottom. Right padding is --col-end-pad (0 unless the
     today-highlight card is on — see above). */
  padding: 0 var(--col-end-pad) 40px var(--checkbox-gutter);
  /* Ruled-paper lines down the whole column, not just between real rows —
     toggled off via html[data-lines="off"] below. content-box origin so
     the lines respect the same inset as the rows (not the checkbox gutter). */
  background-image: linear-gradient(var(--border) 1px, transparent 1px);
  background-size: 100% var(--row-h);
  background-position: 0 -1px;
  background-origin: content-box;
  background-clip: content-box;
}
html[data-lines="off"] .column-tasks { background-image: none; }

/* drag-over indicator */
.column-tasks.drag-over-empty { background: var(--bg-hover); }
.drop-line {
  /* Absolutely positioned (top set from JS) rather than a real sibling
     inserted into the row flow — a real 1-2px element there was pushing
     every row below it down by that much on every dragover, and stacking
     right against whichever row's own border it landed beside made that
     border look thicker too. Purely a visual overlay now: it can never
     move or resize anything. left/right replicate .column-tasks' own
     padding, which a positioned child doesn't inherit automatically. */
  position: absolute; left: var(--checkbox-gutter); right: var(--col-end-pad);
  height: 1px; background: var(--text); pointer-events: none;
}

/* ===================================================================
   Task row
   =================================================================== */

.task-row {
  /* No horizontal padding here — .column-tasks provides the inset (left
     gutter for the checkbox, right padding matching it), so the row's own
     box, including border-bottom, exactly matches the text content width. */
  position: relative; display: flex; align-items: center; gap: 8px;
  padding: var(--row-pad-y) 0;
  border-bottom: 1px solid var(--border);
  cursor: grab;
}
.task-row:active { cursor: grabbing; }
/* The browser's native drag-ghost image (the translucent copy that
   follows the cursor) is always a snapshot of this row's *normal*
   appearance — the .dragging class is deliberately added a tick after
   dragstart (see onDragStart) specifically so it's excluded from that
   snapshot. Left fully normal-looking itself, the source row sitting at
   its original spot reads as a second copy of whatever the ghost is
   currently hovering over ("double lines"). Dimming its content — while
   keeping the border-bottom fully solid, same 1px width as always so the
   row's height never shifts — makes it read as "this is where it was",
   distinct from the bold ghost that's "what you're holding". */
.task-row.dragging { color: var(--text-muted); border-bottom-color: var(--text); }
.task-row.done { color: var(--text-muted); }

.task-checkbox {
  /* Floats in the left gutter, outside the row's own box/border-bottom —
     not part of the row's flex layout at all. */
  position: absolute; left: calc(-1 * var(--checkbox-gutter)); top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; border-radius: 5px;
  border: 1.5px solid var(--border-strong); background: var(--bg);
  cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0;
  opacity: 0; transition: opacity .1s ease;
}
/* Hidden by default — done-ness already reads from the strikethrough
   title, so the box itself only needs to appear when it's actionable. */
.task-row:hover .task-checkbox, .task-row:focus-within .task-checkbox { opacity: 1; }
.task-checkbox:hover { border-color: var(--text); }
.task-row.done .task-checkbox, .subtask-row.done .task-checkbox { background: var(--text-muted); border-color: var(--text-muted); }
.task-checkbox .icon { width: 11px; height: 11px; background-color: #fff; opacity: 0; }
.task-row.done .task-checkbox .icon, .subtask-row.done .task-checkbox .icon { opacity: 1; }

/* Fills the row's available width (same in edit and display state), with
   the actions/details icon anchored at the row's right edge. */
.task-title-wrap { position: relative; flex: 1 1 auto; min-width: 0; height: 1.3em; }
.task-title {
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  line-height: 1.3; cursor: text;
  /* Without this, pressing down on the title text starts a native text
     selection instead of the row's own drag (row.draggable is set on the
     ancestor .task-row) — a plain click still opens edit mode fine, since
     that fires on mouseup/click regardless of selectability. */
  user-select: none;
}
.task-row.done .task-title { text-decoration: line-through; text-decoration-color: var(--text-muted); }
/* Seamless overflow: the truncated title just extends over whatever's
   below it, no card/shadow — same background, same text, just visible.
   Deliberately NOT position:absolute — since the wrap is shrink-to-fit
   (flex: 0 1 auto), pulling the span out of flow would collapse the
   wrap's own box to zero width and shift the actions button into the
   text. overflow:visible instead: the box's footprint (and therefore
   its siblings' layout) never changes, only what paints outside it.
   position:relative is only there so z-index has an effect. */
.task-title-wrap:not(.editing):hover .task-title {
  position: relative; overflow: visible; text-overflow: clip; z-index: 6;
}
.task-title-input {
  display: block; width: 100%; border: none; outline: none; background: transparent;
  line-height: 1.3; padding: 0; margin: 0; font: inherit; color: inherit;
}
.task-title-input::placeholder { color: var(--text-muted); }

/* z-index above the title's hover-expand popup (z-index: 6) — otherwise
   that popup visually and hit-test-wise covers these buttons whenever a
   truncated title is what triggered the row hover in the first place. */
.task-actions { flex: none; display: flex; align-items: center; gap: 2px; position: relative; z-index: 8; }
.task-actions .icon-btn {
  opacity: 0; transition: opacity .1s ease; border-radius: 6px;
  /* Opaque backing matching the row's own background — a very long
     truncated title can visually overflow underneath this button on
     hover (it stays on top and clickable regardless, via z-index, but
     without this it would render as text showing through the icon). */
  background: var(--bg);
}
html[data-highlight="on"] .column.is-today .task-actions .icon-btn { background: var(--bg-subtle); }
.task-row:hover .task-actions .icon-btn, .task-row:focus-within .task-actions .icon-btn { opacity: 1; }
/* Same specificity as the background rules above (two classes) — restate
   the hover highlight so it still wins on source order. */
.task-actions .icon-btn:hover { background: var(--bg-hover); }
/* Opacity set directly on this button (not inherited through a hidden
   ancestor) so it stays visible without hovering, as a persistent hint
   that the task has details. */
.task-actions .details-btn.has-details { opacity: 1; color: var(--text-secondary); }

/* min-height matches a real .task-row's box: its details button
   (.icon-btn.small, 28px) plus the same vertical padding, added explicitly
   here because box-sizing:border-box means a plain "min-height: 28px"
   would be a no-op (padding + text already exceed 28px on their own).
   A real row is always at least that tall because of the button, even
   though its own text (1.3em) is shorter — without this the add-row
   measures shorter than a real row, which both looks off (the input sits
   high in a taller-than-it-needs-to-be visual slot) and throws off
   syncRowHeightVar()'s --row-h measurement whenever the add-row is the
   only row present (an empty column) — see app.js. */
.task-add-row {
  display: flex; align-items: center; padding: var(--row-pad-y) 0;
  min-height: calc(28px + (var(--row-pad-y) * 2));
}
.task-add-input {
  /* font: inherit — without it this used the browser's default form-field
     font/size instead of the app's, throwing off both its own vertical
     centering and the row-height measurement JS takes from this element. */
  flex: 1 1 auto; width: 100%; border: none; outline: none; background: transparent;
  color: var(--text); font: inherit; line-height: 1.3; padding: 0;
}

/* ===================================================================
   Lists drawer
   =================================================================== */

.lists-drawer {
  position: fixed; left: 0; right: 0; bottom: 0; height: 46vh; min-height: 260px;
  /* --bg-subtle instead of --bg-elevated (both other floating panels use
     that) — it's the one deliberately off-white token in the palette, so
     the card reads as its own surface against the page above it, even
     full-width/flush with no gap around it. Columns/rows inside stay
     transparent, so this shows through everywhere, not just the gaps. */
  background: var(--bg-subtle); border-top: 1px solid var(--border-strong);
  box-shadow: 0 -8px 24px rgba(0,0,0,.1);
  display: flex; flex-direction: column;
  /* cubic-bezier(0.32,0.72,0,1) — a fast-out, gently-settling curve (the
     same shape several polished bottom-sheet implementations default to)
     instead of the generic "ease" keyword, which feels comparatively
     mechanical/linear-ish here. Same curve both ways (open and close) for
     a consistent feel; slightly longer than before (.22s) since a snappy
     duration undersells a nicer curve — too quick to actually see it. */
  transform: translateY(105%);
  transition: transform .32s cubic-bezier(0.32, 0.72, 0, 1);
  z-index: 30;
}
.lists-drawer.open { transform: translateY(0); }
.lists-drawer.resizing { transition: none; }

.lists-drawer-handle {
  position: absolute; top: -7px; left: 0; right: 0; height: 14px;
  display: flex; align-items: center; justify-content: center;
  cursor: row-resize; touch-action: none;
}
.lists-drawer-handle span {
  width: 40px; height: 4px; border-radius: 3px; background: var(--border-strong);
  opacity: 0; transition: opacity .12s ease, background .12s ease;
}
.lists-drawer-handle:hover span, .lists-drawer-handle.active span {
  opacity: 1; background: var(--text-muted);
}
.lists-drawer-header {
  display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding: 10px 16px;
  flex: none;
}
.lists-columns { flex: 1 1 auto; overflow: auto; }
/* Fixed width matching the day columns above (--day-col-w, measured in
   app.js — how many lists exist has nothing to do with how many days are
   shown, so the day columns' own flex:1 1 0 sharing behavior can't just
   be reused here: a single list would stretch to fill the whole row).
   Neither grows nor shrinks — more lists than fit just scroll
   horizontally instead of everything getting squeezed narrower. */
.lists-columns .column { flex: 0 0 var(--day-col-w, 190px); width: var(--day-col-w, 190px); }
.lists-columns .column-header { display: flex; align-items: center; gap: 6px; }
.list-delete-btn { opacity: 0; }
.column-header:hover .list-delete-btn { opacity: 1; }
.empty-lists-hint { padding: 24px; color: var(--text-muted); font-size: .9em; }

/* ===================================================================
   Detail panel
   =================================================================== */

.scrim {
  position: fixed; inset: 0; background: rgba(0,0,0,.12); z-index: 39;
  opacity: 0; pointer-events: none; transition: opacity .18s ease;
}
.scrim.open { opacity: 1; pointer-events: auto; }

.settings-panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 360px; max-width: 92vw;
  background: var(--bg-elevated); box-shadow: var(--shadow-panel);
  transform: translateX(100%); transition: transform .22s ease;
  z-index: 40; overflow-y: auto; border-left: 1px solid var(--border);
}
.settings-panel.open { transform: translateX(0); }

/* Detail panel is a centered modal, not a sidebar. */
.detail-panel {
  position: fixed; inset: 0; z-index: 40;
  display: flex; align-items: center; justify-content: center; padding: 24px;
  opacity: 0; pointer-events: none; transition: opacity .16s ease;
}
.detail-panel.open { opacity: 1; pointer-events: auto; }
.detail-panel-inner {
  width: 100%; max-width: 560px; max-height: 82vh; overflow-y: auto;
  background: var(--bg-elevated); border-radius: 16px;
  box-shadow: var(--shadow-pop), 0 24px 64px rgba(0,0,0,.22);
  transform: scale(.96) translateY(8px); transition: transform .16s ease;
}
.detail-panel.open .detail-panel-inner { transform: scale(1) translateY(0); }

.detail-panel-inner { padding: 22px 24px; position: relative; }
.settings-panel-inner { padding: 22px 24px 40px; position: relative; }

.detail-header {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  border-bottom: 1px solid var(--border); margin-bottom: 10px; padding-bottom: 10px;
}
.detail-title-wrap { position: relative; flex: 1 1 auto; min-width: 0; }
.detail-title {
  display: block; width: 100%; border: none; outline: none; background: transparent;
  font: inherit; color: inherit; font-size: 1.35em; font-weight: 800; padding: 4px 0;
}
.detail-title-display { cursor: text; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.detail-title-display:empty:before { content: attr(data-placeholder); color: var(--text-muted); font-weight: 800; }
.detail-close { flex: none; }

/* Settings panel keeps the older floating-over-the-heading treatment —
   it wasn't part of this restructure. */
.panel-close { position: absolute; top: 16px; right: 16px; }
.detail-toolbar { display: flex; align-items: center; gap: 2px; padding: 6px 0 12px; margin-left: -10px; }
.toolbar-btn {
  width: 30px; height: 30px; border-radius: 7px; border: none; background: transparent;
  color: var(--text-secondary); cursor: pointer; display: flex; align-items: center; justify-content: center;
  font-size: .95em;
}
.toolbar-btn:hover { background: var(--bg-hover); color: var(--text); }

.detail-body {
  min-height: 90px; outline: none; line-height: 1.55; font-size: .96em;
  padding: 2px 0; margin-bottom: 4px;
}
.detail-body:empty:before {
  content: attr(data-placeholder); color: var(--text-muted);
}
.detail-body img { max-width: 100%; border-radius: 8px; display: block; margin: 8px 0; }
.detail-body a { color: var(--text); text-decoration-color: var(--border-strong); }

.detail-subtasks { display: flex; flex-direction: column; margin-top: 6px; }
.subtask-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; }
/* .task-checkbox is position:absolute by default now (floats in the day
   row's checkbox gutter) — subtask rows have no such gutter, so put it
   back in normal flow here. */
/* transform: none — the base rule's translateY(-50%) is paired with
   top:50% for the day-row's absolute centering trick; transform applies
   regardless of position, so without this it was still nudging the
   (now static, flex-centered) subtask checkbox up by half its height. */
.subtask-row .task-checkbox { position: static; transform: none; flex: none; opacity: 1; }
.subtask-text {
  flex: 1 1 auto; border: none; outline: none; background: transparent; padding: 2px 0;
}
.subtask-row.done .subtask-text { color: var(--text-muted); text-decoration: line-through; }
.subtask-remove { opacity: 0; flex: none; }
.subtask-row:hover .subtask-remove { opacity: 1; }
/* Red icon, same grey hover background as any other icon button (e.g.
   the modal close button) — only the color singles it out as destructive. */
.icon-btn.subtask-remove:hover { color: var(--danger); }
.detail-footer {
  margin-top: 24px; padding-top: 14px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.detail-meta { font-size: .78em; color: var(--text-muted); }
.detail-delete {
  border: none; background: none; padding: 4px 2px; cursor: pointer;
  color: var(--text-muted); font-size: .82em; font-weight: 400;
}
.detail-delete:hover, .detail-delete.confirming { color: var(--danger); }

/* ===================================================================
   Settings panel
   =================================================================== */

.settings-panel { width: 360px; }
.settings-panel h2 { margin: 0 0 20px; font-size: 1.2em; }
.setting-group { margin-bottom: 22px; }
.setting-label { display: block; font-size: .8em; font-weight: 700; color: var(--text-secondary); margin-bottom: 8px; text-transform: uppercase; letter-spacing: .04em; }
.option-row { display: flex; gap: 6px; }
.option-btn {
  flex: 1 1 0; padding: 10px 8px; border-radius: 9px; border: 1px solid var(--border-strong);
  background: var(--bg); cursor: pointer; font-size: .88em;
}
.option-btn:hover { background: var(--bg-hover); }
.option-btn.active { background: var(--text); color: #fff; border-color: var(--text); }
.setting-note { font-size: .8em; color: var(--text-muted); line-height: 1.5; }

.setting-select {
  /* font: inherit first, then override size — otherwise the shorthand
     resets the size override that follows it (same gotcha as other
     native form controls in this app). */
  display: block; width: 100%; padding: 10px 12px; border-radius: 9px;
  border: 1px solid var(--border-strong); background: var(--bg); color: var(--text);
  font: inherit; font-size: .88em; cursor: pointer;
}
.setting-select:hover { background: var(--bg-hover); }
.setting-select:focus { outline: none; box-shadow: 0 0 0 2px rgba(23,23,23,.08); }

.btn-outline-block {
  display: block; width: 100%; margin-top: 28px; padding: 10px 14px;
  border: 1px solid var(--border-strong); border-radius: 9px; background: transparent;
  color: var(--text); font-size: .9em; font-weight: 600; cursor: pointer; text-align: center;
}
.btn-outline-block:hover { background: var(--bg-hover); }

.settings-footer {
  margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
}
.settings-footer-link {
  border: none; background: none; padding: 8px 2px; cursor: pointer;
  color: var(--text-secondary); font-size: .9em; font-weight: 600; text-decoration: none;
}
.settings-footer-link:hover { color: var(--text); }
.settings-footer-logout { color: var(--danger); }
.settings-footer-logout:hover { color: var(--danger); opacity: .8; }

/* ===================================================================
   Toast
   =================================================================== */

.toast {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
  background: var(--text); color: #fff; padding: 10px 18px; border-radius: 10px;
  font-size: .88em; box-shadow: var(--shadow-pop); z-index: 60;
}

/* ===================================================================
   Responsive
   =================================================================== */

@media (max-width: 640px) {
  .column { min-width: 82vw; scroll-snap-align: start; }
  .columns { scroll-snap-type: x mandatory; }
  .settings-panel { width: 100vw; max-width: 100vw; }
  .detail-panel { padding: 12px; }
  .detail-panel-inner { max-height: 92vh; border-radius: 14px; }
}

/* Touch devices have no :hover — anything gated on it needs to stay reachable. */
@media (hover: none) {
  .task-actions .icon-btn { opacity: 1; }
  .task-checkbox { opacity: .6; }
  .list-delete-btn { opacity: .55; }
  .subtask-remove { opacity: .55; }
  /* No hover on touch — the topbar would otherwise be permanently hidden
     and unreachable. */
  .wordmark, .topbar-right { opacity: 1; }
}
