/* Personal Dashboard - Stylesheet
 * A polished, calm, "glanceable at 7am" aesthetic
 */

/* CSS Custom Properties - Design System */
:root {
  /* Light theme (default) */
  --bg-primary: #F6F7F9;
  --bg-card: #FFFFFF;
  --text-primary: #1A202C;
  --text-muted: #64748B;
  --border-color: #E2E8F0;

  /* Accent */
  --accent: #14B8A6;
  --accent-hover: #0D9488;

  /* Status */
  --status-up: #22C55E;
  --status-down: #EF4444;
  --status-warning: #F59E0B;

  /* Delta indicators (prices: up is bad, down is good) */
  --delta-increase: #EF4444;
  --delta-decrease: #22C55E;

  /* Spacing */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;

  /* Border radius */
  --drop-target-bg: rgba(20, 184, 166, 0.08);
  --drop-target-border: var(--accent);
  --palette-bg: #F0F1F4;

  /* The layout pill in the header: a shade off the header so it reads as
     a control, not a second title. */
  --layout-switch-bg: #EEF2F7;
  --layout-switch-border: #D3DAE5;

  --radius-sm: 6px;
  --radius-md: 12px;
  --radius-lg: 16px;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);

  /* Typography */
  --font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-size-xs: 0.75rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;

  /* Transitions */
  --transition-fast: 150ms ease;
  --transition-normal: 250ms ease;

  /* Tooltips: inverted from the page, so they read as a label floating
     over it rather than another card. */
  --tooltip-bg: #1A202C;
  --tooltip-text: #FFFFFF;
}

/* Dark theme */
[data-theme="dark"] {
  --drop-target-bg: rgba(20, 184, 166, 0.16);
  --palette-bg: #161B22;
  --layout-switch-bg: #232B3B;
  --layout-switch-border: #34405A;
  --bg-primary: #0F1420;
  --bg-card: #1A2130;
  --text-primary: #E8ECF4;
  --text-muted: #8B96A8;
  --border-color: #2A3348;
  --tooltip-bg: #E8ECF4;
  --tooltip-text: #1A202C;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -2px rgba(0, 0, 0, 0.2);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -4px rgba(0, 0, 0, 0.2);
}

/* Reset */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* The hidden attribute must actually hide. The browser rule for it is
   display:none, which loses to any class that sets display -- .btn sets
   flex -- so a hidden button was still on screen and still clickable. */
[hidden] {
  display: none !important;
}

/* Base styles */
html {
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  font-family: var(--font-family);
  font-size: var(--font-size-base);
  line-height: 1.5;
  color: var(--text-primary);
  background-color: var(--bg-primary);
  min-height: 100vh;
}

/* Typography */
h1, h2, h3, h4, h5, h6 {
  font-weight: 600;
  line-height: 1.25;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* Focus styles for accessibility */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Header */
.header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg-card);
  border-bottom: 1px solid var(--border-color);
  padding: var(--space-md) var(--space-lg);
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

.header__title {
  font-size: var(--font-size-lg);
  font-weight: 600;
  color: var(--text-primary);
}

/* The title and, beside it, the layout on show. */
.header__brand {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
  flex-shrink: 0;
}

/* The layout selector: a pill naming the arrangement on show, opening a
   menu of the others. Styled as a control and not a heading, since a
   heading is not something one expects to press. */
.layout-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  max-width: 14rem;
  padding: 2px var(--space-sm) 2px var(--space-md);
  border: 1px solid var(--layout-switch-border);
  border-radius: 999px;
  background: var(--layout-switch-bg);
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--font-size-sm);
  font-weight: 500;
  line-height: 1.6;
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}

.layout-switch:hover,
.layout-switch:focus-visible {
  border-color: var(--accent);
}

.layout-switch__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.layout-switch__chevron {
  flex-shrink: 0;
  color: var(--text-muted);
}

/* On a phone the search bar takes a row of its own under the title and the
   buttons, rather than all three fighting for one; the pill keeps its name
   short so the title stays on the line. */
@media (max-width: 767px) {
  .header {
    flex-wrap: wrap;
    gap: var(--space-sm) var(--space-md);
  }

  .header__search {
    order: 3;
    flex-basis: 100%;
    max-width: none;
  }

  .layout-switch {
    max-width: 8rem;
  }
}

/* Search Results Dropdown */
.search-results {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  max-height: 400px;
  overflow-y: auto;
  z-index: 200;
  margin-top: var(--space-xs);
}

.search-results__empty {
  padding: var(--space-md);
  text-align: center;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

.search-results__group {
  padding: var(--space-sm) 0;
}

.search-results__group:not(:last-child) {
  border-bottom: 1px solid var(--border-color);
}

.search-results__header {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-size-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.search-results__item {
  display: block;
  padding: var(--space-sm) var(--space-md);
  text-decoration: none;
  color: var(--text-primary);
  cursor: pointer;
  transition: background-color var(--transition-fast);
}

.search-results__item:hover {
  background: var(--bg-primary);
}

.search-results__title {
  display: block;
  font-weight: 500;
  font-size: var(--font-size-sm);
}

.search-results__snippet {
  display: block;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  margin-top: 2px;
}

/* Highlight animation for todo scroll-to */
.highlight {
  animation: highlight-pulse 2s ease-out;
}

@keyframes highlight-pulse {
  0% { background-color: var(--accent); }
  100% { background-color: transparent; }
}

.header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-left: auto;
}

/* Search input */
.search-input {
  width: 100%;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-primary);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  transition: border-color var(--transition-fast);
}

.search-input:focus {
  border-color: var(--accent);
  outline: none;
}

/* Card grid */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg);
  padding: var(--space-lg);
  max-width: 1400px;
  margin: 0 auto;
}

/* A card may span up to the full grid width. --card-span is set inline per
   card; the fallback keeps a card one column wide if it is ever missing. */
.card {
  grid-column: span min(var(--card-span, 1), 3);
}

@media (max-width: 1199px) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  /* Spans clamp to the columns that exist, or a wide card overflows the grid
     instead of wrapping. */
  .card {
    grid-column: span min(var(--card-span, 1), 2);
  }
}

@media (max-width: 699px) {
  .card-grid {
    grid-template-columns: 1fr;
  }
  .card {
    grid-column: span 1;
  }
}

/* ============================================================
   Pager: paging between layouts
   ============================================================ */

/* One dot per page of the layout between two chevrons, centred under the
   header. Drawn by dashboard-pages.js and hidden while the layout has one
   page and nothing is being edited, so a single page sees nothing new; in
   edit mode a ＋ after the last dot adds a page. The grid's own padding
   follows, so the strip sits between header and cards without adding a gap
   of its own below. */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  max-width: 1400px;
  margin: 0 auto;
  padding: var(--space-sm) var(--space-lg) 0;
}

.pager__dots {
  display: flex;
  align-items: center;
}

/* The dot itself is small; the button around it is not, so a finger and a
   pointer both find it. The dot is drawn as a pseudo-element so the button
   can be any size without the dot growing with it. */
.pager__dot {
  display: flex;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
}

.pager__dot::before {
  content: '';
  display: block;
  width: 8px;
  height: 8px;
  margin: auto;
  border-radius: 50%;
  background: var(--text-muted);
  opacity: 0.6;
  transition: transform 0.15s ease, opacity 0.15s ease, background-color 0.15s ease;
}

.pager__dot:hover::before,
.pager__dot:focus-visible::before {
  opacity: 0.9;
}

.pager__dot[aria-current]::before {
  background: var(--accent);
  opacity: 1;
  transform: scale(1.25);
}

.pager__dot:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.pager__arrow:disabled {
  opacity: 0.3;
  cursor: default;
}

/* The ＋ sits with the dots, a touch apart from the last, and stays last
   however a dragged dot lands beside it. */
.pager__add {
  order: 1;
  margin-left: var(--space-xs);
  color: var(--text-muted);
}

/* A dot can be dragged into a new place while editing. */
.pager__dot[draggable="true"] {
  cursor: grab;
}

.pager__dot.dragging {
  opacity: 0.4;
}

@media (hover: none) and (pointer: coarse) {
  .pager__dot {
    width: 40px;
    height: 44px;
  }
}

/* The slide between pages: the old cards drift out the way the swipe went
   and the new ones follow in from the other side. --page-shift carries the
   direction, set by the script before each run. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes page-leave {
    from { transform: translateX(0); opacity: 1; }
    to { transform: translateX(var(--page-shift, -32px)); opacity: 0; }
  }

  @keyframes page-enter {
    from { transform: translateX(calc(-1 * var(--page-shift, -32px))); opacity: 0; }
    to { transform: translateX(0); opacity: 1; }
  }

  .card-grid--leaving {
    animation: page-leave 0.16s ease-in forwards;
  }

  .card-grid--entering {
    animation: page-enter 0.2s ease-out;
  }
}

/* Widgets sit inside a card and must never widen it. min-width: 0 is the
   load-bearing declaration: grid and flex children default to min-width: auto
   and will otherwise push past their track. */
.widget {
  min-width: 0;
  max-width: 100%;
}

.widget__body > * {
  min-width: 0;
  max-width: 100%;
}

/* Wide content inside a widget scrolls itself rather than the page. */
.widget__body table,
.widget__body pre {
  display: block;
  overflow-x: auto;
}

/* A card holding more than one widget separates them. */
.card .widget + .widget {
  border-top: 1px solid var(--border-color);
}

.widget-footer {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  text-align: right;
  padding: var(--space-sm) var(--space-md);
}

/* Oil Prices: one row per benchmark or port and grade — the name and a grade
   badge, a sparkline of the trend window, the price, then the unit and the
   day's range under the name and the day's change under the price. A grid
   with named areas, so the sparkline spans both lines. The colours follow the
   other price widgets: a rise is --delta-increase, whatever the theme. */
.oil-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.oil-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 4.5rem auto;
  grid-template-areas:
    "name spark price"
    "meta spark change";
  column-gap: var(--space-sm);
  align-items: center;
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-primary);
  border-radius: var(--radius-sm);
}

.oil-row--empty {
  opacity: 0.7;
}

.oil-name {
  grid-area: name;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--font-size-sm);
}

.oil-grade {
  display: inline-block;
  margin-left: var(--space-xs);
  padding: 0 var(--space-xs);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  vertical-align: middle;
}

.oil-meta {
  grid-area: meta;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.oil-price {
  grid-area: price;
  text-align: right;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.oil-change {
  grid-area: change;
  min-height: 1em;
  text-align: right;
  font-size: var(--font-size-xs);
  font-variant-numeric: tabular-nums;
}

.oil-change.change--up { color: var(--delta-increase); }
.oil-change.change--down { color: var(--delta-decrease); }

.oil-spark {
  grid-area: spark;
  display: block;
  height: 1.75rem;
  line-height: 1.75rem;
  text-align: center;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.oil-spark svg {
  display: block;
  width: 100%;
  height: 100%;
}

.oil-spark polyline {
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.oil-spark--up { color: var(--delta-increase); }
.oil-spark--down { color: var(--delta-decrease); }

/* Exchange Rates: two lines per pair — the pair and its rate, then the
   currencies' names and the change since the previous feed day. A grid,
   so the second line's change sits under the rate whether or not the
   names beside it are long. */
.fx-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.fx-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--space-sm);
  align-items: baseline;
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-primary);
  border-radius: var(--radius-sm);
}

.fx-pair {
  font-size: var(--font-size-sm);
  font-weight: 500;
}

.fx-rate {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.fx-names {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fx-change {
  font-size: var(--font-size-xs);
  font-variant-numeric: tabular-nums;
  justify-self: end;
}

.fx-change.change--up { color: var(--delta-increase); }
.fx-change.change--down { color: var(--delta-decrease); }

/* Card */
.card {
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}

.card:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.card__header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md);
  border-bottom: 1px solid var(--border-color);
}

.card__icon {
  width: 20px;
  height: 20px;
  color: var(--accent);
}

.card__title {
  font-size: var(--font-size-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  flex: 1;
}

/* A placement's own name after the widget's: "WEATHER – Southern Africa",
   as the user typed it rather than shouted. */
.card__label {
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
}

.card__timestamp {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.card__body {
  padding: var(--space-md);
}

.card__error {
  padding: var(--space-sm) var(--space-md);
  background: rgba(239, 68, 68, 0.1);
  color: var(--status-down);
  font-size: var(--font-size-sm);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.card__stale {
  padding: var(--space-sm) var(--space-md);
  background: rgba(245, 158, 11, 0.1);
  color: var(--status-warning);
  font-size: var(--font-size-sm);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: var(--space-sm) var(--space-md);
  border: none;
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
  font-weight: 500;
  cursor: pointer;
  transition: all var(--transition-fast);
}

.btn--primary {
  background: var(--accent);
  color: white;
}

.btn--primary:hover {
  background: var(--accent-hover);
}

.btn--ghost {
  background: transparent;
  color: var(--text-muted);
}

.btn--ghost:hover {
  background: var(--border-color);
  color: var(--text-primary);
}

.btn--icon {
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: var(--radius-sm);
}

/* Tooltips
   A name for an icon-only control, shown beneath it on hover and on keyboard
   focus. Not the browser's title attribute: that takes a second to appear,
   never shows for the keyboard, and cannot be styled. The text is in
   data-tooltip, beside an aria-label carrying the same words — the accessible
   name comes from the label, and a pseudo-element is no part of it. */
[data-tooltip] {
  position: relative;
}

[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + var(--space-xs));
  left: 50%;
  transform: translate(-50%, -4px);
  padding: var(--space-xs) var(--space-sm);
  background: var(--tooltip-bg);
  color: var(--tooltip-text);
  font-size: var(--font-size-xs);
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  z-index: 300;
  /* Hiding: fade out, then stop being rendered once the fade is done. */
  transition: opacity var(--transition-fast), transform var(--transition-fast),
              visibility 0s linear 150ms;
}

[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  /* Showing: a short pause first, so a pointer crossing the row on its way
     somewhere else does not set every label flickering. */
  transition-delay: 250ms;
}

/* The last icon sits at the page edge, where a centred label would be cut
   off; it hangs left from the icon's right edge instead. */
.header__actions > [data-tooltip]:last-child::after {
  left: auto;
  right: 0;
  transform: translate(0, -4px);
}

.header__actions > [data-tooltip]:last-child:hover::after,
.header__actions > [data-tooltip]:last-child:focus-visible::after {
  transform: translate(0, 0);
}

/* A touch screen has no hover, only a tap that the browser reports as one and
   leaves in place — so the label would appear on the tap and stay until the
   next. Nothing to show it on, so nothing shown. */
@media (hover: none) {
  [data-tooltip]::after {
    display: none;
  }
}

/* Mobile: Larger touch targets (44px minimum per WCAG) */
@media (max-width: 699px) {
  .btn--icon {
    width: 44px;
    height: 44px;
  }

  .btn--small {
    min-height: 44px;
    padding: var(--space-sm) var(--space-md);
  }
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Status indicators */
.status-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}

.status-dot--up {
  background: var(--status-up);
  box-shadow: 0 0 6px var(--status-up);
}

.status-dot--down {
  background: var(--status-down);
  box-shadow: 0 0 6px var(--status-down);
}

/* Delta indicators */
.delta {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: var(--font-size-xs);
  font-weight: 500;
}

.delta--up {
  color: var(--delta-increase);
}

.delta--down {
  color: var(--delta-decrease);
}

/* Number formatting */
.number {
  font-variant-numeric: tabular-nums;
}

.number--large {
  font-size: var(--font-size-xl);
  font-weight: 600;
}

/* Badge */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 10px;
  font-size: var(--font-size-xs);
  font-weight: 600;
  background: var(--accent);
  color: white;
}

.badge--highlight {
  background: var(--status-down);
}

/* Form elements */
.form-group {
  margin-bottom: var(--space-md);
}

.form-label {
  display: block;
  margin-bottom: var(--space-xs);
  font-size: var(--font-size-sm);
  font-weight: 500;
  color: var(--text-primary);
}

.form-input {
  width: 100%;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
  color: var(--text-primary);
  font-size: var(--font-size-base);
  transition: border-color var(--transition-fast);
}

.form-input:focus {
  border-color: var(--accent);
  outline: none;
}

.form-input--error {
  border-color: var(--status-down);
}

.form-error {
  margin-top: var(--space-xs);
  font-size: var(--font-size-sm);
  color: var(--status-down);
}

/* Checkbox */
.checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  cursor: pointer;
}

.checkbox input {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
}

/* Todo item */
/* Add-task row: stretch keeps the field and the Add button the same height */
.todo-add {
  display: flex;
  align-items: stretch;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}

.todo-input {
  flex: 1;
  min-width: 0;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  font-family: inherit;
  transition: border-color var(--transition-fast);
}

.todo-input::placeholder {
  color: var(--text-muted);
}

.todo-input:focus {
  border-color: var(--accent);
  outline: none;
}

.todo-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--border-color);
}

.todo-item:last-child {
  border-bottom: none;
}

.todo-item--completed .todo-item__text {
  text-decoration: line-through;
  color: var(--text-muted);
}

.todo-item--pinned {
  background: rgba(20, 184, 166, 0.08);
  margin: 0 calc(-1 * var(--space-md));
  padding: var(--space-sm) var(--space-md);
  border-left: 3px solid var(--accent);
}

.todo-item--archived {
  background: rgba(128, 128, 128, 0.1);
  opacity: 0.7;
}

.todo-archive-toggle {
  display: flex;
  justify-content: flex-end;
  padding: var(--space-xs) 0;
  margin-bottom: var(--space-sm);
  border-bottom: 1px solid var(--border-color);
}

.todo-archive-label {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: 0.85rem;
  color: var(--text-muted);
  cursor: pointer;
}

.todo-archive-label input {
  cursor: pointer;
}

.todo-pin-indicator {
  font-size: 0.8rem;
  margin-right: var(--space-xs);
}

.btn--active {
  color: var(--accent);
}

.todo-item__text {
  flex: 1;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.todo-item__date {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

/* Todo text */
.todo-text {
  flex: 1;
  word-wrap: break-word;
  overflow-wrap: break-word;
  cursor: pointer;
}

.todo-text:hover {
  background: var(--bg-hover);
  border-radius: var(--radius-xs);
  padding: 2px 4px;
  margin: -2px -4px;
}

/* Todo actions */
.todo-actions {
  display: flex;
  gap: 2px;
  opacity: 0.5;
  transition: opacity var(--transition);
}

.todo-item:hover .todo-actions {
  opacity: 1;
}

/* Todo edit input */
.todo-edit-input {
  flex: 1;
  padding: var(--space-xs) var(--space-sm);
  border: 2px solid var(--accent);
  border-radius: var(--radius-sm);
  font-size: inherit;
  font-family: inherit;
  background: var(--bg-card);
  color: var(--text-primary);
}

.todo-edit-input:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(20, 184, 166, 0.2);
}

/* Completed todo styling */
.todo-item--completed .todo-text {
  text-decoration: line-through;
  color: var(--text-muted);
}

/* Todo checkbox */
.todo-checkbox {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
  margin-top: 2px;
  cursor: pointer;
}

/* Toast notifications */
.toast-container {
  position: fixed;
  bottom: var(--space-lg);
  right: var(--space-lg);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.toast {
  padding: var(--space-md);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  box-shadow: var(--shadow-lg);
  animation: toast-in 0.3s ease;
}

.toast--success {
  border-left: 3px solid var(--status-up);
}

.toast--error {
  border-left: 3px solid var(--status-down);
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Skeleton loading */
.skeleton {
  background: linear-gradient(90deg, var(--border-color) 25%, var(--bg-primary) 50%, var(--border-color) 75%);
  background-size: 200% 100%;
  animation: skeleton-loading 1.5s infinite;
  border-radius: var(--radius-sm);
}

@keyframes skeleton-loading {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}

/* Modal/Dialog */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.modal {
  background: var(--bg-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  max-width: 500px;
  width: 90%;
  max-height: 90vh;
  overflow-y: auto;
}

.modal__header {
  padding: var(--space-md);
  border-bottom: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.modal__title {
  font-size: var(--font-size-lg);
  font-weight: 600;
}

.modal__body {
  padding: var(--space-md);
}

.modal__footer {
  padding: var(--space-md);
  border-top: 1px solid var(--border-color);
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}

/* Empty state */
.empty-state {
  text-align: center;
  padding: var(--space-xl);
  color: var(--text-muted);
}

.empty-state__icon {
  width: 48px;
  height: 48px;
  margin: 0 auto var(--space-md);
  opacity: 0.5;
}

.empty-state__text {
  font-size: var(--font-size-sm);
}

/* Login/Setup page. The height leaves room for the site footer beneath, so
   the card stays centred and the footer still shows without scrolling. */
.auth-page {
  min-height: calc(100vh - 2.5rem);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
}

/* The footer every page ends with: the policy links and the copyright, small
   and quiet. The auth pages make room for it above. */
.site-footer {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-sm) var(--space-md);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.site-footer a {
  color: inherit;
  text-decoration: none;
}

.site-footer a:hover {
  color: var(--text-primary);
  text-decoration: underline;
}

/* The policy pages: one readable column, no chrome but the footer. */
.legal-page {
  max-width: 44rem;
  margin: 0 auto;
  padding: var(--space-xl) var(--space-lg) var(--space-lg);
  line-height: 1.6;
}

.legal-page__home {
  font-size: var(--font-size-sm);
  margin-bottom: var(--space-lg);
}

.legal-page h1 {
  font-size: var(--font-size-xl);
  margin: 0 0 var(--space-xs);
}

.legal-page h2 {
  font-size: var(--font-size-lg);
  margin: var(--space-xl) 0 var(--space-sm);
}

.legal-page p,
.legal-page ul {
  margin: 0 0 var(--space-md);
}

.legal-page ul {
  padding-left: 1.5rem;
}

/* The help page: contents down the side, one readable column beside it.
   The contents stick below the sticky header, and a section scrolled to
   stops beneath it rather than under it. */
.help-page {
  display: grid;
  grid-template-columns: 15rem minmax(0, 44rem);
  justify-content: center;
  gap: var(--space-xl);
  padding: var(--space-xl) var(--space-lg) var(--space-lg);
}

.help-nav {
  position: sticky;
  top: 5.5rem;
  align-self: start;
  max-height: calc(100vh - 7rem);
  overflow-y: auto;
  font-size: var(--font-size-sm);
}

.help-nav__toggle {
  display: none;
}

.help-nav__heading {
  margin: var(--space-lg) 0 var(--space-xs);
  font-size: var(--font-size-xs);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.help-nav__heading:first-child {
  margin-top: 0;
}

.help-nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.help-nav a {
  display: block;
  padding: 0.3rem var(--space-sm);
  border-left: 2px solid var(--border-color);
  color: var(--text-primary);
  text-decoration: none;
}

.help-nav a:hover {
  border-left-color: var(--text-muted);
}

.help-nav a[aria-current="true"] {
  border-left-color: var(--accent);
  color: var(--accent-hover);
  font-weight: 600;
}

.help-content {
  line-height: 1.6;
}

.help-content section {
  scroll-margin-top: 5.5rem;
  padding-bottom: var(--space-lg);
}

.help-content section + section {
  border-top: 1px solid var(--border-color);
  padding-top: var(--space-lg);
}

.help-content h2 {
  font-size: var(--font-size-lg);
  margin: 0 0 var(--space-sm);
}

.help-content h3 {
  font-size: var(--font-size-base);
  margin: var(--space-lg) 0 var(--space-xs);
}

.help-content p,
.help-content ul {
  margin: 0 0 var(--space-md);
}

.help-content ul {
  padding-left: 1.5rem;
}

.help-content li + li {
  margin-top: var(--space-xs);
}

.help-content code {
  font-size: 0.9em;
  padding: 0.1em 0.3em;
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
  border: 1px solid var(--border-color);
}

.help-note {
  padding: var(--space-sm) var(--space-md);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  color: var(--text-muted);
}

/* On a phone the contents fold behind a button at the top of the page. */
@media (max-width: 767px) {
  .help-page {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-md);
    padding: var(--space-md);
  }

  .help-nav {
    position: static;
    max-height: none;
    overflow: visible;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    background: var(--bg-card);
  }

  .help-nav__toggle {
    display: flex;
    width: 100%;
    justify-content: space-between;
    min-height: 44px;
  }

  .help-nav__list {
    display: none;
    padding: 0 var(--space-md) var(--space-md);
  }

  .help-nav--open .help-nav__list {
    display: block;
  }

  .help-nav__toggle svg {
    transition: transform var(--transition-fast);
  }

  .help-nav--open .help-nav__toggle svg {
    transform: rotate(180deg);
  }

  .help-content section {
    scroll-margin-top: 7rem;
  }
}

.auth-card {
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-xl);
  width: 100%;
  max-width: 400px;
}

.auth-card__title {
  font-size: var(--font-size-xl);
  font-weight: 600;
  text-align: center;
  margin-bottom: var(--space-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
}

/* The mark beside the name, on the sign-in card and in the dashboard header. */
.auth-card__logo,
.header__logo {
  flex-shrink: 0;
  border-radius: 22%;
}

.auth-card__logo {
  width: 36px;
  height: 36px;
}

.header__logo {
  width: 24px;
  height: 24px;
  vertical-align: -5px;
  margin-right: var(--space-xs);
}

/* What the site is, said once on the sign-in card for whoever arrives with
   no account: a few quiet lines between the title and the form. */
.auth-card__intro {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  text-align: center;
  line-height: 1.5;
  margin: calc(-1 * var(--space-sm)) 0 var(--space-lg);
}

/* Utility classes */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.text-muted {
  color: var(--text-muted);
}

.text-sm {
  font-size: var(--font-size-sm);
}

.mt-sm { margin-top: var(--space-sm); }
.mt-md { margin-top: var(--space-md); }
.mb-sm { margin-bottom: var(--space-sm); }
.mb-md { margin-bottom: var(--space-md); }

/* Health card */
.health-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.health-item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-primary);
  border-radius: var(--radius-sm);
  border-left: 3px solid transparent;
}

.health-item--up {
  border-left-color: var(--status-up);
}

.health-item--down {
  border-left-color: var(--status-down);
}

.health-status {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: bold;
  color: white;
}

.health-status--up {
  background: var(--status-up);
  box-shadow: 0 0 6px var(--status-up);
}

.health-status--down {
  background: var(--status-down);
  box-shadow: 0 0 6px var(--status-down);
}

.health-up-indicator {
  font-size: var(--font-size-xs);
  color: var(--status-up);
  background: rgba(34, 197, 94, 0.1);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  font-weight: 600;
}

.health-name {
  flex: 1;
  color: var(--text-primary);
  text-decoration: none;
  font-weight: 500;
}

.health-name:hover {
  color: var(--accent);
}

.health-error {
  font-size: var(--font-size-xs);
  color: var(--status-down);
  background: rgba(239, 68, 68, 0.1);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
}

.health-down-since {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  margin-left: auto;
  white-space: nowrap;
}

/* 24h History Strip */
.health-history {
  display: flex;
  gap: 2px;
  margin-top: var(--space-xs);
  height: 16px;
  align-items: center;
}

.health-history-bar {
  flex: 1;
  min-width: 4px;
  max-width: 8px;
  height: 100%;
  border-radius: 2px;
  transition: transform 0.15s ease, opacity 0.15s ease;
  cursor: pointer;
}

.health-history-bar--up {
  background: var(--status-up);
}

.health-history-bar--down {
  background: var(--status-down);
}

.health-history-bar:hover {
  transform: scaleY(1.3);
  opacity: 0.8;
}

.health-history-empty {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-style: italic;
}

.loading-state {
  color: var(--text-muted);
  text-align: center;
  padding: var(--space-md);
}

/* Refresh button spinning animation */
.refreshing {
  animation: spin 1s linear infinite;
}

.refreshing svg {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

/* Fuel Prices card */
.fuel-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.fuel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--border-color);
  margin-bottom: var(--space-sm);
}

.fuel-effective {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  font-weight: 500;
}

.stale-indicator {
  font-size: var(--font-size-xs);
  color: var(--status-warning);
  cursor: help;
}

.fuel-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.fuel-item {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  background: var(--bg-primary);
  border-radius: var(--radius-sm);
}

.fuel-name {
  font-size: var(--font-size-sm);
  color: var(--text-primary);
}

.fuel-price {
  font-size: var(--font-size-base);
  font-weight: 600;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.fuel-change {
  font-size: var(--font-size-xs);
  font-weight: 500;
  min-width: 60px;
  text-align: right;
}

.fuel-change.change--up {
  color: var(--delta-increase);
}

.fuel-change.change--down {
  color: var(--delta-decrease);
}

.fuel-footer {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  text-align: right;
  padding-top: var(--space-xs);
  border-top: 1px solid var(--border-color);
  margin-top: var(--space-sm);
}


.health-footer {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  text-align: right;
  padding-top: var(--space-xs);
  border-top: 1px solid var(--border-color);
  margin-top: var(--space-sm);
}

/* Email card */
.email-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.mailbox-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.mailbox-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-primary);
  border-radius: var(--radius-sm);
  border-left: 3px solid var(--accent);
}

.mailbox-item--error {
  border-left-color: var(--status-down);
}

.mailbox-name {
  color: var(--text-primary);
  text-decoration: none;
  font-weight: 500;
}

.mailbox-name:hover {
  color: var(--accent);
}

.mailbox-count {
  font-size: var(--font-size-sm);
  padding: 2px 8px;
  background: var(--bg-secondary);
  border-radius: var(--radius-full);
  color: var(--text-muted);
}

.mailbox-count--unread {
  background: var(--accent);
  color: white;
  font-weight: 600;
}

.mailbox-error {
  font-size: var(--font-size-xs);
  color: var(--status-down);
  cursor: help;
}

.mailbox-checking {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-style: italic;
}

.email-footer {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  text-align: right;
  padding-top: var(--space-xs);
  border-top: 1px solid var(--border-color);
  margin-top: var(--space-sm);
}

.unread-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  background: var(--accent);
  color: white;
  font-size: 11px;
  font-weight: 600;
  border-radius: var(--radius-full);
  margin-left: var(--space-xs);
}


/* ============================================================
   Card builder
   ============================================================ */

.builder {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: var(--space-lg);
  padding: var(--space-lg);
  max-width: 1400px;
  margin: 0 auto;
  align-items: start;
}

@media (max-width: 899px) {
  .builder {
    grid-template-columns: 1fr;
    padding: var(--space-md);
  }

  /* The cards come first. Stacked, the palette is taller than a phone screen,
     so leaving it on top meant scrolling past every widget before reaching the
     thing you opened this page to arrange. */
  .builder__cards {
    order: 1;
  }

  .widget-palette {
    order: 2;
    /* Sticky earns nothing in a single column and only complicates scrolling. */
    position: static;
  }

  /* Compact: a phone cannot drag a palette item anyway, so the entry only has
     to name the widget and offer Add. The description goes. */
  .palette-item {
    align-items: center;
    gap: var(--space-sm);
  }

  .palette-item__body small {
    display: none;
  }

  .palette-item__add {
    min-height: 36px;
  }

  /* The header is five controls; on a phone they need two lines and a real
     touch target rather than being squeezed onto one. */
  .builder-card__header {
    gap: var(--space-sm);
  }

  .builder-card__name {
    flex: 1 1 100%;
    min-width: 0;
  }
}

.widget-palette {
  background: var(--palette-bg);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: var(--space-md);
  position: sticky;
  top: var(--space-lg);
}

.widget-palette__title {
  font-size: var(--font-size-base);
  margin: 0 0 var(--space-xs);
}

/* The palette is two lists: the widgets the account already has and is
   not showing on this layout, then the kinds it can add new. */
.palette-section__title {
  margin: var(--space-md) 0 0;
  font-size: var(--font-size-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.palette-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-sm);
  margin-top: var(--space-sm);
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  cursor: grab;
}

.palette-item:active { cursor: grabbing; }
.palette-item__icon { color: var(--accent); flex-shrink: 0; }
.palette-item__body { min-width: 0; flex: 1; }

/* The click route into a card, for touch and keyboard where drag has none. */
.palette-item__add {
  flex-shrink: 0;
  align-self: center;
  padding: 2px var(--space-sm);
  font-size: var(--font-size-xs);
}

.card-choices {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.card-choice {
  justify-content: flex-start;
  text-align: left;
  border: 1px solid var(--border-color);
}

.builder__toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-md);
  flex-wrap: wrap;
}

/* Which layout is being built, and the actions on it, at the head of the
   toolbar; the rest of the toolbar acts within that layout. */
.builder__layout {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding-right: var(--space-md);
  border-right: 1px solid var(--border-color);
}

.builder__layout .layout-select {
  width: auto;
  min-width: 11rem;
  padding: var(--space-sm);
}

.builder-card {
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-md);
}

.builder-card__header {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md);
  border-bottom: 1px solid var(--border-color);
  flex-wrap: wrap;
}

.builder-card__name {
  flex: 1;
  min-width: 120px;
  font-weight: 600;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-primary);
  font-family: inherit;
}

.builder-card__name:hover,
.builder-card__name:focus {
  border-color: var(--border-color);
  background: var(--bg-primary);
  outline: none;
}

/* min-height keeps an empty card a usable drop target. */
.builder-card__zone {
  padding: var(--space-md);
  min-height: 72px;
  transition: background var(--transition-fast);
}

.builder-card__zone.drag-over {
  background: var(--drop-target-bg);
  outline: 2px dashed var(--drop-target-border);
  outline-offset: -6px;
}

.builder-card__empty {
  margin: 0;
  text-align: center;
  padding: var(--space-sm);
}

.builder-widget {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-sm);
  cursor: grab;
}

.builder-widget:active { cursor: grabbing; }
.builder-widget__name { flex: 1; min-width: 0; }
.builder-widget__label { color: var(--text-muted); font-weight: 400; }

/* The element being dragged. DragReorder has always added this class; until
   now nothing defined it. */
.dragging {
  opacity: 0.4;
}

/* Where the dragged item would land. */
.drop-placeholder {
  height: 3px;
  margin: var(--space-xs) 0;
  background: var(--accent);
  border-radius: 2px;
}

/* The lift on hover fights the drag image while a drag is in progress. */
body:has(.dragging) .card:hover {
  transform: none;
}


/* ============================================================
   Health and Email widgets
   ============================================================ */

.health-list, .email-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.health-item, .email-item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-primary);
  border-radius: var(--radius-sm);
  border-left: 3px solid transparent;
}

.health-item--down { border-left-color: var(--status-down); }

.health-status--up { color: var(--status-up); }
.health-status--down { color: var(--status-down); }

.health-name, .email-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.health-badge {
  font-size: var(--font-size-xs);
  font-weight: 600;
  padding: 2px var(--space-xs);
  border-radius: var(--radius-sm);
}

.health-badge--up { color: var(--status-up); background: rgba(34, 197, 94, 0.12); }
.health-badge--down { color: var(--status-down); background: rgba(239, 68, 68, 0.12); }

/* The bars shrink rather than pushing the card wider. */
.health-history {
  display: flex;
  gap: 1px;
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
}

.health-bar {
  width: 4px;
  height: 16px;
  border-radius: 1px;
  flex-shrink: 0;
}

.health-bar--up { background: var(--status-up); }
.health-bar--down { background: var(--status-down); }

.email-item--error { border-left-color: var(--status-warning); }

.email-count {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}

.email-count--unread {
  color: white;
  background: var(--accent);
  padding: 2px var(--space-sm);
  border-radius: var(--radius-sm);
}

.email-error {
  font-size: var(--font-size-xs);
  color: var(--status-warning);
}


/* ============================================================
   Managed record lists inside widget settings
   ============================================================ */

.record-manager {
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: var(--space-sm);
  background: var(--bg-primary);
}

.record-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-sm);
}

.record-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background: var(--bg-card);
  border-radius: var(--radius-sm);
}

/* Each cell shares the row and truncates rather than widening it. */
.record-cell {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--font-size-sm);
}

.record-cell:first-child { font-weight: 500; }
.record-cell:not(:first-child) { color: var(--text-muted); }

/* A widget's word on the row — "Google · connected" — and the actions it
   offers go on a second line, indented past the handle, so the name and
   address keep the first line: beside them they squeezed the name to a
   letter. Only a row with something to say wraps. */
.record-row--info {
  flex-wrap: wrap;
}

.record-row__info {
  flex-basis: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-left: calc(1rem + var(--space-sm));
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

.record-row__status {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.record-row__actions {
  display: flex;
  gap: var(--space-xs);
  flex-shrink: 0;
}

.record-actions {
  display: flex;
  gap: var(--space-xs);
  flex-shrink: 0;
}

/* Several ways to add a record: sign in here, or the plain form with a
   server prefilled. They wrap on a phone rather than overflow. */
.record-add {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

.record-form {
  margin-top: var(--space-sm);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--border-color);
}

.record-form__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}

/* List fields: a repeatable sub-form kept in the settings blob, one row per
   item, edited in place. Rows hold selects, which take the mousedown, so the
   handle is what drags; the arrows are the touch route. */
.list-manager {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-sm);
}

.list-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  width: 100%;
}

.list-rows:empty { display: none; }

.list-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background: var(--bg-primary);
  border-radius: var(--radius-sm);
}

.list-row.dragging { opacity: 0.5; }

.list-row__fields {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
}

.list-row__fields .form-input {
  flex: 1 1 9rem;
  min-width: 0;
  width: auto;
}


/* ============================================================
   Web search bar and engine settings
   ============================================================ */

.header__search {
  display: flex;
  align-items: stretch;
  gap: var(--space-xs);
  flex: 1;
  max-width: 560px;
  margin: 0 auto;
}

.search-engine {
  flex-shrink: 0;
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--font-size-sm);
  cursor: pointer;
}

/* The search input inside a widget, distinct from the header one. */
.widget-search {
  width: 100%;
  margin-bottom: var(--space-sm);
  font-size: var(--font-size-sm);
}

.engine-default {
  font-size: var(--font-size-xs);
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-xs);
  margin-left: var(--space-xs);
}

@media (max-width: 767px) {
  }


/* ============================================================
   Notes & To-Dos widget
   ============================================================ */

/* Positioned so the search panel can lie over it. */
.notepad-bar {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}

.notepad-bar .notepad-heading-input {
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
  font-size: var(--font-size-sm);
}

.notepad-new,
.notepad-mode {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  padding: 0;
  line-height: 1;
}

.notepad-new {
  font-size: var(--font-size-lg);
}

/* Ghost-styled, so the search button reads as belonging to the field beside
   it rather than as a second thing to press. */
.notepad-mode {
  border: 1px solid var(--border-color);
  color: var(--text-muted);
}

.notepad-mode:hover,
.notepad-mode[aria-expanded="true"] {
  color: var(--accent);
}

/* The search panel: a small floating box laid over the bar, so searching
   takes no extra room and the field it covers comes straight back when the
   panel closes. Raised a little so it reads as something on top. */
.notepad-search {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0 var(--space-xs);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  box-shadow: var(--shadow-md);
}

.notepad-search__icon {
  display: inline-flex;
  flex-shrink: 0;
  padding-left: var(--space-xs);
  color: var(--text-muted);
}

.notepad-search .notepad-search__input {
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
  padding: var(--space-xs) var(--space-sm);
  border-color: transparent;
  background: transparent;
  font-size: var(--font-size-sm);
}

.notepad-search .notepad-search__input:focus {
  border-color: transparent;
}

.notepad-search__close {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  padding: 0;
  color: var(--text-muted);
}

@media (max-width: 599px) {
  .notepad-new,
  .notepad-mode {
    width: 44px;
    height: 44px;
  }

  /* Matches the buttons beside it, so the bar lines up and the field is as
     easy to hit as they are. */
  .notepad-bar .notepad-heading-input {
    min-height: 44px;
  }

  .notepad-search__close {
    width: 40px;
    height: 40px;
  }
}

/* A row is a fixed height so the list below can be sized in rows rather than
   pixels, and stays right whatever the font size is. */
.notepad-row {
  --notepad-row-h: 2.25rem;

  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: var(--notepad-row-h);
  padding: 0 var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
}

/* Only the handle is a grab surface: the rest of the row is a checkbox and a
   button, which take the mousedown themselves. */
.notepad-drag {
  flex-shrink: 0;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
  line-height: 1;
  cursor: grab;
  opacity: 0.5;
}

.notepad-row:hover .notepad-drag {
  opacity: 1;
}

.notepad-row.dragging {
  opacity: 0.5;
  cursor: grabbing;
}

.notepad-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* The card keeps its height however long the list grows. The count comes from
   the placement's own setting, so two placements can differ. */
.notepad-list--scrolling {
  max-height: calc(var(--notepad-visible, 6) * (2.25rem + var(--space-xs)));
  overflow-y: auto;
  padding-right: 2px;
}

.notepad-row--pinned {
  border-color: var(--accent);
}

.notepad-check {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
}

/* Keeps the headings of notes and tasks on the same left edge. */
.notepad-check-spacer {
  display: inline-block;
  flex-shrink: 0;
  width: 16px;
}

/* The heading is a button, not a div with a click handler, so it is reachable
   by keyboard and announced as something that does anything. */
.notepad-heading {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: none;
  background: none;
  font-family: inherit;
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.notepad-heading:hover {
  color: var(--accent);
}

/* A completed task stays legible — struck through and dimmed, not hidden,
   because hiding it is a separate setting. */
.notepad-row--done .notepad-heading {
  text-decoration: line-through;
  color: var(--text-muted);
}

.notepad-marks {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.notepad-mark {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  line-height: 1;
}

/* ---- the editor modal ---- */

/* A phone has no room for the footer's five buttons in a row, and the modal
   itself should use the whole screen rather than floating in the middle of it. */
@media (max-width: 599px) {
  .modal--wide {
    width: 100%;
    max-width: none;
    max-height: 100%;
    height: 100%;
    border-radius: 0;
  }

  .modal--wide .modal__footer {
    flex-wrap: wrap;
  }

  .modal--wide .modal__footer .btn {
    flex: 1 1 auto;
    min-height: 44px;
  }

  /* The spacer only makes sense while the buttons are on one line. */
  .notepad-editor__spacer {
    display: none;
  }

  .notepad-editor__body {
    min-height: 140px;
  }
}

/* Wider than the default 500px: this one holds a note body and images. */
.modal--wide {
  max-width: 640px;
}

.notepad-editor__title {
  width: 100%;
  padding: var(--space-sm);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--font-size-lg);
  font-weight: 600;
}

.notepad-editor__title:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-primary);
}

/* ---- the rich editor (Quill, snow theme, recoloured) ----

   Quill ships its own stylesheet with fixed greys and blues. Everything
   below overrides those with the app's variables, so the editor follows the
   theme like the rest of the page. Scoped to the wrapper: the toolbar is
   inserted before the editor as a sibling, and the wrapper holds both. */
.notepad-editor__rich-wrap .ql-toolbar.ql-snow {
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: var(--bg-primary);
  font-family: inherit;
}

.notepad-editor__rich-wrap .ql-container.ql-snow {
  border: 1px solid var(--border-color);
  border-top: none;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  background: var(--bg-primary);
  font-family: inherit;
  font-size: var(--font-size-sm);
}

.notepad-editor__rich-wrap:focus-within .ql-toolbar.ql-snow,
.notepad-editor__rich-wrap:focus-within .ql-container.ql-snow {
  border-color: var(--accent);
}

.notepad-editor__rich-wrap .ql-editor {
  min-height: 200px;
  max-height: 55vh;
  padding: var(--space-sm);
  color: var(--text-primary);
  line-height: 1.6;
}

.notepad-editor__rich-wrap .ql-editor.ql-blank::before {
  left: var(--space-sm);
  color: var(--text-muted);
  font-style: normal;
}

.notepad-editor__rich-wrap .ql-editor img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
}

.notepad-editor__rich-wrap .ql-editor a {
  color: var(--accent);
}

.notepad-editor__rich-wrap .ql-editor blockquote {
  border-left: 3px solid var(--border-color);
  color: var(--text-muted);
}

.notepad-editor__rich-wrap .ql-editor pre.ql-syntax,
.notepad-editor__rich-wrap .ql-editor .ql-code-block-container {
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

/* Toolbar icons: muted at rest, accent under the pointer or when the format
   is on at the caret. Quill draws them as SVG strokes and fills. */
.notepad-editor__rich-wrap .ql-snow .ql-stroke {
  stroke: var(--text-muted);
}

.notepad-editor__rich-wrap .ql-snow .ql-fill,
.notepad-editor__rich-wrap .ql-snow .ql-stroke.ql-fill {
  fill: var(--text-muted);
}

.notepad-editor__rich-wrap .ql-snow .ql-picker {
  color: var(--text-muted);
}

.notepad-editor__rich-wrap .ql-snow.ql-toolbar button:hover,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar button:focus,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar button.ql-active,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar .ql-picker-label:hover,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar .ql-picker-label.ql-active,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar .ql-picker-item:hover,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar .ql-picker-item.ql-selected {
  color: var(--accent);
}

.notepad-editor__rich-wrap .ql-snow.ql-toolbar button:hover .ql-stroke,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar button:focus .ql-stroke,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar button.ql-active .ql-stroke,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke {
  stroke: var(--accent);
}

.notepad-editor__rich-wrap .ql-snow.ql-toolbar button:hover .ql-fill,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar button:focus .ql-fill,
.notepad-editor__rich-wrap .ql-snow.ql-toolbar button.ql-active .ql-fill {
  fill: var(--accent);
}

/* The heading picker's dropdown and the link tooltip are drawn by Quill in
   its own colours; these are the surfaces to recolour. */
.notepad-editor__rich-wrap .ql-snow .ql-picker-options,
.notepad-editor__rich-wrap .ql-snow .ql-tooltip {
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  box-shadow: var(--shadow-md);
}

.notepad-editor__rich-wrap .ql-snow .ql-tooltip input[type="text"] {
  border: 1px solid var(--border-color);
  background: var(--bg-primary);
  color: var(--text-primary);
}

.notepad-editor__rich-wrap .ql-snow .ql-tooltip a {
  color: var(--accent);
}

/* The link tooltip is positioned by Quill relative to the editor and can
   poke out of a narrow modal; clamp it inside. */
.notepad-editor__rich-wrap .ql-snow .ql-tooltip {
  left: var(--space-sm) !important;
  max-width: calc(100% - 2 * var(--space-sm));
}

/* A finger needs bigger targets, and the toolbar has many. */
@media (hover: none), (max-width: 599px) {
  .notepad-editor__rich-wrap .ql-snow.ql-toolbar button {
    width: 36px;
    height: 36px;
    padding: 8px;
  }

  .notepad-editor__rich-wrap .ql-editor {
    min-height: 140px;
    max-height: none;
  }
}

.notepad-editor__controls {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-top: var(--space-sm);
}

/* The day the entry was written, as a date field beside the two flags. A
   native date input, so a phone gets its own picker and the value is always
   yyyy-mm-dd whatever the locale shows. */
.notepad-editor__created {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  margin-left: auto;
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

.notepad-editor__created .form-input {
  width: auto;
  padding: var(--space-xs) var(--space-sm);
  font-size: var(--font-size-sm);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 599px) {
  .notepad-editor__created {
    margin-left: 0;
    flex-basis: 100%;
  }
}

.notepad-editor__delete {
  color: var(--status-down);
}

/* Pushes Cancel and Save away from Delete, so the destructive one is not
   next to the one you reach for most. */
.notepad-editor__spacer {
  flex: 1;
}

.notepad-editor__images {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}

.notepad-editor__images:empty {
  margin-top: 0;
}

.notepad-image {
  position: relative;
  width: 110px;
}

.notepad-image img {
  display: block;
  width: 100%;
  height: 80px;
  object-fit: cover;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

.notepad-image__remove {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--bg-card);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  line-height: 1;
  cursor: pointer;
  opacity: 0.85;
}

.notepad-image__remove:hover {
  opacity: 1;
  color: var(--status-down);
}

/* A file that is not a picture is a chip carrying its name. The same remove
   button as a thumbnail, in the same corner, so the two kinds of attachment
   are handled the same way. */
.notepad-file {
  position: relative;
  display: flex;
  align-items: center;
  max-width: 100%;
  min-height: 44px;
  padding-right: 26px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
}

.notepad-file__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-width: 0;
  padding: var(--space-xs) var(--space-sm);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  text-decoration: none;
}

.notepad-file__link:hover .notepad-file__name {
  color: var(--accent);
  text-decoration: underline;
}

.notepad-file__icon {
  flex-shrink: 0;
}

.notepad-file__name {
  max-width: 220px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.notepad-file .notepad-image__remove {
  top: 50%;
  transform: translateY(-50%);
}


/* ============================================================
   Lightbox and export chooser
   ============================================================ */

/* Deliberately not themed: a picture is judged against a dark ground in either
   theme, and this is the one surface in the app that is about the image rather
   than the interface. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
  background: rgba(0, 0, 0, 0.9);
}

/* The page behind must not scroll under the viewer, which on a phone is the
   difference between pinching an image and losing your place. */
body.lightbox-open {
  overflow: hidden;
}

.lightbox__image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--radius-sm);
}

.lightbox__close,
.lightbox__nav {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  cursor: pointer;
  line-height: 1;
  transition: background-color var(--transition-fast);
}

.lightbox__close:hover,
.lightbox__nav:hover {
  background: rgba(255, 255, 255, 0.25);
}

/* 44px is the smallest comfortable touch target, so the controls are that size
   everywhere rather than shrinking on the screens that need them biggest. */
.lightbox__close {
  top: var(--space-md);
  right: var(--space-md);
  width: 44px;
  height: 44px;
  font-size: 1.75rem;
}

.lightbox__nav {
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  font-size: 2rem;
}

.lightbox__nav--prev { left: var(--space-md); }
.lightbox__nav--next { right: var(--space-md); }

.lightbox__count {
  position: absolute;
  bottom: var(--space-md);
  left: 50%;
  transform: translateX(-50%);
  padding: 4px var(--space-sm);
  border-radius: var(--radius-sm);
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: var(--font-size-xs);
}

/* On a phone the arrows would sit on top of the picture, so they move to the
   bottom corners where the thumbs already are. */
@media (max-width: 599px) {
  .lightbox {
    padding: var(--space-sm);
  }

  .lightbox__nav {
    top: auto;
    bottom: var(--space-md);
    transform: none;
  }

  .lightbox__count {
    bottom: calc(var(--space-md) + 52px);
  }
}

/* Thumbnails say they can be opened. */
.notepad-image img {
  cursor: zoom-in;
}

.export-choices {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.export-choice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-height: 44px;
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  text-align: left;
}


/* ============================================================
   Camera and location controls
   ============================================================ */

.notepad-editor__tools {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}

/* These are the controls a phone reaches for, so they get a real touch target
   and share the row evenly rather than sizing to their labels. */
.notepad-tool {
  flex: 1 1 auto;
  min-height: 44px;
  border: 1px solid var(--border-color);
  font-size: var(--font-size-sm);
  white-space: nowrap;
}

.notepad-tool:disabled {
  opacity: 0.6;
  cursor: default;
}

.location {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-sm);
  margin-top: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
  font-size: var(--font-size-sm);
}

/* The dot carries the quality of the fix, so a coarse network position is not
   mistaken for a GPS one at a glance. */
.location__dot {
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  border-radius: 50%;
}

.location__dot--good { background: var(--status-up); }
.location__dot--fair { background: var(--status-warning); }
.location__dot--coarse { background: var(--status-down); }

.location__coords {
  color: var(--accent);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}

.location__coords:hover {
  text-decoration: underline;
}

.location__accuracy {
  color: var(--text-muted);
  font-size: var(--font-size-xs);
}

/* Takes the whole second line, so a permission hint reads as a sentence rather
   than being squeezed between the coordinates and the remove button. */
.location__note {
  flex-basis: 100%;
  color: var(--text-muted);
  font-size: var(--font-size-xs);
}


/* The grab surface for reordering cards. The rest of the header is form
   controls, which take the mousedown themselves, so a drag begun anywhere else
   never reaches the card. */
.builder-card__drag {
  flex-shrink: 0;
  padding: 0 var(--space-xs);
  color: var(--text-muted);
  cursor: grab;
  opacity: 0.5;
  user-select: none;
}

.builder-card:hover .builder-card__drag {
  opacity: 1;
}

.builder-card.dragging {
  opacity: 0.5;
}


/* Up and down, for touch, where HTML5 drag does not exist. Kept beside the
   delete button so every control that acts on the whole card is in one place. */
.builder-card__moves {
  display: flex;
  flex-shrink: 0;
  gap: 2px;
}

.builder-card__moves .btn {
  min-width: 36px;
  min-height: 36px;
  font-size: var(--font-size-xs);
}

.builder-card__moves .btn:disabled {
  opacity: 0.3;
  cursor: default;
}

@media (max-width: 899px) {
  /* A real touch target: on a phone these are the only way to reorder. */
  .builder-card__moves .btn {
    min-width: 44px;
    min-height: 44px;
  }

  /* Drag is a mouse gesture, so the handle is only clutter here. */
  .builder-card__drag {
    display: none;
  }
}


/* ============================================================
   Dashboard edit mode
   ============================================================ */

/* Every piece of edit chrome is in the markup at all times and shown by
   class. Not by the hidden attribute: that rule is !important, so a
   stylesheet could never show what it hides. */
.card__edit,
.widget__edit,
.card-grid__add,
.edit-toggle__done {
  display: none;
}

/* The header button: a pencil normally, the word Done while editing. */
body.is-editing .edit-toggle {
  width: auto;
  padding: 0 var(--space-md);
  background: var(--accent);
  color: white;
}

body.is-editing .edit-toggle:hover {
  background: var(--accent-hover);
  color: white;
}

body.is-editing .edit-toggle__icon { display: none; }
body.is-editing .edit-toggle__done { display: inline; }

/* The toolbar at the top of every card. */
body.is-editing .card__edit {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: var(--space-xs) var(--space-sm);
  background: var(--drop-target-bg);
  border-bottom: 1px dashed var(--accent);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

/* The card's name is otherwise never on the dashboard; here it is what the
   Move to menu names, so it is worth showing — and it is the button that
   renames the card. */
.card__edit-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 0 var(--space-xs);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: inherit;
  font: inherit;
  font-size: var(--font-size-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: left;
  cursor: pointer;
}

.card__edit-name:hover,
.card__edit-name:focus-visible {
  color: var(--text-primary);
  text-decoration: underline;
}

.card__edit .btn:disabled {
  opacity: 0.35;
  cursor: default;
}

/* A hidden card is on the page for the edit mode to show again, and
   nowhere else. Its widgets are not drawn. */
.card--hidden {
  display: none;
}

body.is-editing .card--hidden {
  display: block;
  opacity: 0.6;
  border-style: dashed;
}

.card__hidden {
  padding: var(--space-lg) var(--space-md);
  text-align: center;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

.card__span {
  flex-shrink: 0;
  padding: 2px var(--space-xs);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  color: var(--text-primary);
  font: inherit;
  font-size: var(--font-size-xs);
}

.card__edit .btn,
.widget__edit .btn {
  width: 28px;
  height: 28px;
  font-size: var(--font-size-xs);
}

/* The pressed look of Edit widgets while the card is open. */
.card--editing .card__edit [data-card-edit] {
  background: var(--accent);
  color: white;
}

/* The per-widget controls, only while the card is open. */
body.is-editing .card--editing .widget__edit {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}

.edit-grip {
  flex-shrink: 0;
  padding: 0 var(--space-xs);
  color: var(--text-muted);
  cursor: grab;
  opacity: 0.6;
  user-select: none;
}

/* Nothing inside a widget takes input while editing. The inert attribute on
   the body does the real work — no focus, no keys, no clicks — and this
   backs it up for hit-testing on older engines, and shows the state. */
body.is-editing .widget__body {
  pointer-events: none;
  user-select: none;
  opacity: 0.55;
}

/* The header's own buttons give way to ▲▼⋯ while editing; the menu carries
   Settings… there, and a refresh is not what editing is for. */
body.is-editing [data-widget-refresh],
body.is-editing [data-widget-settings] {
  display: none;
}

/* The whole card is the grab surface: with the bodies inert a press anywhere
   on it reaches the card. */
body.is-editing .card {
  user-select: none;
  cursor: grab;
}

body.is-editing .card:hover {
  transform: none;
  box-shadow: var(--shadow-sm);
}

body.is-editing .card--editing {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

body.is-editing .card--editing .widget {
  cursor: grab;
}

body.is-editing .card.drag-over {
  background: var(--drop-target-bg);
}

/* An empty card is on the page for the edit mode to fill, and nowhere else. */
.card--empty {
  display: none;
}

body.is-editing .card--empty {
  display: block;
  min-height: 120px;
  border-style: dashed;
}

body.is-editing .card--empty::after {
  content: "Drop a widget here";
  display: block;
  padding: var(--space-lg) var(--space-md);
  text-align: center;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

/* The new-card tile. order keeps it last however a drag rearranges the
   grid's children — DragReorder appends a card dragged past the end. */
body.is-editing .card-grid__add {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  order: 9999;
  min-height: 120px;
  border: 2px dashed var(--border-color);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--font-size-sm);
  cursor: pointer;
}

body.is-editing .card-grid__add:hover,
body.is-editing .card-grid__add:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
}

/* The add-widget picker lists the builder's palette items as buttons: the
   whole row is the choice, there being no drag to offer in a modal. */
.palette-item--choice {
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.palette-item--choice:hover,
.palette-item--choice:focus-visible {
  border-color: var(--accent);
}

.palette-item--choice:disabled {
  opacity: 0.5;
  cursor: default;
}

/* A widget an administrator shows without granting: listed so the person
   knows it exists, with nothing to drag and nothing to press. */
.palette-item--unavailable {
  opacity: 0.55;
  cursor: default;
}
.palette-item--unavailable:active { cursor: default; }
.palette-item__note { font-style: italic; }

/* The wiggle: what everybody knows an editable home screen by. A card that
   is open for widget editing holds still, and so does the one being dragged,
   or the drag image is a tilted frame. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes card-wiggle {
    0% { transform: rotate(-0.3deg); }
    50% { transform: rotate(0.3deg); }
    100% { transform: rotate(-0.3deg); }
  }

  body.is-editing .card {
    animation: card-wiggle 0.4s ease-in-out infinite;
  }

  body.is-editing .card:nth-child(even) {
    animation-delay: -0.2s;
  }

  body.is-editing .card.dragging,
  body.is-editing .card--editing {
    animation: none;
  }
}

@media (max-width: 699px) {
  /* One column here, so earlier is up and later is down. */
  .card__move svg {
    transform: rotate(90deg);
  }
}

@media (hover: none) and (pointer: coarse) {
  /* Drag is a mouse gesture; the grip would only be clutter here, and the
     buttons are the whole of the control, so they get a finger's width. */
  .edit-grip {
    display: none;
  }

  .card__edit .btn,
  .widget__edit .btn {
    width: 36px;
    height: 36px;
  }
}


/* ============================================================
   Header on a narrow screen
   ============================================================ */

/* One row cannot hold a title, a search box and four buttons on a phone: the
   search squeezed to nothing and the buttons went off the right edge, reachable
   only by scrolling sideways. The search takes its own row instead, where it
   has the width it actually needs. */
@media (max-width: 767px) {
  .header {
    flex-wrap: wrap;
    gap: var(--space-sm);
    padding: var(--space-sm) var(--space-md);
  }

  .header__title {
    flex: 0 1 auto;
    min-width: 0;
    font-size: var(--font-size-base);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Pushed hard right on the first row, so the buttons keep their place
     whatever the title is. */
  .header__actions {
    flex: 0 0 auto;
    margin-left: auto;
  }

  /* Ordered after the actions, which with wrapping puts it on the next line. */
  .header__search {
    order: 3;
    flex: 1 1 100%;
    width: auto;
    max-width: none;
    margin: 0;
  }

  /* The engine picker keeps its label readable without eating the input. */
  .search-engine {
    max-width: 40%;
  }
}



/* ============================================================
   The map mark on a located entry
   ============================================================ */

/* The location mark, in both note widgets. It reads as one of the row's marks
   until you touch it, then as the link it is. */
.mark-link {
  flex-shrink: 0;
  text-decoration: none;
  color: var(--text-muted);
  line-height: 1;
}

.mark-link:hover,
.mark-link:focus-visible {
  color: var(--accent);
}

/* Touch needs something to aim at, and the mark itself is a single glyph. The
   negative margin keeps the row its own height while the target grows. */
@media (max-width: 599px) {
  .mark-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    margin: -10px 0;
  }
}


/* ============================================================
   Notes & To-Dos row controls
   ============================================================ */

/* The footer: hide-completed and the filter on the left, the counts on the
   right where they always were. */
.notepad-footer {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding-left: var(--space-xs);
  padding-right: var(--space-xs);
}

.notepad-footer__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  cursor: pointer;
  white-space: nowrap;
}

.notepad-footer__toggle input {
  width: 14px;
  height: 14px;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

.notepad-footer__filter {
  width: 24px;
  height: 24px;
  color: var(--text-muted);
}

.notepad-footer__filter:hover,
.notepad-footer__filter.is-active {
  color: var(--accent);
}

.notepad-footer__count {
  margin-left: auto;
  text-align: right;
}

/* The filter dialog: a short column of labelled fields. */
.notepad-filter {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.notepad-filter__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

.notepad-filter__field .form-input {
  font-size: var(--font-size-sm);
}

.notepad-filter__dates {
  display: flex;
  gap: var(--space-sm);
}

.notepad-filter__dates .notepad-filter__field {
  flex: 1;
  min-width: 0;
}

/* Touch targets for the footer controls, matching the rest of the card. */
@media (hover: none), (max-width: 599px) {
  .notepad-footer__toggle input {
    width: 18px;
    height: 18px;
  }

  .notepad-footer__filter {
    width: 36px;
    height: 36px;
  }
}

/* When the entry was written, in the same muted weight as the marks and with
   even digits so the column lines up down the list. */
.notepad-date {
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* One ⋯ button opening a menu. Always on show: it is the only way to the
   row's actions, and a control that has to be found by hovering is not one on
   touch, or for anyone who does not know it is there. Muted rather than
   hidden, so the heading still leads. */
.notepad-actions {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.notepad-actions .btn {
  width: 26px;
  height: 26px;
  padding: 0;
  font-size: var(--font-size-base);
  line-height: 1;
  color: var(--text-muted);
}

.notepad-actions .btn:hover {
  color: var(--accent);
}

/* On touch the button takes a real target, and the informational marks give
   up their space for it — the editor says the same thing when you open the
   entry. The date stays: it is the one thing on the row worth the room. */
@media (hover: none), (max-width: 599px) {
  .notepad-actions .btn {
    width: 36px;
    height: 36px;
  }

  .notepad-marks .notepad-mark:not(.mark-link) {
    display: none;
  }
}

/* ============================================================
   Dropdown menu
   ============================================================ */

/* Built by Dashboard.Menu under whichever button opened it. Fixed and on the
   body, so a scrolling list cannot clip it; above the cards and the header,
   below a modal, which closes it before opening anyway. */
.menu {
  position: fixed;
  z-index: 900;
  min-width: 180px;
  max-width: calc(100vw - 8px);
  padding: var(--space-xs) 0;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  box-shadow: var(--shadow-lg);
}

.menu__item {
  display: block;
  width: 100%;
  padding: var(--space-sm) var(--space-md);
  border: none;
  background: none;
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--font-size-sm);
  text-align: left;
  cursor: pointer;
}

.menu__item:hover,
.menu__item:focus-visible {
  outline: none;
  background: var(--bg-primary);
  color: var(--accent);
}

.menu__item:disabled {
  color: var(--text-muted);
  cursor: default;
}

.menu__item--danger {
  color: var(--status-down);
}

.menu__item--danger:hover,
.menu__item--danger:focus-visible {
  color: var(--status-down);
}

/* The choice already in force, in a menu of alternatives. */
.menu__item--current {
  font-weight: 600;
}

.menu__item--current::after {
  content: ' \2713';
  color: var(--accent);
}

/* A line of information at the top, not a choice: the entry's date, say. */
.menu__heading {
  padding: var(--space-xs) var(--space-md) var(--space-sm);
  color: var(--text-muted);
  font-size: var(--font-size-xs);
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--border-color);
  margin-bottom: var(--space-xs);
}

.menu__separator {
  height: 1px;
  margin: var(--space-xs) 0;
  background: var(--border-color);
}

/* A finger needs more to aim at than a pointer does. */
@media (hover: none), (max-width: 599px) {
  .menu__item {
    min-height: 44px;
  }
}

/* Recovery code
   ------------------------------------------------------------------------ */

/* Shown once, on setup and on the first login of a carried-over account, then
   never again. Monospace and widely spaced because it is meant to be copied
   onto paper by eye, and user-select makes it easy to take digitally instead. */
.recovery-code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: var(--font-size-lg);
  letter-spacing: 0.08em;
  text-align: center;
  word-break: break-all;
  padding: var(--space-md);
  margin-bottom: var(--space-md);
  border: 1px dashed var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-primary);
  color: var(--text-primary);
  user-select: all;
}

/* Settings and admin pages
   ------------------------------------------------------------------------
   Promoted out of settings.html's inline <style> so the admin console can
   use the same chrome instead of duplicating eighty lines of it. Appended at
   the end of the file, which is the lowest-conflict edit available while
   another branch may be working further up. */
  .settings-page {
    max-width: 800px;
    margin: 0 auto;
    padding: var(--space-lg);
  }
  .settings-header {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    margin-bottom: var(--space-lg);
  }
  .tabs {
    display: flex;
    align-items: center;
    border-bottom: 1px solid var(--border-color);
    margin-bottom: var(--space-lg);
  }
  /* Pushed to the far end, away from the tabs it is not one of. */
  .tabs__action {
    margin-left: auto;
    margin-bottom: var(--space-xs);
    gap: var(--space-xs);
    border: 1px solid var(--border-color);
    white-space: nowrap;
  }
  @media (max-width: 767px) {
    .tabs {
        gap: var(--space-xs);
    }
    /* Icon only. The label is what would not fit beside the tabs, and
       losing it keeps everything on one row. */
    .tabs__action-label {
        display: none;
    }
    .tabs__action {
        width: 44px;
        min-height: 44px;
        padding: 0;
        justify-content: center;
    }
  }
  .tab {
    padding: var(--space-sm) var(--space-md);
    border: none;
    background: none;
    cursor: pointer;
    color: var(--text-muted);
    border-bottom: 2px solid transparent;
    transition: all var(--transition-fast);
  }
  .tab:hover {
    color: var(--text-primary);
  }
  .tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
  }
  .tab-content {
    display: none;
  }
  .tab-content.active {
    display: block;
  }
  .settings-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
  }
  .settings-item {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    padding: var(--space-md);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
  }
  .settings-item__drag {
    cursor: grab;
    color: var(--text-muted);
  }
  .settings-item__name {
    flex: 1;
    /* A flex child will not shrink below its content without this, so a
       long address pushed the buttons clean off the row. */
    min-width: 0;
  }
  .settings-item__name small {
    /* An address has no spaces to break at, so it is allowed to break
       anywhere rather than setting the row's width. */
    overflow-wrap: anywhere;
  }
  .settings-item__actions {
    display: flex;
    flex-shrink: 0;
    gap: var(--space-xs);
  }

/* Admin console
   ------------------------------------------------------------------------ */

.admin-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  margin-bottom: var(--space-md);
}

.admin-heading {
  font-size: var(--font-size-lg);
  margin: 0;
}

/* Widget access: one row per widget, with a Shown and an Available box.
   Available implies shown, so the two boxes are three states, not four. */
.access-table-wrap { overflow-x: auto; }

.access-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-sm);
}

.access-table th,
.access-table td {
  padding: var(--space-xs) var(--space-sm);
  text-align: center;
}

.access-table th:first-child,
.access-table td:first-child {
  text-align: left;
  width: 100%;
}

.access-table thead th {
  font-weight: 500;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-color);
  white-space: nowrap;
}

.access-table tbody tr + tr td { border-top: 1px solid var(--border-color); }

.access-table input[type="checkbox"] {
  width: 18px;
  height: 18px;
  cursor: pointer;
}

.access-table__state {
  display: block;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.access-table__bulk {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-bottom: var(--space-sm);
}

/* The chooser's button on the invite and new-person forms, with what is
   chosen written beside it. */
.access-choice {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

/* A section within a tab's form, set off from the fields above it. */
.admin-subheading {
  font-size: var(--font-size-base);
  font-weight: 600;
  margin: var(--space-lg) 0 var(--space-xs);
  padding-top: var(--space-md);
  border-top: 1px solid var(--border-color);
}

/* A badge sits inline with the name it qualifies, so it must not inherit the
   line height of the row and push it taller. */
.settings-item__name .badge {
  margin-left: var(--space-xs);
  vertical-align: middle;
}


/* ============================================================
   Record lists: reordering, and the location field
   ============================================================ */

/* The grab surface. The rest of the row is cells and buttons, and a button
   takes the mousedown before a drag can start. */
.record-handle {
  flex-shrink: 0;
  color: var(--text-muted);
  cursor: grab;
  user-select: none;
}

.record-row.dragging {
  opacity: 0.5;
}

.record-cell a {
  color: var(--accent);
  text-decoration: none;
}

.record-cell a:hover {
  text-decoration: underline;
}

.location-field__coords {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-sm);
}

.location-field__coord {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.location-field__find {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}

.location-field__find .form-input {
  flex: 1;
  min-width: 10rem;
}

.place-results {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-top: var(--space-xs);
}

.place-results:empty {
  display: none;
}

/* A result is a button, so it is a keyboard target too, but drawn as a row. */
.place-result {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  width: 100%;
  padding: var(--space-xs) var(--space-sm);
  background: var(--bg-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.place-result:hover,
.place-result:focus-visible {
  border-color: var(--accent);
}

.place-result__name {
  font-weight: 500;
}

.place-result__detail {
  font-size: var(--font-size-xs);
}


/* ============================================================
   Weather widget
   ============================================================ */

.weather-banner {
  margin-bottom: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-primary);
  border-left: 3px solid var(--status-warning);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

.weather-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.weather-row {
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-primary);
  border-radius: var(--radius-sm);
}

.weather-row__head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-sm);
}

.weather-row__place {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.weather-row__name {
  color: var(--text-primary);
  font-weight: 600;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.weather-row__name:hover {
  text-decoration: underline;
}

.weather-row__area {
  color: var(--text-muted);
  font-size: var(--font-size-xs);
}

/* Today's range, the one number a glance is after. */
.weather-row__today {
  display: flex;
  align-items: baseline;
  gap: var(--space-xs);
  font-variant-numeric: tabular-nums;
}

.weather-row__max {
  font-size: var(--font-size-xl);
  font-weight: 600;
}

.weather-row__min {
  color: var(--text-muted);
}

.weather-row__note {
  flex-basis: 100%;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

.weather-row__note--error {
  color: var(--status-down);
}

/* A row folds to its head — the name and today's range — and the chevron, or
   a press anywhere on the head, unfolds the week below it. */
.weather-row--foldable .weather-row__head {
  cursor: pointer;
}

.weather-row__toggle {
  display: inline-flex;
  align-items: center;
  align-self: center;
  padding: 2px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  line-height: 0;
}

.weather-row__toggle svg {
  transition: transform 0.15s ease;
}

.weather-row--open .weather-row__toggle svg {
  transform: rotate(180deg);
}

.weather-row--foldable .weather-row__head:hover .weather-row__toggle,
.weather-row__toggle:focus-visible {
  color: var(--text-primary);
}

/* The strip scrolls inside its row on a narrow screen rather than widening
   the card. */
.weather-days {
  display: flex;
  gap: var(--space-xs);
  margin-top: var(--space-sm);
  padding-bottom: 2px;
  overflow-x: auto;
}

.weather-day {
  flex: 1 0 2.75rem;
  min-width: 2.75rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--space-xs) 0;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: var(--font-size-xs);
}

.weather-day--today {
  background: var(--bg-card);
  color: var(--text-primary);
}

.weather-day__label {
  font-weight: 500;
}

.weather-icon {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
}

.weather-day__temps {
  display: flex;
  gap: 3px;
  font-variant-numeric: tabular-nums;
}

.weather-day__max {
  color: var(--text-primary);
  font-weight: 500;
}

.weather-day__rain {
  white-space: nowrap;
}

/* The pictures take the theme's own colours, so they read on both grounds. */
.weather-icon--sun,
.weather-icon--partly { color: var(--status-warning); }

.weather-icon--cloud,
.weather-icon--overcast,
.weather-icon--fog { color: var(--text-muted); }

.weather-icon--drizzle,
.weather-icon--rain,
.weather-icon--heavy-rain,
.weather-icon--sleet,
.weather-icon--snow,
.weather-icon--heavy-snow { color: var(--accent); }

.weather-icon--thunder { color: var(--status-down); }

/* ============================================================
   SAFEX Feed Futures widget
   ============================================================ */

/* One row per instrument, the same two-line grid as Exchange Rates: the
   name and the nearest contract's price, then the code, the contract and
   the day's change. The row is a button — every contract opens from it —
   so it is reset to look like a row and given a focus ring of its own. It
   is not .btn, so that class's display rule never reaches it. */
.safex-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.safex-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--space-sm);
  align-items: baseline;
  width: 100%;
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-primary);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.safex-row:hover { border-color: var(--border-color); }

.safex-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.safex-row--empty { cursor: default; color: var(--text-muted); }
.safex-row--empty:hover { border-color: transparent; }

.safex-name {
  font-size: var(--font-size-sm);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.safex-price {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  justify-self: end;
}

.safex-meta {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.safex-change {
  font-size: var(--font-size-xs);
  font-variant-numeric: tabular-nums;
  justify-self: end;
}

.safex-change.change--up,
.safex-table__change.change--up { color: var(--delta-increase); }

.safex-change.change--down,
.safex-table__change.change--down { color: var(--delta-decrease); }

/* The forward curve in the modal. The wrap scrolls sideways on a phone:
   the modal sits on the body, out of reach of the widget body's table
   rule. */
.safex-table-wrap { overflow-x: auto; }

.safex-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-sm);
  font-variant-numeric: tabular-nums;
}

.safex-table th,
.safex-table td {
  padding: var(--space-xs) var(--space-sm);
  text-align: right;
  white-space: nowrap;
}

.safex-table th:first-child,
.safex-table td:first-child { text-align: left; }

.safex-table thead th {
  font-weight: 500;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-color);
}

.safex-table tbody tr + tr td { border-top: 1px solid var(--border-color); }

.safex-table__front td { font-weight: 600; }

.safex-table__badge {
  margin-left: var(--space-xs);
  font-size: var(--font-size-xs);
  font-weight: 400;
  color: var(--text-muted);
}

.safex-table__note { margin: var(--space-sm) 0 0; }

/* ---- Backup tab (settings.html) ----
   A section row is a checkbox with the description beneath the name; a
   section the account cannot restore is shown dimmed with the reason. */
.backup-section .checkbox {
  align-items: flex-start;
  width: 100%;
}

.backup-section .checkbox input {
  margin-top: 0.2em;
}

.backup-section--off {
  opacity: 0.6;
}

.backup-restore-heading {
  margin-top: var(--space-xl, 2rem);
}

.backup-file-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex-wrap: wrap;
}

.backup-report {
  margin: 0;
  padding-left: 1.25rem;
}

.backup-report li {
  margin-bottom: var(--space-xs);
}


/* ============================================================
   Document Store widget
   ============================================================ */

/* The bar: a name for a new document and the + that opens the editor with
   it. Same metrics as the Notes & To-Dos bar, so the two cards line up. */
.docstore-bar {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}

.docstore-bar .docstore-name-input {
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
  font-size: var(--font-size-sm);
}

.docstore-new {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  padding: 0;
  font-size: var(--font-size-lg);
  line-height: 1;
}

.docstore-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* The card keeps its height however long the list grows. The count comes
   from the placement's own setting, so two placements can differ. */
.docstore-list--scrolling {
  max-height: calc(var(--docstore-visible, 6) * (2.25rem + var(--space-xs)));
  overflow-y: auto;
  padding-right: 2px;
}

/* A row is a fixed height so the list above can be sized in rows rather
   than pixels, and stays right whatever the font size is. */
.docstore-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 2.25rem;
  padding: 0 var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
}

/* Only the handle is a grab surface: the name is a button, which takes the
   mousedown itself. */
.docstore-drag {
  flex-shrink: 0;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
  line-height: 1;
  cursor: grab;
  opacity: 0.5;
}

.docstore-row:hover .docstore-drag {
  opacity: 1;
}

.docstore-row.dragging {
  opacity: 0.5;
  cursor: grabbing;
}

/* The name is a button, not a div with a click handler, so it is reachable
   by keyboard and announced as something that does anything. */
.docstore-name {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: none;
  background: none;
  font-family: inherit;
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.docstore-name:hover {
  color: var(--accent);
}

/* How many files sit behind the name, drawn as a sheet or a pair of them. */
.docstore-mark {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  line-height: 1;
}

.docstore-mark svg {
  display: block;
}

.docstore-mark--empty {
  font-style: italic;
}

.docstore-date {
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.docstore-actions {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.docstore-footer {
  justify-content: flex-end;
}

.docstore-footer__count {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

/* The date gives up its room on a narrow screen so the name keeps it. */
@media (max-width: 599px) {
  .docstore-date {
    display: none;
  }
}

/* ---- the editor ---- */

.docstore-editor__name {
  width: 100%;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--font-size-lg);
  font-weight: 600;
}

.docstore-editor__name:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-primary);
}

/* The strip of files, one chip each, in the order they were added. */
.docstore-files {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-md);
}

.docstore-files:empty {
  margin-bottom: 0;
}

.docstore-file {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 40px;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-primary);
}

.docstore-file__icon {
  display: flex;
  flex-shrink: 0;
  color: var(--text-muted);
}

.docstore-file__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  text-decoration: none;
}

.docstore-file__name:hover {
  color: var(--accent);
  text-decoration: underline;
}

.docstore-file__size {
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.docstore-file__remove {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--text-muted);
  font-size: var(--font-size-md);
  line-height: 1;
  cursor: pointer;
}

.docstore-file__remove:hover {
  color: var(--status-down);
  background: var(--bg-card);
}

.docstore-editor__tools {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

.docstore-editor__tools .btn svg {
  margin-right: var(--space-xs);
  vertical-align: -2px;
}

.docstore-editor__spacer {
  flex: 1;
}

.docstore-editor__delete {
  color: var(--status-down);
}

/* A drag with files over the dialog says it will take them. */
.modal.is-dragover {
  outline: 2px dashed var(--accent);
  outline-offset: -6px;
}

/* ---- the download panel ---- */

.docstore-panel {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

/* A row is a tick box and the file beside it; the box chooses, the name is
   still a download of that one file. */
.docstore-panel__row {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding-left: var(--space-sm);
  border-bottom: 1px solid var(--border-color);
}

.docstore-panel__row:last-child {
  border-bottom: none;
}

.docstore-panel__check,
.docstore-panel__all input {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

.docstore-panel__all {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin-bottom: var(--space-xs);
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  cursor: pointer;
}

.docstore-panel__file {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 44px;
  padding: var(--space-xs) var(--space-sm);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  text-decoration: none;
}

.docstore-panel__file:hover,
.docstore-panel__file:focus-visible {
  background: var(--bg-primary);
  color: var(--accent);
}

/* Touch needs something to aim at. */
@media (hover: none), (max-width: 599px) {
  .docstore-panel__check,
  .docstore-panel__all input {
    width: 18px;
    height: 18px;
  }
}

.docstore-panel__icon {
  display: flex;
  flex-shrink: 0;
  color: var(--text-muted);
}

.docstore-panel__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.docstore-panel__size {
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}
