/* ============================================================================
   Brand bridge — remap the app's design vocabulary onto the TakomoCo tokens
   ============================================================================

   The original stylesheet is 2,353 lines built on an indigo-on-white system,
   and every rule in it already refers to variables (`--brand-primary`,
   `--bg-surface-1`, `--text-primary`). Rewriting all of it by hand would be a
   large, risky diff with plenty of opportunity to lose behaviour.

   So the variables are remapped instead. Every existing rule keeps working and
   the whole application re-skins at once, onto the warm espresso/clay system
   extracted in BRAND.md. Loaded AFTER index.css so these definitions win.

   This is a bridge, not the destination. The component library in §4.2 still
   needs building; what this achieves is visual continuity with the main site
   and the §8 mobile and accessibility fixes, without a 2,000-line rewrite.

   Anything that could not be expressed as a variable remap — touch targets,
   the iOS zoom threshold, the mobile navigation, dynamic viewport units — is
   in the sections below the token map.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   Theme selection

   The app is dark-first, matching the main site, but ships both. On a first
   visit with no stored preference the OS setting decides (§4.2); after that
   the user's explicit choice wins and is persisted.
   --------------------------------------------------------------------------- */

:root {
  color-scheme: light dark;
}

/* ============================================================== light theme */

:root {
  --brand-primary:       var(--interactive);
  --brand-primary-light: var(--clay-600);
  --brand-primary-dark:  #6f411f;
  --brand-primary-glow:  var(--interactive-subtle);
  --brand-secondary:     var(--clay-500);
  --brand-accent:        var(--ember-500);

  --color-success:    var(--status-success-fg);
  --color-success-bg: var(--status-success-bg);
  --color-warning:    var(--status-warning-fg);
  --color-warning-bg: var(--status-warning-bg);
  --color-danger:     var(--status-danger-fg);
  --color-danger-bg:  var(--status-danger-bg);
  --color-info:       var(--status-info-fg);
  --color-info-bg:    var(--status-info-bg);

  --bg-base:      var(--surface-base);
  --bg-surface-1: var(--surface-1);
  --bg-surface-2: var(--surface-sunken);
  --bg-surface-3: #ebdfd0;
  --bg-elevated:  var(--surface-raised);
  --bg-hover:     var(--interactive-subtle);
  --bg-active:    rgba(138, 82, 48, 0.14);

  --glass-bg:     rgba(255, 255, 255, 0.82);
  --glass-border: var(--border-subtle);
  --glass-blur:   12px;                     /* the brand's panel blur */

  --text-tertiary: var(--text-muted);
  --text-inverse:  var(--cream-100);

  /* index.css defines these four itself and loads AFTER tokens.css, so its
     values win over the token layer. They must be restated here — measured,
     not assumed: --text-secondary was resolving to #a0a0c8, a periwinkle left
     over from the indigo system, which is why badges and secondary buttons
     still read as purple after the remap. */
  --text-primary:   var(--espresso-900);
  --text-secondary: #4a3d31;
  --border-subtle:  rgba(138, 82, 48, 0.16);
  --border-strong:  rgba(138, 82, 48, 0.34);

  --border-default: var(--border-subtle);
  --border-focus:   var(--focus-ring);

  /* Radius scale from the brand: lg (0.5rem) dominant, md next, full for pills. */
  --radius-sm:   0.25rem;
  --radius-md:   0.375rem;
  --radius-lg:   0.5rem;
  --radius-xl:   0.75rem;
  --radius-full: 9999px;

  /* §8.3: the old --text-base was 0.9375rem (15px), which makes Safari on iOS
     zoom the page on every field focus. 16px is the threshold. */
  --text-base: 1rem;
  --text-sm:   0.875rem;
  --text-xs:   0.75rem;

  /* Warm shadows rather than neutral drops — the brand uses glow, not weight. */
  --shadow-sm: 0 1px 2px rgba(28, 22, 17, 0.06);
  --shadow-md: 0 4px 12px -4px rgba(28, 22, 17, 0.12);
  --shadow-lg: 0 12px 32px -12px rgba(28, 22, 17, 0.18);
  --shadow-xl: 0 20px 48px -16px rgba(28, 22, 17, 0.22);
}

/* =============================================================== dark theme */

.dark-theme {
  color-scheme: dark;

  --brand-primary:       var(--interactive);
  --brand-primary-light: var(--ember-300);
  --brand-primary-dark:  var(--clay-600);
  --brand-primary-glow:  var(--interactive-subtle);
  --brand-secondary:     var(--clay-500);
  --brand-accent:        var(--ember-400);

  --color-success:    var(--status-success-fg);
  --color-success-bg: var(--status-success-bg);
  --color-warning:    var(--status-warning-fg);
  --color-warning-bg: var(--status-warning-bg);
  --color-danger:     var(--status-danger-fg);
  --color-danger-bg:  var(--status-danger-bg);
  --color-info:       var(--status-info-fg);
  --color-info-bg:    var(--status-info-bg);

  --bg-base:      var(--surface-base);
  --bg-surface-1: var(--surface-1);
  --bg-surface-2: var(--surface-raised);
  --bg-surface-3: var(--espresso-600);
  --bg-elevated:  var(--surface-raised);
  --bg-hover:     var(--interactive-subtle);
  --bg-active:    rgba(193, 122, 75, 0.26);

  /* The brand's spec-sheet panel: warm dark, hairline border, faint top light. */
  --glass-bg:     rgba(28, 22, 17, 0.66);
  --glass-border: var(--border-subtle);

  --text-tertiary: var(--text-muted);
  --text-inverse:  var(--espresso-950);

  /* index.css defines these four itself and loads AFTER tokens.css, so its
     values win over the token layer. They must be restated here — measured,
     not assumed: --text-secondary was resolving to #a0a0c8, a periwinkle left
     over from the indigo system, which is why badges and secondary buttons
     still read as purple after the remap. */
  --text-primary:   var(--cream-200);
  --text-secondary: var(--cream-400);
  --border-subtle:  rgba(193, 122, 75, 0.16);
  --border-strong:  var(--espresso-500);

  --border-default: var(--border-subtle);
  --border-focus:   var(--focus-ring);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 4px 14px -4px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 14px 36px -12px rgba(0, 0, 0, 0.6);
  --shadow-xl: 0 0 55px -15px rgba(217, 142, 61, 0.35);   /* the brand glow */
}

/* ---------------------------------------------------------------------------
   Brand character

   The pieces of the main site's feel that are not just colour: the monospace
   tracked eyebrow, hairline rules that fade at the ends, and the inner top
   light on panels.
   --------------------------------------------------------------------------- */

.sidebar-brand-text span:last-child,
.sidebar-section-label,
.clause-badge {
  font-family: var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.6875rem;
}

.sidebar-section-label { color: var(--brand-eyebrow); }

.card,
.stat-tile {
  box-shadow: inset 0 1px 0 rgba(236, 227, 214, 0.04), var(--shadow-sm);
}

.section-divider::before,
.section-divider::after {
  background: linear-gradient(90deg, transparent,
    rgba(193, 122, 75, 0.35) 12%, rgba(193, 122, 75, 0.35) 88%, transparent);
}

::selection {
  background: rgba(193, 122, 75, 0.32);
  color: var(--text-primary);
}

/* ---------------------------------------------------------------------------
   §8.3 — touch and input

   Minimum 44x44px targets, and inputs at 16px so iOS does not zoom on focus.
   --------------------------------------------------------------------------- */

.form-input,
.form-select,
.form-textarea {
  font-size: max(1rem, var(--text-base));   /* never below the 16px iOS threshold */
  min-height: 44px;
  border-radius: var(--radius-md);
}
.form-textarea { min-height: 88px; }

.btn,
.topbar-btn,
.nav-item,
.filter-chip,
.modal-close {
  min-height: 44px;
  min-width: 44px;
}

.btn-sm {
  /* Small buttons keep their visual size but keep a full-size hit area. */
  min-height: 32px;
  position: relative;
}
.btn-sm::after {
  content: "";
  position: absolute;
  inset: -6px;
}

/* Row actions and the calibration "done" control were far below the minimum. */
.notif-item-dismiss,
table .btn {
  min-height: 44px;
  min-width: 44px;
}

/* ---------------------------------------------------------------------------
   §8.2 — dynamic viewport

   The auth and syncing overlays used 100vh, which on mobile is the viewport
   WITHOUT browser chrome — so the bottom of the sign-in card sat under the
   address bar. dvh tracks the visible area as the chrome collapses.
   --------------------------------------------------------------------------- */

.auth-overlay,
.syncing-overlay {
  height: 100dvh;
  min-height: 100svh;
  padding-bottom: env(safe-area-inset-bottom);
}

.app-layout {
  min-height: 100dvh;
}

/* ---------------------------------------------------------------------------
   §8.2 — navigation below the desktop breakpoint

   Below 1024px the sidebar collapsed to a 68px icon rail with its labels,
   section headings and badges hidden — forty unlabelled glyphs, no tooltips.
   A user reported it as impossible to navigate, which it was.

   The rail is gone entirely. Everything under 1024px now gets the off-canvas
   drawer, which is fully labelled; phones additionally get the bottom tab bar
   for the handful of things an operator does standing at a machine.

   The lesson worth keeping: an icon-only rail needs icons that are legible on
   their own and few enough to memorise. Forty entries is neither.
   --------------------------------------------------------------------------- */

.mobile-nav-toggle,
.bottom-nav { display: none; }

@media (max-width: 1024px) {
  .app-layout { grid-template-columns: 1fr; }

  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(84vw, 20rem);
    z-index: 300;
    transform: translateX(-100%);
    transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
    padding-top: env(safe-area-inset-top);
    overflow-y: auto;
  }
  .sidebar.open { transform: translateX(0); }

  .sidebar-scrim {
    position: fixed;
    inset: 0;
    background: rgba(21, 16, 12, 0.6);
    z-index: 299;
  }

  .mobile-nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    background: none;
    border: 0;
    color: var(--text-primary);
    font-size: 1.25rem;
  }

  .topbar { padding-left: 0.5rem; }
  .topbar-title { font-size: var(--text-lg); }
  .search-bar input { width: 100%; min-width: 0; }

  .content {
    padding: 1rem 1rem calc(4.5rem + env(safe-area-inset-bottom));
  }

  .bottom-nav {
    display: flex;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 200;
    background: var(--bg-surface-1);
    border-top: 1px solid var(--border-subtle);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .bottom-nav a {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    min-height: 56px;
    font-size: 0.6875rem;
    color: var(--text-muted);
    text-decoration: none;
  }
  .bottom-nav a[aria-current="page"] { color: var(--interactive); }
  .bottom-nav-icon { font-size: 1.25rem; line-height: 1; }

  /* §8.2: modals become full-screen sheets rather than overflowing. */
  .modal-overlay .modal {
    position: fixed;
    inset: 0;
    max-width: none;
    max-height: none;
    width: 100%;
    height: 100dvh;
    border-radius: 0;
    display: flex;
    flex-direction: column;
  }
  .modal-body { flex: 1; overflow-y: auto; }
  .modal-footer { padding-bottom: calc(1rem + env(safe-area-inset-bottom)); }

  /* §8.2: wide tables stack into cards. Several lists have 8-11 columns, and
     sideways scrolling inside a card is not a usable answer on a phone. */
  .table-wrapper table,
  .table-wrapper thead,
  .table-wrapper tbody,
  .table-wrapper th,
  .table-wrapper td,
  .table-wrapper tr { display: block; }

  .table-wrapper thead {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }
  .table-wrapper tr {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    margin-bottom: 0.75rem;
    padding: 0.75rem;
    background: var(--bg-surface-1);
  }
  .table-wrapper td {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.35rem 0;
    border: 0;
    text-align: right;
  }
  /*
   * The heading, echoed from data-label — stamped from the table's own <thead>
   * by QMSApp.labelTableCells(), so a cell does not have to carry it by hand.
   *
   * `margin-right: auto` rather than `justify-content: space-between`: a cell
   * holding two things (a Type badge plus its kinematics, a Cal Due pill plus
   * "Done") has three flex children, and space-between scatters all three
   * across the row. Pushing off the label alone keeps the value side together
   * on the right where it reads as one answer.
   */
  .table-wrapper td::before {
    content: attr(data-label);
    font-weight: 600;
    color: var(--text-muted);
    text-align: left;
    margin-right: auto;
    /* The label must not be squeezed to one word per line by a long value. */
    flex: 0 0 auto;
  }
  /* An empty cell would otherwise collapse to just its label with no sign that
     the value is genuinely blank. */
  .table-wrapper td:empty::after {
    content: '—';
    color: var(--text-muted);
  }
  .table-wrapper td.primary-cell { font-size: 1rem; }
}

/*
  Tablet (768–1024px): the drawer above applies, so the sidebar is labelled.
  What does NOT apply at this size is the phone furniture — a bottom tab bar,
  full-screen modal sheets and a shrunken search box are right on a 390px
  handset and wrong on a 900px laptop window, which has room for all of it.

  This block undoes those specifically, rather than the drawer being duplicated
  into two nearly-identical breakpoints that then drift apart.
*/
@media (min-width: 768px) and (max-width: 1024px) {
  .bottom-nav { display: none; }
  .content { padding: var(--space-lg, 1.5rem); }
  .search-bar { flex: 0 1 16rem; }

  .modal-overlay .modal {
    position: relative;
    inset: auto;
    width: auto;
    height: auto;
    max-width: 40rem;
    max-height: 85vh;
    border-radius: var(--radius-lg, 12px);
  }
}

/* ---------------------------------------------------------------------------
   §8.5 — accessibility
   --------------------------------------------------------------------------- */

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 400;
  padding: 0.75rem 1rem;
  background: var(--surface-1);
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: 0 0 var(--radius-lg) 0;
}
.skip-link:focus { left: 0; }

/* The badge system conveyed status with colour plus a dot. The glyphs in
   tokens.css carry it in shape too; this keeps them legible at badge size. */
.badge { gap: 0.3em; }

/* §8.2: browser zoom to 200% and OS large-text must not break the layout. */
@media (max-width: 1400px) {
  .stat-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); }
}

/* Honour reduced motion for the staggered entrance the shell uses. */
@media (prefers-reduced-motion: reduce) {
  .animate-fade-in,
  .animate-fade-in > * { animation: none !important; }
}

/* ---------------------------------------------------------------------------
   Hardcoded leftovers

   A handful of rules in index.css use literal indigo rather than a variable,
   so the remap cannot reach them. Overridden here rather than edited in place,
   to keep this bridge the single file to delete when the component library
   lands.
   --------------------------------------------------------------------------- */

.badge-primary {
  background: var(--interactive-subtle);
  color: var(--brand-primary-light);
}

.auth-hero-brand .brand-name,
.sidebar-brand-icon,
.auth-hero-logo {
  background: linear-gradient(135deg, var(--clay-400) 0%, var(--ember-400) 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

.sidebar-brand-icon,
.auth-hero-logo {
  /* These are filled shapes, not text — keep the gradient as a background. */
  -webkit-text-fill-color: var(--espresso-950);
  color: var(--espresso-950);
}

/* ---------------------------------------------------------------------------
   Phone topbar overflow

   At 390px the topbar's four controls plus a two-line title pushed the
   notification bell off the right edge — found by screenshotting rather than
   by reasoning about widths. The title truncates to one line, and quick-add is
   dropped because the bottom bar already carries the actions it duplicates.
   --------------------------------------------------------------------------- */

@media (max-width: 767px) {
  .topbar {
    gap: 0.25rem;
    padding-right: 0.5rem;
    padding-top: env(safe-area-inset-top);
  }
  .topbar-left { min-width: 0; flex: 1; }
  .topbar-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 1rem;
  }
  .topbar-right { flex: 0 0 auto; gap: 0.25rem; }
  #quick-add-btn { display: none; }
  .topbar-btn { padding-left: 0.5rem; padding-right: 0.5rem; }
}

/* The topbar search sets its own font-size and so escaped the .form-input rule
   above — 14px, which is under the iOS zoom threshold like everything else. */
.search-bar input,
#global-search {
  font-size: max(1rem, var(--text-base));
}

/* ---------------------------------------------------------------------------
   Card header action groups

   `.record-header-actions` was referenced by the schema factory's detail
   header and by the readiness screen but was never given any layout, so its
   buttons stacked and overlapped the subtitle beside them — found by
   screenshotting the readiness screen. Giving it a wrapping flex row fixes
   both callers, and letting `.card-header` wrap keeps the title and the
   actions from colliding at narrow widths.
   --------------------------------------------------------------------------- */

.card-header {
  flex-wrap: wrap;
  gap: 0.75rem;
}

.record-header-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  flex: 0 0 auto;
}

@media (max-width: 767px) {
  .record-header-actions { width: 100%; }
  .record-header-actions .btn { flex: 1 1 auto; }
}

/* ---------------------------------------------------------------------------
   Dashboard split (§10.8)

   The alerts/activity pair was an inline `grid-template-columns: 1fr 1fr` with
   no responsive collapse, so at 390px each column was about 170px wide and
   every row wrapped to four lines. It stacks below the tablet breakpoint.
   --------------------------------------------------------------------------- */

.dashboard-split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: var(--space-lg, 1.5rem);
  align-items: start;
}

/* ---------------------------------------------------------------------------
   Navigation rework

   The reported problem: below 1024px the sidebar was a 68px rail with every
   label hidden and no tooltips — forty emoji, indistinguishable. Above 1024px
   it was a flat, unlabelled-by-description list two screens tall.

   What changed: real icons that inherit currentColor, a one-line description
   under every label, and sections that collapse so the list is short. The
   section you are in opens itself.
   --------------------------------------------------------------------------- */

.qms-icon {
  display: block;
  flex: 0 0 auto;
  /* Optical alignment: a 20px stroke icon sits slightly high against text. */
  margin-top: 1px;
}

/* ---- section headers, now buttons ---- */

.sidebar-section-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
  background: none;
  border: 0;
  cursor: pointer;
  padding: 0.7rem 0.9rem 0.35rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: left;
}

.sidebar-section-label:hover { color: var(--text-secondary); }

.sidebar-section-chevron {
  display: flex;
  transition: transform 160ms ease;
  transform: rotate(-90deg);   /* pointing right = collapsed */
  opacity: 0.7;
}

.sidebar-section.is-open .sidebar-section-chevron {
  transform: rotate(-270deg);  /* pointing down = expanded */
}

/* Collapsed sections are removed from the flow rather than hidden, so they
   take no space and their links are not tab stops. */
.sidebar-section:not(.is-open) .sidebar-section-items { display: none; }

/* ---- nav rows ---- */

.sidebar-nav .nav-item {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
  padding: 0.5rem 0.9rem;
  border-radius: 0.5rem;
  margin: 0 0.4rem 1px;
  color: var(--text-secondary);
  text-decoration: none;
  /* 44px minimum touch target, met by padding plus two text lines. */
  min-height: 44px;
}

.sidebar-nav .nav-item:hover {
  background: var(--interactive-subtle);
  color: var(--text-primary);
}

.sidebar-nav .nav-item.active {
  background: var(--interactive-subtle);
  color: var(--text-primary);
  /* The rule carries the selection as well as the tint, so it survives
     forced-colors mode and reads at a glance. */
  box-shadow: inset 3px 0 0 var(--interactive);
}

.nav-item-icon {
  display: flex;
  color: var(--text-muted);
}

.nav-item:hover .nav-item-icon,
.nav-item.active .nav-item-icon { color: var(--interactive); }

.nav-item-text {
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  min-width: 0;
  flex: 1;
}

.nav-item-label {
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.3;
}

/* The answer to "I have no idea what each page does". Kept to one line so
   forty rows stay scannable; the full sentence is in the row's title. */
.nav-item-desc {
  font-size: 0.6875rem;
  line-height: 1.35;
  color: var(--text-muted);
  /* Wraps to at most two lines rather than truncating. An ellipsis after
     three words ("Today at a glance — counts, ale…") does not answer the
     question the description exists to answer. Sections collapse, so the
     extra height costs nothing on a normal screen.

     `white-space: normal` is load-bearing: .nav-item sets `nowrap` and it
     inherits, so the clamp below had nothing to wrap and every description
     stayed on one clipped line. */
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.nav-item.active .nav-item-desc { color: var(--text-secondary); }

/* A collapsed-by-choice sidebar keeps the icons only — but every row carries a
   title attribute now, so hovering still says what it is. */
.sidebar.collapsed .nav-item-desc,
.sidebar.collapsed .nav-item-text,
.sidebar.collapsed .sidebar-section-chevron { display: none; }
.sidebar.collapsed .sidebar-section-label { justify-content: center; padding: 0.6rem 0; }
.sidebar.collapsed .nav-item { justify-content: center; padding: 0.6rem; }

/* ---- chrome icons ---------------------------------------------------------
   The topbar buttons, drawer toggle and bottom bar carried emoji, which meant
   they could not take the button's colour and rendered at a different weight
   on every platform. They are icons now, so they inherit currentColor.
   --------------------------------------------------------------------------- */

/*
  Deliberately no `display` here. An earlier version set `display: inline-flex`
  on this list, which overrode `.mobile-nav-toggle { display: none }` above and
  put a hamburger in the desktop topbar — caught by the responsive test and by
  looking at a screenshot. Centring is applied without touching the property
  that decides whether the control exists at this width.
*/
.topbar-btn,
#sidebar-toggle-btn,
.modal-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
}

.mobile-nav-toggle {
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
}

.topbar-btn:hover,
.mobile-nav-toggle:hover,
#sidebar-toggle-btn:hover,
.modal-close:hover { color: var(--text-primary); }

/* The close button reuses the plus icon rotated, so the set carries one glyph
   instead of two that must be kept visually consistent by hand. */
.modal-close .qms-icon { transform: rotate(45deg); }

/* Collapsing points the chevron the way the panel will move. */
.sidebar.collapsed #sidebar-toggle-btn .qms-icon { transform: rotate(180deg); }

.search-bar-icon {
  display: inline-flex;
  align-items: center;
  color: var(--text-muted);
}

.bottom-nav-icon { display: flex; }

/* ---- sidebar width --------------------------------------------------------
   260px left about 170px for a description once the icon and padding were
   taken out, which truncated most of them after three words. The extra 40px
   is the difference between a useful subtitle and an ellipsis.
   --------------------------------------------------------------------------- */

@media (min-width: 1025px) {
  :root { --sidebar-width: 300px; }
}

/* Empty-state glyphs: muted, so an empty list reads as calm rather than as an
   error. They were emoji at 3xl, which shouted. */
.empty-glyph,
.empty-state-icon,
.notif-empty-icon {
  display: flex;
  justify-content: center;
  color: var(--text-muted);
  margin-bottom: 0.5rem;
}

/* A stroked icon alone on a wide card reads as a stray mark. Seating it on a
   soft disc gives the empty state something to centre on, and lets the glyph
   keep full contrast — the old rule dropped it to opacity 0.3 to tame a 48px
   emoji, which left the SVG that replaced it almost invisible. */
.empty-state .empty-state-icon {
  width: 64px;
  height: 64px;
  margin: 0 auto var(--space-md);
  align-items: center;
  border-radius: 50%;
  background: var(--surface-sunken, var(--bg-tertiary));
  color: var(--text-tertiary);
}

.toast-icon { display: flex; align-items: center; }
.toast-success .toast-icon { color: var(--status-success-fg); }
.toast-warning .toast-icon { color: var(--status-warning-fg); }
.toast-danger  .toast-icon { color: var(--status-danger-fg); }
.toast-info    .toast-icon { color: var(--status-info-fg); }
