/* Shown from first paint until the session check resolves — see the HTML
   comment next to it for why this exists. Solid var(--bg) so nothing
   underneath is visible through it, fades out (not an abrupt cut) once
   removed via .app-loading.hide. */
.app-loading {
  position: fixed; inset: 0; z-index: 9999; background: var(--bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
  transition: opacity 0.25s ease;
}
.app-loading.hide { opacity: 0; pointer-events: none; }
.app-loading-heart {
  width: 56px; height: 56px; border-radius: 16px;
  background: var(--accent-grad); display: flex; align-items: center; justify-content: center;
  font-size: 1.6rem; animation: appLoadingHeartbeat 1.4s ease-in-out infinite;
}
@keyframes appLoadingHeartbeat {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.12); }
}
.app-loading-text { color: var(--text-muted); font-size: 0.85rem; font-weight: 600; margin: 0; }

.page-hero { padding: 56px 24px 24px; text-align: center; }
.page-hero h1 { font-size: 2.2rem; margin: 0 0 8px; }
.page-hero p { color: var(--text-muted); margin: 0; }
.user-badge { font-size: 1.1rem; color: var(--text-muted); font-weight: 400; }
.today-label {
  text-align: center; color: var(--text-muted); font-size: 0.88rem; font-weight: 600;
  margin: 0 0 4px; text-transform: capitalize;
}

/* The hero sits directly on the animated blob background (no card behind
   it), so its near-white dark-mode text can land on a light-hued patch of
   the blob and wash out. A dark text-shadow keeps it readable regardless
   of which color the blob is cycling through underneath. */
@media (prefers-color-scheme: dark) {
  :root.animated-bg-on:not([data-theme="light"]) .page-hero h1,
  :root.animated-bg-on:not([data-theme="light"]) .page-hero p,
  :root.animated-bg-on:not([data-theme="light"]) .user-badge,
  :root.animated-bg-on:not([data-theme="light"]) .today-label {
    text-shadow: 0 2px 10px rgba(0, 0, 0, 0.6), 0 1px 3px rgba(0, 0, 0, 0.7);
  }
}
:root[data-theme="dark"].animated-bg-on .page-hero h1,
:root[data-theme="dark"].animated-bg-on .page-hero p,
:root[data-theme="dark"].animated-bg-on .user-badge,
:root[data-theme="dark"].animated-bg-on .today-label {
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.6), 0 1px 3px rgba(0, 0, 0, 0.7);
}

.tab-bar {
  display: flex; gap: 8px; flex-wrap: wrap; justify-content: center;
  padding: 8px 0 24px;
}
.tab-btn {
  padding: 10px 18px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--bg-elevated); color: var(--text-muted); font-weight: 600;
  font-size: 0.92rem; cursor: pointer; transition: all 0.15s ease;
}
.tab-btn:hover { border-color: var(--accent); color: var(--text); transform: translateY(-1px); }
.tab-btn:active { transform: translateY(0); }
.tab-btn.active { background: var(--accent-grad); color: white; border-color: transparent; }
.tab-panel { padding-bottom: 80px; }

/* Mobile-only bottom tab bar (native-app style); hidden on desktop where
   the top .tab-bar already works well. */
.bottom-tab-bar { display: none; }
.tab-panel:not([hidden]) { animation: tabFadeIn 0.32s cubic-bezier(0.16, 1, 0.3, 1) both; }
@keyframes tabFadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.item-row, .note-row { animation: rowIn 0.28s ease both; }
.item-row:nth-child(1), .note-row:nth-child(1) { animation-delay: 0.02s; }
.item-row:nth-child(2), .note-row:nth-child(2) { animation-delay: 0.05s; }
.item-row:nth-child(3), .note-row:nth-child(3) { animation-delay: 0.08s; }
.item-row:nth-child(4), .note-row:nth-child(4) { animation-delay: 0.11s; }
.item-row:nth-child(5), .note-row:nth-child(5) { animation-delay: 0.14s; }
@keyframes rowIn {
  from { opacity: 0; transform: translateX(-6px); }
  to { opacity: 1; transform: translateX(0); }
}
.item-row { transition: transform 0.15s ease, box-shadow 0.15s ease; }
.item-row:hover { transform: translateY(-1px); box-shadow: var(--shadow); }

.calendar-toolbar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 16px 0; flex-wrap: wrap; gap: 12px;
}
.calendar-toolbar-right { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.color-legend { display: flex; gap: 16px; flex-wrap: wrap; padding: 14px 6px 4px; }
.legend-item {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.8rem; color: var(--text-muted); font-weight: 600;
}
.legend-item .dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }

/* Compact density: event rows/day-number badges shrink so a busy month fits
   without scrolling, at the cost of readability — an explicit user choice
   via the toolbar toggle, not a fixed breakpoint. */
#calendar.compact .fc-event {
  padding: 0 4px; font-size: 0.72rem; margin-bottom: 1px;
}
#calendar.compact .fc-daygrid-day-events { margin-top: 1px; }
#calendar.compact .fc-daygrid-day-top .fc-daygrid-day-number {
  font-size: 0.78rem; padding: 2px 4px;
}
#calendar.compact.fc .fc-daygrid-day-frame { padding: 2px; }

/* Overall calendar size (separate from density: this scales everything —
   grid, text, buttons — proportionally, for readability preference rather
   than fitting-more-on-screen). `zoom` (not `transform: scale`) is used
   deliberately since it reflows layout, so nothing overlaps or leaves gaps. */
#calendar.size-small { zoom: 0.85; }
#calendar.size-large { zoom: 1.18; }

/* Safety net: FullCalendar sizes its own grid via a JS measurement of this
   card at render time. If that measurement is ever stale (taken before
   fonts/layout fully settled — updateSize() below covers the normal case,
   but this covers whatever normal case doesn't), the grid can end up wider
   than the card. Since the page has overflow-x:hidden site-wide (to stop
   the page itself from scrolling sideways), an overflow here would
   otherwise make the rightmost day column(s) — Saturday/Sunday — silently
   unreachable with no way to scroll to them. A local scrollbar on just this
   card means that never happens, even if the underlying measurement issue
   isn't fully eliminated. */
.calendar-card { padding: 20px; overflow-x: auto; }

/* Shopping list / files list */
.list-card { max-width: 560px; margin: 0 auto; padding: 24px; }
.inline-form { display: flex; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }
.inline-form input {
  flex: 1; min-width: 140px; padding: 10px 14px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 0.95rem;
}
.inline-form select {
  padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 0.88rem; cursor: pointer;
}
.item-list { display: flex; flex-direction: column; gap: 8px; max-width: 560px; margin: 0 auto; }
.shopping-group + .shopping-group { margin-top: 18px; }
.shopping-group-title {
  margin: 0 0 8px; font-size: 0.78rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.03em; color: var(--text-muted);
}
.shopping-group .item-row + .item-row { margin-top: 8px; }
.item-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-elevated);
}
.item-row.checked span { text-decoration: line-through; color: var(--text-muted); }
.item-check { display: flex; align-items: center; gap: 10px; cursor: pointer; }
.item-check input { width: 18px; height: 18px; }
.item-delete { border: none; background: none; color: #e5484d; cursor: pointer; font-size: 0.9rem; }

/* Notes */
/* Shared tally counters ("wie oft haben wir gekocht") */
.counters-card { padding: 20px; max-width: 900px; margin: 0 auto 20px; }
.counters-card h3 { margin: 0 0 14px; font-size: 1.05rem; }
.counters-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
.counter-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 14px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg-elevated);
}
.counter-title { font-weight: 600; font-size: 0.95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.counter-controls { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.counter-btn {
  width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 1.1rem; font-weight: 700; cursor: pointer;
  display: flex; align-items: center; justify-content: center; line-height: 1;
  transition: background-color 0.12s ease, transform 0.12s ease;
}
.counter-btn:hover { background: var(--border); }
.counter-btn:active { transform: scale(0.9); }
.counter-btn.increment { background: var(--accent-grad); color: white; border-color: transparent; }
.counter-value { min-width: 32px; text-align: center; font-weight: 800; font-size: 1.15rem; color: var(--accent); }
.counter-delete { border: none; background: none; color: #e5484d; cursor: pointer; font-size: 0.85rem; flex-shrink: 0; }

/* Jahrestag countdown, shown on the hero when a "together since" date is set */
.anniversary-countdown {
  text-align: center; margin: 6px 0 0; font-weight: 700; font-size: 0.95rem; color: var(--accent);
}

/* "Wir beide" tab: Monatlicher Check-in, Date-Ideen-Jar, Nachricht an Partner */
.checkin-card, .date-ideas-card, .push-message-card { padding: 20px; max-width: 900px; margin: 0 auto 20px; }
.checkin-card h3, .date-ideas-card h3, .push-message-card h3 { margin: 0 0 6px; font-size: 1.05rem; }

.checkin-month-nav { display: flex; align-items: center; justify-content: center; gap: 12px; margin-bottom: 10px; }
.checkin-month-nav p { margin: 0; min-width: 160px; text-align: center; }
.checkin-nav-btn {
  width: 32px; height: 32px; padding: 0; border-radius: 50%; display: flex;
  align-items: center; justify-content: center; font-size: 1.1rem; line-height: 1;
}
.checkin-nav-btn:disabled { opacity: 0.35; cursor: default; }
.checkin-card textarea {
  width: 100%; padding: 10px 14px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 0.92rem; font-family: inherit; resize: vertical;
}
.checkin-card .settings-label { display: block; margin: 10px 0 6px; }
.checkin-answer { padding: 12px 0; border-top: 1px solid var(--border); }
.checkin-answer:first-child { border-top: none; }
.checkin-answer h4 { margin: 0 0 6px; font-size: 0.95rem; color: var(--accent); }
.checkin-answer p { margin: 4px 0; font-size: 0.9rem; line-height: 1.5; }

.date-ideas-list { display: flex; flex-direction: column; gap: 8px; margin: 12px 0 14px; }
.date-idea-done-at { font-weight: 400; font-size: 0.8rem; color: var(--text-muted); }
.date-idea-surprise-result {
  margin-top: 12px; padding: 14px; border-radius: 12px; text-align: center;
  font-weight: 700; font-size: 1.05rem; background: color-mix(in srgb, var(--accent) 14%, transparent);
}
#date-idea-form select {
  padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 0.88rem; cursor: pointer;
}

.push-message-card input, .push-message-card textarea {
  width: 100%; padding: 10px 14px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 0.92rem; font-family: inherit;
}
.push-message-card textarea { resize: vertical; }

/* Small always-visible mood check-in button, floating above the mobile
   bottom-tab-bar (and just above the footer on desktop) so it's reachable
   from any tab without hunting through a menu. */
.mood-widget { position: fixed; right: 16px; bottom: calc(16px + env(safe-area-inset-bottom)); z-index: 60; }
.mood-widget-btn {
  width: 48px; height: 48px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--bg-elevated); box-shadow: var(--shadow); font-size: 1.4rem; cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: transform 0.12s ease;
}
.mood-widget-btn:hover { transform: scale(1.08); }
.mood-widget-btn:active { transform: scale(0.95); }
.mood-widget-popover {
  position: absolute; right: 0; bottom: 58px; width: 260px; padding: 16px;
  border-radius: 16px; border: 1px solid var(--border); background: var(--bg-elevated);
  box-shadow: var(--shadow);
}
.mood-widget-hint { margin: 0 0 10px; font-size: 0.8rem; color: var(--text-muted); }
.mood-picker { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.mood-btn {
  width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--bg); font-size: 1.15rem; cursor: pointer; display: flex;
  align-items: center; justify-content: center; transition: transform 0.12s ease, border-color 0.12s ease, background-color 0.12s ease;
}
.mood-btn:hover { transform: translateY(-2px); }
.mood-btn.selected { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, transparent); }
.mood-custom-form { display: flex; gap: 6px; margin-bottom: 12px; }
.mood-custom-form input {
  flex: 1; min-width: 0; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 1rem;
}
.mood-custom-form button { padding: 6px 12px; font-size: 0.95rem; }
.mood-today { display: flex; flex-direction: column; gap: 6px; }
.mood-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 6px 10px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg);
}
.mood-row-name { font-weight: 600; font-size: 0.82rem; }
.mood-row-emoji { font-size: 1.1rem; }
@media (max-width: 640px) {
  .mood-widget { bottom: calc(92px + env(safe-area-inset-bottom)); }
}

.notes-layout { display: grid; grid-template-columns: 260px 1fr; gap: 20px; max-width: 900px; margin: 0 auto; }
@media (max-width: 700px) { .notes-layout { grid-template-columns: 1fr; } }
.notes-list-card { padding: 16px; height: fit-content; }
.note-row {
  padding: 10px 12px; border-radius: 8px; cursor: pointer; margin-bottom: 4px;
  display: flex; flex-direction: column; gap: 2px;
}
.note-row:hover { background: var(--border); }
.note-row.active { background: color-mix(in srgb, var(--accent) 15%, transparent); }
.note-row strong { font-size: 0.9rem; }
.note-row span { font-size: 0.78rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.note-editor-card {
  padding: 20px; display: flex; flex-direction: column; gap: 12px;
  animation: tabFadeIn 0.25s ease both;
}
#note-title {
  padding: 10px 14px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 1.05rem; font-weight: 700;
}
.note-toolbar { display: flex; gap: 6px; }
.fmt-btn {
  width: 32px; height: 32px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg-elevated); color: var(--text); cursor: pointer; font-size: 0.85rem;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
.fmt-btn:hover { background: var(--border); border-color: var(--accent); }

.note-body-editor {
  min-height: 280px; padding: 12px 14px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 0.95rem; line-height: 1.6;
  overflow-y: auto;
}
.note-body-editor:focus { outline: none; border-color: var(--accent); }
.note-body-editor:empty::before { content: attr(data-placeholder); color: var(--text-muted); }
.note-body-editor ul { padding-left: 22px; margin: 8px 0; }

/* Settings tab */
.settings-layout {
  display: grid; grid-template-columns: 1fr 1fr; gap: 20px;
  max-width: 900px; margin: 0 auto;
}
@media (max-width: 700px) { .settings-layout { grid-template-columns: 1fr; } }
.settings-card { padding: 22px; display: flex; flex-direction: column; gap: 4px; }
.settings-card h2 { margin: 0 0 4px; font-size: 1.05rem; }
.settings-hint { color: var(--text-muted); font-size: 0.82rem; margin: 0 0 8px; }
.settings-label { font-size: 0.78rem; font-weight: 700; color: var(--text-muted); margin: 10px 0 6px; }

.segmented {
  display: flex; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; width: fit-content;
}
.segmented button {
  padding: 8px 16px; border: none; background: var(--bg); color: var(--text-muted);
  font-weight: 600; font-size: 0.88rem; cursor: pointer; transition: background-color 0.12s ease, color 0.12s ease;
}
.segmented button + button { border-left: 1px solid var(--border); }
.segmented button.active { background: var(--accent-grad); color: white; }

/* Files filter has more options than a typical segmented control (fits
   fine on desktop as one row, but needs to wrap into pills on mobile
   instead of overflowing or getting clipped). */
.files-filter {
  flex-wrap: wrap; width: auto; border: none; border-radius: 0; gap: 8px;
}
.files-filter button {
  border-radius: 999px; border: 1px solid var(--border) !important;
  background: var(--bg-elevated);
}
.files-filter button.active { border-color: transparent !important; }

.file-row { align-items: center; }
.file-info { display: flex; align-items: center; gap: 12px; min-width: 0; }
.file-icon { font-size: 1.5rem; flex-shrink: 0; }
.file-thumb {
  width: 40px; height: 40px; object-fit: cover; border-radius: 8px; flex-shrink: 0;
  background: var(--bg);
}
.file-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.file-action-btn {
  width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 0.95rem; cursor: pointer;
  display: flex; align-items: center; justify-content: center; line-height: 1;
  transition: background-color 0.12s ease, transform 0.12s ease;
}
.file-action-btn:hover { background: var(--border); }
.file-action-btn:active { transform: scale(0.9); }
.file-action-btn.view { background: var(--accent-grad); color: white; border-color: transparent; }
.file-action-btn.delete { color: #e5484d; }

.accent-swatch-grid { display: flex; gap: 10px; flex-wrap: wrap; }
.accent-swatch {
  width: 34px; height: 34px; border-radius: 50%; cursor: pointer;
  border: 2px solid transparent; transition: transform 0.12s ease, border-color 0.12s ease;
}
.accent-swatch:hover { transform: scale(1.1); }
.accent-swatch.selected { border-color: var(--text); }

.custom-color-row { display: flex; align-items: center; gap: 12px; }
.custom-color-row input[type="color"] {
  width: 40px; height: 40px; border: none; border-radius: 50%; padding: 0; cursor: pointer; background: none;
}
.custom-color-preview {
  flex: 1; height: 16px; border-radius: 999px; border: 1px solid var(--border);
}

.settings-links { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }
.settings-links .btn { justify-content: flex-start; }

/* Files dropzone */
.dropzone {
  border: 2px dashed var(--border); border-radius: var(--radius);
  padding: 40px 20px; text-align: center; color: var(--text-muted); cursor: pointer;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}
.dropzone.drag { border-color: var(--accent); background: var(--bg); }
.dropzone .rules { font-size: 0.8rem; }

/* FullCalendar theme integration */
#calendar {
  --fc-border-color: var(--border);
  --fc-page-bg-color: var(--bg-elevated);
  --fc-neutral-bg-color: var(--bg);
  --fc-list-event-hover-bg-color: var(--border);
  --fc-today-bg-color: color-mix(in srgb, var(--accent) 12%, transparent);
  --fc-button-bg-color: var(--bg);
  --fc-button-border-color: var(--border);
  --fc-button-text-color: var(--text);
  --fc-button-active-bg-color: var(--accent);
  --fc-button-active-border-color: var(--accent);
  --fc-button-hover-bg-color: var(--border);
  color: var(--text);
}
#calendar .fc-toolbar-title { color: var(--text); font-size: 1.3rem; }
#calendar .fc-col-header-cell-cushion,
#calendar .fc-daygrid-day-number { color: var(--text-muted); }
#calendar a { text-decoration: none; }
#calendar .fc-toolbar { flex-wrap: wrap; row-gap: 10px; }

/* FullCalendar measures its container once via JS and sets an explicit
   inline pixel width on .fc-scrollgrid-sync-table (and sizes columns off
   that) to keep header/body in sync. If that measurement is ever taken
   before layout has fully settled — seen on mobile, at any breakpoint —
   it locks in a too-wide value, pushing Saturday/Sunday off-screen with no
   visible scrollbar and no way to reach them at all. Previously this was
   only overridden inside the <640px media query, which meant it silently
   depended on the media query's own viewport read being correct at the
   exact moment — the same kind of stale-measurement race it was meant to
   fix. Applying it unconditionally (desktop included, where it's a no-op
   since FullCalendar's own measurement is normally already correct there)
   removes that dependency entirely.
   Forcing width on the tables alone isn't enough: FullCalendar renders an
   *empty* <colgroup></colgroup> (no <col> children at all), so with
   table-layout:fixed the column widths come from the first row's own
   cells instead — meaning the header/day cells' own (also stale/inline)
   widths were still what actually determined column widths regardless of
   the table's width. Forcing every header/day cell to an equal 1/7 share
   directly is what actually fixes the column widths. On top of that, the
   whole body table is wrapped in a plain <div class="fc-daygrid-body">
   that ALSO carries its own stale inline pixel width — a div, not a
   table, so table-layout:fixed doesn't touch it at all; without
   overriding this wrapper too, the 100%-width table inside it just
   resolves 100% of that stale (too-wide) div, and the overflow returns
   one level up. */
#calendar .fc-scrollgrid,
#calendar .fc-scrollgrid table,
#calendar .fc-scrollgrid-sync-table,
#calendar .fc-col-header,
#calendar .fc-daygrid-body {
  table-layout: fixed !important;
  width: 100% !important;
  min-width: 0 !important;
}
#calendar .fc-col-header-cell,
#calendar .fc-daygrid-day {
  width: 14.2857% !important;
}

/* Day number moves to the top-left (FullCalendar defaults to top-right via
   flex-direction: row-reverse), so it never overlaps the always-visible "+"
   button we add at top-right. */
#calendar .fc-daygrid-day-top { flex-direction: row; justify-content: flex-start; }

/* Clear "today" indicator: a solid accent-colored badge around today's date
   number (matches the pattern from Google/Apple Calendar), not just a faint
   background tint on the whole cell. */
#calendar .fc-day-today .fc-daygrid-day-number {
  background: var(--accent-grad);
  color: white;
  font-weight: 800;
  width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  margin: 2px;
  padding: 0;
}

@media (max-width: 640px) {
  /* Tightened from the original 40/16/16 — every pixel reclaimed here is a
     pixel the actual month grid gets instead, letting more of it fit above
     the fold before scrolling kicks in. */
  .page-hero { padding: 22px 16px 8px; }
  .page-hero h1 { font-size: 1.5rem; margin: 0 0 4px; }
  .page-hero p { font-size: 0.85rem; }
  /* The greeting ("— Guten Morgen, Otilia 💛") was an easy-to-miss inline
     span next to a big bold "Wir" heading — give it its own line and real
     weight so the personalization is actually visible, not just present. */
  .user-badge {
    display: block; margin-top: 2px; font-size: 0.95rem; font-weight: 700; color: var(--text);
  }
  .tab-bar { display: none; }
  /* Icon-only, floating pill — labels under each emoji were redundant (the
     emoji alone is already recognizable after the first use) and made the
     bar feel like a cramped strip; a rounded floating island reads more
     like a native app's tab bar. */
  .bottom-tab-bar {
    display: flex; position: fixed; left: 14px; right: 14px;
    bottom: calc(12px + env(safe-area-inset-bottom)); z-index: 70;
    background: var(--bg-elevated); border: 1px solid var(--border);
    border-radius: 999px; box-shadow: var(--shadow); padding: 6px; gap: 4px;
  }
  .bottom-tab-bar .tab-btn {
    flex: 1; display: flex; align-items: center; justify-content: center;
    background: none; border: none; border-radius: 999px; color: var(--text-muted);
    font-size: 1.3rem; line-height: 1; padding: 10px 0;
    transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
  }
  .bottom-tab-bar .tab-btn .icon { font-size: 1.3rem; line-height: 1; }
  .bottom-tab-bar .tab-btn.active { background: var(--accent-grad); color: white; transform: scale(1.06); }
  .bottom-tab-bar .tab-btn:hover:not(.active) { background: var(--border); }
  .today-label { margin: 0 0 2px; font-size: 0.8rem; }
  .calendar-card { padding: 10px 4px; }
  .calendar-toolbar { padding: 8px 4px; }
  #calendar .fc-toolbar { justify-content: center; }
  #calendar .fc-toolbar-title { font-size: 1.05rem; }
  #calendar .fc-button { padding: 5px 8px; font-size: 0.82rem; }
  #calendar .fc-daygrid-day-number { font-size: 0.75rem; padding: 2px 3px; }
  #calendar .fc-col-header-cell-cushion { font-size: 0.68rem; padding: 4px 1px; }
  #calendar .fc-daygrid-day-frame { padding: 0; min-width: 0; }
  /* Time prefix ("19 Uhr Date Night") ate into the little width a day cell
     has on a phone, often leaving just 2-3 letters of the actual title
     before the ellipsis. Dropping the time here (still shown in Woche/Tag/
     Liste views, and in the quick-view popover) gives the title the space. */
  #calendar .fc-event-time { display: none; }
  /* Previously hard-truncated to a single line with "…", which on a narrow
     day cell often left only a couple of letters visible — barely readable.
     Wrapping onto up to 2 lines (day cells already grow to fit their tallest
     event, so this doesn't break the grid) shows far more of the actual
     title before anything gets cut off, and the font is a touch bigger too. */
  #calendar .fc-event-title {
    font-size: 0.78rem; font-weight: 700; line-height: 1.25;
    white-space: normal;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  /* !important: the unconditional #calendar .fc-event rule further down
     this file has equal specificity and comes later in source order, so
     without !important it would silently win here too and this override
     would do nothing (same class of bug as the fc-scrollgrid fix above). */
  #calendar .fc-daygrid-event { padding: 1px 4px !important; }
  #calendar .fc-daygrid-day-events { margin: 0; }

}
#calendar .fc-event {
  border-radius: 6px; border: none; padding: 2px 6px; cursor: pointer;
  font-weight: 600; transition: transform 0.1s ease, box-shadow 0.1s ease;
}
#calendar .fc-daygrid-event:hover { transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0,0,0,0.18); }
#calendar .fc-button { text-transform: capitalize; box-shadow: none !important; }
#calendar .fc-daygrid-day-top { position: relative; }
#calendar .fc-daygrid-day-frame { cursor: pointer; }

/* Modal */
/* The backdrop is position:fixed, but overflow:hidden alone doesn't fully
   pin the page behind it on iOS — a touch-drag (e.g. the swipe-to-dismiss
   gesture) could still rubber-band/shift the background visibly underneath
   the sheet. position:fixed on body itself is what actually locks it; the
   matching `top` offset (restoring the true scroll position) is set/cleared
   in app.js's updateBodyScrollLock, since it depends on scrollY at lock time. */
body.modal-open { position: fixed; left: 0; right: 0; overflow: hidden; }
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.5);
  display: flex; align-items: center; justify-content: center;
  z-index: 100; padding: 20px;
  animation: backdropIn 0.2s ease both;
}
@keyframes backdropIn { from { opacity: 0; } to { opacity: 1; } }
.modal {
  width: 100%; max-width: 460px; padding: 28px;
  max-height: 90vh; overflow-y: auto;
  animation: modalIn 0.28s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes modalIn {
  from { opacity: 0; transform: translateY(16px) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.modal-handle { display: none; }
.modal h2 { margin: 0 0 20px; font-size: 1.3rem; }
#event-form { display: flex; flex-direction: column; gap: 10px; }
#event-form label { font-size: 0.82rem; font-weight: 600; color: var(--text-muted); margin-top: 6px; }
#event-form input:not([type="checkbox"]), #event-form textarea, #event-form select {
  padding: 10px 14px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 0.95rem; font-family: inherit;
  width: 100%;
}
#event-form select { appearance: auto; cursor: pointer; }
.checkbox-label {
  display: flex !important; flex-direction: row !important; align-items: center; gap: 8px !important;
  cursor: pointer; margin-top: 4px !important;
}
.checkbox-label input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; }
.row { display: flex; gap: 12px; }
.row > div { flex: 1; min-width: 0; }

/* Ort/Wiederholung/Erinnerung/Notizen are collapsed by default (see app.js)
   so the common "quick add" case fits one mobile screen without scrolling. */
#more-options { display: flex; flex-direction: column; gap: 10px; }
.more-options-toggle {
  background: none; border: none; padding: 8px 2px; margin-top: 2px;
  color: var(--accent); font-weight: 700; font-size: 0.85rem; text-align: left;
  cursor: pointer; align-self: flex-start;
}
.more-options-toggle:hover { text-decoration: underline; }

.color-picker { display: flex; gap: 8px; flex-wrap: wrap; }
.color-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px; border-radius: 999px; border: 1.5px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 0.82rem; font-weight: 600;
  cursor: pointer; font-family: inherit;
  transition: transform 0.12s ease, border-color 0.12s ease, background-color 0.12s ease;
}
.color-chip .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--chip-color); flex-shrink: 0; }
.color-chip:hover { transform: translateY(-1px); }
.color-chip.selected {
  border-color: var(--chip-color);
  background: color-mix(in srgb, var(--chip-color) 16%, transparent);
}

/* Sticky so Abbrechen/Speichern stay reachable no matter how tall the form
   gets (allDay/location/recurrence/reminder fields can push content below
   the fold on small screens) — bleeds to the edges of .modal's own padding
   and repaints its background so scrolled-under content doesn't show through. */
.modal-actions {
  display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap;
  position: sticky; bottom: -28px; margin: 16px -28px -28px; padding: 16px 28px;
  background: var(--bg-elevated); border-top: 1px solid var(--border); z-index: 2;
}
.modal-actions #delete-btn, .modal-actions #view-delete-btn { margin-right: auto; color: #e5484d; }

/* Quick-view popover */
.view-header { display: flex; align-items: center; gap: 10px; margin: 0 0 6px; }
.view-header h2 { margin: 0; font-size: 1.25rem; }
.view-color-dot { width: 14px; height: 14px; border-radius: 50%; flex-shrink: 0; }
.view-time { color: var(--text-muted); font-weight: 600; margin: 0 0 12px; }
.view-detail {
  display: flex; align-items: center; gap: 10px; padding: 10px 0;
  border-top: 1px solid var(--border); font-size: 0.92rem;
}
.view-notes { padding-top: 12px; }
.view-notes p { margin: 4px 0 0; white-space: pre-wrap; color: var(--text-muted); font-size: 0.92rem; line-height: 1.5; }

/* Day-events list (shown when tapping a day that already has events) */
.day-list-card { max-width: 440px; overflow: hidden; }
.day-list-card h2 { margin: 0 0 12px; font-size: 1.15rem; text-transform: capitalize; }
/* Swiping left/right in navigateDayList() briefly slides the outgoing day's
   content out one side and the new day's content in from the other, echoing
   the swipe direction — an instant content swap read as a glitch rather than
   a deliberate "go to next/previous day" navigation. */
.day-list-content { transition: transform 0.18s ease, opacity 0.18s ease; }
.day-list-content.slide-out-left { transform: translateX(-28px); opacity: 0; }
.day-list-content.slide-out-right { transform: translateX(28px); opacity: 0; }
.day-list-items { display: flex; flex-direction: column; gap: 8px; max-height: 50vh; overflow-y: auto; }
.day-list-item {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: 12px; background: var(--bg-elevated);
  cursor: pointer; transition: background-color 0.12s ease, transform 0.1s ease;
}
.day-list-item:hover { background: var(--border); transform: translateY(-1px); }
.day-list-item-color { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
.day-list-item-text { min-width: 0; flex: 1; }
.day-list-item-title { font-weight: 700; font-size: 0.95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.day-list-item-time { font-size: 0.8rem; color: var(--text-muted); margin-top: 2px; }

/* File preview modal */
.file-preview-card { max-width: 640px; padding: 16px; }
.file-preview-header {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px;
}
.file-preview-name { font-weight: 700; font-size: 0.92rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-preview-close {
  flex-shrink: 0; width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); cursor: pointer; font-size: 0.9rem;
  display: flex; align-items: center; justify-content: center;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
.file-preview-close:hover { background: var(--border); border-color: var(--accent); }
.file-preview-body {
  display: flex; align-items: center; justify-content: center;
  max-height: 75vh; overflow: auto; border-radius: 12px; background: var(--bg);
}
.file-preview-body img { max-width: 100%; max-height: 75vh; display: block; object-fit: contain; }
.file-preview-body video { max-width: 100%; max-height: 75vh; display: block; }
.file-preview-body audio { width: 100%; margin: 30px 16px; }
.file-preview-body iframe { width: 100%; height: 75vh; border: none; }

/* Bottom-sheet on mobile: full-width, slides up from the bottom, rounded
   top corners only, with a small drag handle for a native-app feel. Inputs
   are larger (min 44px touch target per Apple/Google guidance). */
@media (max-width: 640px) {
  .modal-backdrop { align-items: flex-end; padding: 0; }
  .modal {
    max-width: 100%; border-radius: 20px 20px 0 0; padding: 12px 20px 24px;
    max-height: 85vh;
    animation: sheetIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) both;
  }
  @keyframes sheetIn {
    from { transform: translateY(100%); }
    to { transform: translateY(0); }
  }
  .modal-handle {
    display: block; width: 40px; height: 4px; border-radius: 999px;
    background: var(--border); margin: 0 auto 16px;
  }
  #event-form input, #event-form select, #event-form textarea { font-size: 16px; padding: 12px 14px; }
  /* Start/Ende stack vertically on mobile (like every other field) — an
     earlier attempt kept them side-by-side to save vertical space, but
     native datetime-local/date inputs have a minimum rendered width that
     doesn't respect a cramped ~160px column, which pushed them wider than
     the screen and caused horizontal scrolling. Scrolling a bit further
     down is a much smaller problem than scrolling sideways. */
  .row { flex-direction: column; gap: 10px; }
  .modal-actions {
    flex-direction: column-reverse;
    bottom: -24px; margin: 16px -20px -24px; padding: 14px 20px 20px;
  }
  .modal-actions .btn { width: 100%; justify-content: center; }
  .modal-actions #delete-btn { margin-right: 0; }
}

/* Optional animated background (Settings → Erscheinungsbild → Hintergrund):
   large blurred blobs that drift, continuously morph their own outline
   (lava-lamp/slime look), AND cycle hue — so the same accent gradient
   plays across a range of related tones instead of staying one fixed pair
   of colors. Off by default — a deliberate opt-in, not a surprise. Cards
   stay fully opaque (an earlier glassmorphism version made them
   translucent too, but that was its own separate thing bolted onto this —
   removed again, this is just the background itself). */
:root.animated-bg-on, :root.animated-bg-on body { background: transparent; }
.bg-glow {
  display: none;
  position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none;
}
:root.animated-bg-on .bg-glow { display: block; }
.bg-glow span {
  position: absolute; width: 62vmax; height: 62vmax;
  background: var(--accent-grad); filter: blur(85px) hue-rotate(0deg); opacity: 0.45;
  border-radius: 42% 58% 70% 30% / 45% 45% 55% 55%;
}
/* Dark mode gets a completely different treatment: a static (no motion)
   multi-point gradient mesh instead of the drifting animated blobs — a
   deliberately calmer, genuinely dark-feeling background rather than the
   light-mode design's bright motion just recolored. Four soft pools of
   color anchored near the corners plus one at center, each fading out
   long before it reaches the next, read as pooled liquid light rather
   than a moving lava lamp. Static also means no continuous repaint/
   compositing cost on a phone. */
@media (prefers-color-scheme: dark) {
  :root.animated-bg-on:not([data-theme="light"]) .bg-glow { display: none; }
  :root.animated-bg-on:not([data-theme="light"]) body {
    background:
      radial-gradient(circle at 10% 10%, color-mix(in srgb, var(--accent) 32%, transparent), transparent 42%),
      radial-gradient(circle at 90% 15%, color-mix(in srgb, var(--accent-2) 26%, transparent), transparent 42%),
      radial-gradient(circle at 85% 90%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 48%),
      radial-gradient(circle at 12% 88%, color-mix(in srgb, var(--accent-2) 22%, transparent), transparent 48%),
      radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--accent) 12%, transparent), transparent 55%),
      var(--bg);
  }
}
:root[data-theme="dark"].animated-bg-on .bg-glow { display: none; }
:root[data-theme="dark"].animated-bg-on body {
  background:
    radial-gradient(circle at 10% 10%, color-mix(in srgb, var(--accent) 32%, transparent), transparent 42%),
    radial-gradient(circle at 90% 15%, color-mix(in srgb, var(--accent-2) 26%, transparent), transparent 42%),
    radial-gradient(circle at 85% 90%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 48%),
    radial-gradient(circle at 12% 88%, color-mix(in srgb, var(--accent-2) 22%, transparent), transparent 48%),
    radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--accent) 12%, transparent), transparent 55%),
    var(--bg);
}

.bg-glow span:nth-child(1) { top: -22%; left: -18%; animation: blob1 24s ease-in-out infinite alternate; }
.bg-glow span:nth-child(2) { bottom: -26%; right: -18%; animation: blob2 29s ease-in-out infinite alternate; }
.bg-glow span:nth-child(3) {
  top: 32%; left: 22%; width: 48vmax; height: 48vmax;
  opacity: 0.32;
  animation: blob3 20s ease-in-out infinite alternate;
}
@keyframes blob1 {
  0% { border-radius: 42% 58% 70% 30% / 45% 45% 55% 55%; transform: translate(0, 0) scale(1) rotate(0deg); filter: blur(85px) hue-rotate(0deg); }
  50% { border-radius: 70% 30% 46% 54% / 30% 64% 36% 70%; transform: translate(16%, 12%) scale(1.18) rotate(24deg); filter: blur(85px) hue-rotate(35deg); }
  100% { border-radius: 34% 66% 62% 38% / 60% 40% 60% 40%; transform: translate(6%, 18%) scale(1.05) rotate(-12deg); filter: blur(85px) hue-rotate(-25deg); }
}
@keyframes blob2 {
  0% { border-radius: 60% 40% 30% 70% / 50% 60% 40% 50%; transform: translate(0, 0) scale(1) rotate(0deg); filter: blur(85px) hue-rotate(20deg); }
  50% { border-radius: 30% 70% 65% 35% / 65% 35% 65% 35%; transform: translate(-14%, -16%) scale(1.22) rotate(-18deg); filter: blur(85px) hue-rotate(-30deg); }
  100% { border-radius: 55% 45% 40% 60% / 40% 55% 45% 60%; transform: translate(-6%, -8%) scale(1.08) rotate(12deg); filter: blur(85px) hue-rotate(45deg); }
}
@keyframes blob3 {
  0% { border-radius: 50% 50% 40% 60% / 55% 45% 55% 45%; transform: translate(0, 0) scale(1); filter: blur(85px) hue-rotate(60deg); }
  50% { border-radius: 65% 35% 60% 40% / 40% 60% 40% 60%; transform: translate(12%, -12%) scale(1.28); filter: blur(85px) hue-rotate(100deg); }
  100% { border-radius: 40% 60% 50% 50% / 60% 40% 60% 40%; transform: translate(-10%, 8%) scale(0.94); filter: blur(85px) hue-rotate(70deg); }
}
@media (prefers-reduced-motion: reduce) {
  .bg-glow span { animation: none; }
}
