/* AP Calendar — shared design tokens & primitives
   Extracted from purina-post-calendar.html so both admin
   and calendar views share one source of truth.

   Token architecture (3 layers):
     1. PRIMITIVE   — raw values (--navy, --beige, --accent-green …)
     2. SEMANTIC    — role-based (--surface-card, --btn-fill …)
                      these are what the Customize tab exposes
     3. USER        — injected at runtime via <style id="ap-user-theme">
                      from /user_prefs/{uid}/theme; overrides any
                      semantic token without touching the defaults */

/* Vendored Inter - matches the PDF export font exactly. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/vendor/fonts/Inter-Regular.ttf") format("truetype");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/vendor/fonts/Inter-Medium.ttf") format("truetype");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/vendor/fonts/Inter-Bold.ttf") format("truetype");
}

/* Curated alternate fonts for the Customize tab. Lazy-loaded via
   Google Fonts when the user picks them so the default Inter path
   stays fast for everyone else. URLs live in theme.js; the
   @font-face declarations get injected on demand. */

:root {
  --navy:     #1a2e3d;
  --navy-mid: #1f3a4d;
  --beige:    #e8e0d0;
  --white:    #ffffff;
  --border:   rgba(var(--navy-rgb),0.11);
  --font:     "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  /* Mobile/responsive breakpoint. Everything narrower than this
     switches to the mobile-friendly layout (drawer sidebar,
     stacked grids, card-style list rows). Above this, the layout
     is pixel-identical to the legacy desktop one. */
  --bp-narrow: 720px;

  /* Theme tokens — the navy RGB triplet feeds the hundreds of
     `rgba(var(--navy-rgb), X)` colors scattered throughout admin.css and
     collaborator.css. Flipping the triplet under .dark-mode is what
     re-skins every border/muted-text instance at once. */
  --navy-rgb: 26, 46, 61;
  --beige-rgb: 232, 224, 208;
  --black-rgb: 0, 0, 0;
  --white-rgb: 255, 255, 255;

  /* Semantic surfaces */
  --bg-page: #eae4d9;            /* outermost body background */
  --bg-cream: #f9f7f2;           /* admin content area */
  --bg-card: #ffffff;            /* cards, panels, modals */
  --bg-input: #ffffff;
  --bg-elevated: #ffffff;

  /* Text */
  --text-primary: #0a3254;
  --text-on-dark: #ffffff;       /* text drawn on .page-header navy bar */
  --ink: #1a2e3d;                /* default ink color for body copy */

  /* Status accents (kept in light mode, replaced in dark) */
  --accent-green: #287850;
  --accent-green-deep: #1d6f49;
  --accent-red: #c0392b;
  --accent-red-deep: #a93226;
  --accent-blue: #2e6da4;
  --accent-amber: #c97a00;

  /* Card radius / shadow — bumped up only in dark mode */
  --card-radius: 8px;
  --card-shadow: 0 1px 2px rgba(var(--navy-rgb),.05);
}

/* ── Dark mode ───────────────────────────────────────────────────
   Activated by toggling `dark-mode` on <body>. Only token VALUES
   change here — component selectors keep their existing structure.
   Surface/border overrides for hardcoded `#fff` rules live at the
   bottom of admin.css and collaborator.css (search "DARK MODE"). */
.dark-mode {
  --navy-rgb: 138, 154, 176;     /* muted slate-blue replaces navy ink */
  --beige-rgb: 138, 154, 176;
  --white-rgb: 13, 21, 32;        /* "white" surfaces become dark navy */

  --navy:       #0d1520;          /* dark navy stays the header surface */
  --navy-mid:   #111d2b;
  --beige:      #ffffff;          /* "beige" text on the header is now white */
  --white:      #ffffff;          /* still literally white — header-center h1 etc. */
  --border:     rgba(255,255,255,0.06);
  --ink:        #ffffff;          /* text flips to white in dark mode */

  --bg-page:     #050810;
  --bg-cream:    #050810;
  --bg-card:     rgba(13, 21, 32, 0.7);
  --bg-input:    #0f1a26;
  --bg-elevated: #111d2b;

  --text-primary: #ffffff;
  --text-on-dark: #ffffff;

  --accent-green:      #00e87a;
  --accent-green-deep: #00c4b4;
  --accent-red:        #ff4d4d;
  --accent-red-deep:   #ff4d4d;
  --accent-blue:       #00c4b4;
  --accent-amber:      #f59e0b;

  --card-radius: 16px;
  --card-shadow:
    0 4px 24px rgba(0, 0, 0, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.04);
}

/* ═══════════════════════════════════════════════════════════════
   SEMANTIC TOKEN LAYER — the customizer's contract

   Every value here is what the user sees and can override via the
   Customize tab. They map onto the primitives above so toggling
   dark mode still works without re-defining each token. The user
   layer (<style id="ap-user-theme"> injected by theme.js) sits
   AFTER this in the cascade and wins by source order.

   Each group corresponds to a panel in the Customize UI.
   ═══════════════════════════════════════════════════════════════ */

:root {
  /* ── SURFACES ── (6) */
  --surface-page:               var(--bg-page);
  --surface-card:               var(--bg-card);
  --surface-card-border:        rgba(var(--navy-rgb), 0.10);
  --surface-input:              var(--bg-input);
  --surface-input-border:       rgba(var(--navy-rgb), 0.20);
  --surface-input-border-focus: rgba(var(--navy-rgb), 0.55);

  /* ── TEXT ── (4) */
  --text-body:    var(--ink);
  --text-muted:   rgba(var(--navy-rgb), 0.55);
  --text-label:   rgba(var(--navy-rgb), 0.65);
  /* Hyperlink colour — applies to invoice links + linked-doc
     links. Overrides the browser default (incl. visited purple)
     so links read at a consistent brand-aware tone. */
  --link-color:   #5eb1ff;

  /* ── BRAND ── (3) */
  --brand-primary:   var(--navy);
  --brand-secondary: var(--beige);
  --brand-accent:    var(--accent-green);

  /* ── ACTIONS ── (4) */
  --btn-fill:         var(--brand-primary);
  --btn-text:         var(--brand-secondary);
  --btn-hover:        var(--navy-mid);
  --btn-ghost-border: rgba(var(--navy-rgb), 0.22);

  /* ── STATUS ── (4) */
  --status-success: var(--accent-green);
  --status-warning: var(--accent-amber);
  --status-danger:  var(--accent-red);
  --status-info:    var(--accent-blue);

  /* ── EFFECTS ── (3) */
  /* --card-radius already defined above. */
  --blur-intensity: 12px;
  --shadow-opacity: 0.08;

  /* ── AMBIENT ── (4) */
  /* Three hue stops for the radial gradient drift on dark mode.
     Defaults mirror the prior teal / forest-green / purple wash.
     --ambient-enabled is 1 (on) / 0 (off) — multiplied into the
     gradient's alpha so the user can mute it entirely. */
  --ambient-hue-1:   #006437;
  --ambient-hue-2:   #00c4b4;
  --ambient-hue-3:   #3c1964;
  --ambient-enabled: 1;

  /* ── TYPE ── (1) */
  --font-active: var(--font);
}

/* Dark-mode-only semantic overrides. Most semantic tokens still
   map through their primitives (--navy, --beige, etc.) which already
   flip under .dark-mode, so we only override here when the dark-mode
   value isn't a simple primitive flip. */
.dark-mode {
  --surface-card:               rgba(255, 255, 255, 0.06);
  --surface-card-border:        rgba(255, 255, 255, 0.11);
  --surface-input:              rgba(8, 13, 20, 0.6);
  --surface-input-border:       rgba(255, 255, 255, 0.10);
  --surface-input-border-focus: rgba(255, 255, 255, 0.30);

  --text-muted: rgba(255, 255, 255, 0.55);
  --text-label: rgba(255, 255, 255, 0.65);
  --link-color: #6ab9ff;   /* a touch lighter on dark for contrast */

  --btn-ghost-border: rgba(255, 255, 255, 0.18);

  --shadow-opacity: 0.40;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 14px; }
body {
  font-family: var(--font-active);
  background: var(--bg-page);
  /* Was: min-width: 900px (forced horizontal scroll on phones).
     Removed for mobile. Desktop layouts already happily render
     wider than this so removal has no visible desktop effect. */
  color: var(--ink);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ── HEADER ───────────────────────────────────────────────── */
.page-header {
  background: var(--navy);
  padding: 18px 36px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
}
.header-left {
  display: flex;
  align-items: center;
  gap: 12px;
}
.logo { display: flex; align-items: center; }
.logo svg.logo-full { height: 46px; width: auto; display: block; }

/* Hamburger button — hidden by default; revealed at narrow widths
   via the @media block at the bottom of this file. Sits in the
   header's left cell next to the logo. */
.hamburger-btn {
  display: none;
  background: none;
  border: 1px solid rgba(var(--beige-rgb),.25);
  border-radius: 6px;
  color: var(--beige);
  padding: 6px;
  cursor: pointer;
  line-height: 0;
  flex-shrink: 0;
}
.hamburger-btn svg { width: 22px; height: 22px; }
.hamburger-btn:hover {
  background: rgba(var(--beige-rgb),.06);
  border-color: rgba(var(--beige-rgb),.45);
}
.ap { fill: var(--beige); }
.header-center { text-align: center; }
.header-center h1 {
  font-size: 17px; font-weight: 300; color: var(--white);
  letter-spacing: .5px; white-space: nowrap;
}
.header-center h1 strong { font-weight: 700; }
.header-center .header-sub {
  font-size: 10px; font-weight: 500; letter-spacing: 2.5px;
  color: rgba(var(--beige-rgb),.45); text-transform: uppercase; margin-top: 5px;
}
.header-right {
  display: flex; align-items: center; gap: 12px; justify-content: flex-end;
}

/* ── BUTTONS ──────────────────────────────────────────────── */
.btn-primary {
  background: var(--btn-fill); color: var(--btn-text); border: none;
  padding: 9px 16px; border-radius: 3px; font-family: var(--font-active);
  font-size: 10px; font-weight: 700; letter-spacing: 1.2px;
  text-transform: uppercase; cursor: pointer;
  transition: background .15s, transform .1s;
}
.btn-primary:hover  { background: var(--btn-hover); }
.btn-primary:active { transform: scale(.97); }

.btn-ghost {
  background: none; border: 1px solid var(--btn-ghost-border); border-radius: 3px;
  padding: 6px 12px; font-family: var(--font-active); font-size: 10px;
  font-weight: 600; color: var(--text-body); cursor: pointer;
  transition: all .12s; letter-spacing: .3px; text-transform: uppercase;
}
.btn-ghost:hover { background: rgba(var(--navy-rgb),.06); border-color: rgba(var(--navy-rgb),.35); }

.btn-ghost-light {
  background: none; border: 1px solid rgba(var(--beige-rgb),.35);
  border-radius: 3px; padding: 6px 12px; font-family: var(--font-active);
  font-size: 10px; font-weight: 600; color: rgba(var(--beige-rgb),.85);
  cursor: pointer; transition: all .12s; letter-spacing: .3px;
  text-transform: uppercase;
}
.btn-ghost-light:hover { background: rgba(var(--beige-rgb),.08); color: var(--beige); }

/* Primary action on a dark header. Solid beige fill + navy text reads
   as the obvious "do this" target against the navy setup-head bar.
   Same shape/sizing as .btn-ghost-light so it composes neatly with
   neighboring secondary buttons. */
.btn-save-light {
  background: var(--beige); color: var(--ink);
  border: 1px solid var(--beige); border-radius: 3px;
  padding: 6px 14px; font-family: var(--font);
  font-size: 10px; font-weight: 700; letter-spacing: .8px;
  text-transform: uppercase; cursor: pointer;
  transition: background .12s, border-color .12s, transform .1s;
}
.btn-save-light:hover  { background: #f1e9d6; border-color: #f1e9d6; }
.btn-save-light:active { transform: scale(.97); }

.btn-close-form {
  background: none; border: none; padding: 4px 8px;
  font-size: 16px; line-height: 1; color: rgba(var(--beige-rgb),.55);
  cursor: pointer; border-radius: 3px; transition: color .12s, background .12s;
  display: flex; align-items: center; justify-content: center;
}
.btn-close-form:hover { color: var(--beige); background: rgba(var(--beige-rgb),.1); }

.btn-danger {
  background: none; border: 1.5px solid rgba(169,50,38,.35);
  color: #a93226; padding: 7px 14px; border-radius: 3px;
  font-family: var(--font); font-size: 10px; font-weight: 700;
  letter-spacing: .8px; text-transform: uppercase; cursor: pointer;
  transition: all .12s;
}
.btn-danger:hover { background: rgba(169,50,38,.07); border-color: rgba(169,50,38,.6); }

/* ── AP-styled confirmation modal ──────────────────────────
   Drop-in replacement for the browser's confirm(). Used by
   confirmModal() in toast.js; markup is built on demand. */
.ap-modal-root {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  animation: ap-modal-in .14s ease-out;
}
.ap-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(15, 25, 35, .55);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.ap-modal-card {
  position: relative;
  width: 100%;
  max-width: 400px;
  background: #fff;
  border-radius: 6px;
  box-shadow: 0 12px 48px rgba(0,0,0,.28), 0 4px 12px rgba(0,0,0,.10);
  padding: 22px 22px 18px;
  animation: ap-modal-card-in .18s ease-out;
}
.ap-modal-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -.1px;
}
.ap-modal-body {
  font-size: 13px;
  font-weight: 400;
  color: rgba(var(--navy-rgb),.72);
  line-height: 1.5;
  margin-top: 8px;
  white-space: pre-wrap;
}
.ap-modal-body:empty { display: none; }
.ap-modal-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 22px;
}
.ap-modal-btn {
  font-family: var(--font);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  border-radius: 3px;
  padding: 9px 16px;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background .12s, border-color .12s, transform .1s;
}
.ap-modal-btn:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--navy-rgb),.18);
}
.ap-modal-btn:active { transform: scale(.98); }
.ap-modal-btn-ghost {
  background: none;
  border-color: rgba(var(--navy-rgb),.25);
  color: var(--ink);
}
.ap-modal-btn-ghost:hover { background: rgba(var(--navy-rgb),.06); border-color: rgba(var(--navy-rgb),.4); }
.ap-modal-btn-primary {
  background: var(--navy);
  color: var(--beige);
  border-color: var(--ink);
}
.ap-modal-btn-primary:hover { background: var(--navy-mid); border-color: var(--navy-mid); }
.ap-modal-btn-primary.is-danger {
  background: #a93226;
  border-color: #a93226;
  color: #fff;
}
.ap-modal-btn-primary.is-danger:hover { background: #8e2a20; border-color: #8e2a20; }
.ap-modal-btn-primary.is-danger:focus { box-shadow: 0 0 0 3px rgba(169,50,38,.25); }

@keyframes ap-modal-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes ap-modal-card-in {
  from { opacity: 0; transform: translateY(8px) scale(.98); }
  to   { opacity: 1; transform: translateY(0)   scale(1);  }
}

.btn-modal {
  border: none; padding: 8px 16px; border-radius: 3px;
  font-family: var(--font); font-size: 10px; font-weight: 700;
  letter-spacing: .8px; text-transform: uppercase; cursor: pointer;
  transition: background .12s, transform .1s;
}
.btn-modal:active { transform: scale(.97); }
.btn-save   { background: var(--navy); color: var(--beige); }
.btn-save:hover { background: #122030; }
.btn-cancel { background: #eae6de; color: var(--ink); }
.btn-cancel:hover { background: #e0dbd0; }

/* ── FORM INPUTS ─────────────────────────────────────────── */
/* Labels: slightly more presence (color bumped from .5 to .65
   so they read as real labels, not whisper text). Consistent
   line-height so labels in side-by-side cells share a baseline
   regardless of wrap. */
.field-label {
  display: block;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: rgba(var(--navy-rgb),.65);
  line-height: 1.4;
  margin-bottom: 7px;
  min-height: 14px;
}
/* Inputs: focus halo (extra to the border-color flip) provides a
   "you're typing here" cue without a heavy outline. The motion
   pass in shared.css adds the navy form-specific halo; this base
   rule keeps non-admin inputs visually consistent. */
.text-input, .select-input {
  width: 100%;
  padding: 9px 11px;
  /* Surface tokens — the Customize tab's "Input background" and
     "Input border" controls land here. */
  background: var(--surface-input, white);
  border: 1.5px solid var(--surface-input-border, rgba(var(--navy-rgb),.2));
  border-radius: 4px;
  font-family: var(--font-active);
  font-size: 12px;
  color: var(--text-body);
  transition: border-color .12s, box-shadow .12s;
  appearance: none;
}
.text-input:focus, .select-input:focus {
  outline: none;
  border-color: var(--surface-input-border-focus, var(--ink));
}
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start; }
.field-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; align-items: start; }
/* Vertical breathing room between stacked field-rows / field-groups.
   Bumped from 8 to 12 so stacked rows feel deliberately separated
   rather than crammed. Scoped to direct section-block descendants
   so side-by-side groups inside a .field-row don't get pushed
   off-baseline. */
.section-block > .field-row + .field-row,
.section-block > .field-row + .field-group,
.section-block > .field-group + .field-row,
.section-block > .field-group + .field-group { margin-top: 12px; }

/* color-swatch wrapper around hidden <input type="color"> */
.color-swatch-btn {
  width: 32px; height: 22px; border-radius: 3px;
  flex-shrink: 0; display: block; position: relative; overflow: hidden;
  cursor: pointer; transition: transform .12s;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.15);
}
.color-swatch-btn:hover { transform: scale(1.1); }
.color-swatch-btn input[type="color"] {
  opacity: 0; position: absolute; inset: -4px;
  width: calc(100% + 8px); height: calc(100% + 8px);
  cursor: pointer; border: none; padding: 0;
}

/* ── FOOTER ──────────────────────────────────────────────── */
.page-footer {
  background: var(--navy); padding: 13px 36px;
  display: flex; justify-content: space-between; align-items: center;
  border-top: 3px solid rgba(var(--beige-rgb),.12);
  margin-top: auto;
}
.footer-text {
  font-size: 9px; color: rgba(var(--beige-rgb),.3);
  letter-spacing: 1.5px; text-transform: uppercase;
}
.footer-center {
  font-size: 9px; color: rgba(var(--beige-rgb),.2);
  letter-spacing: 1px; text-transform: uppercase; text-align: center;
}

/* ── TOAST ───────────────────────────────────────────────── */
/* Compound layout: icon + message + optional Undo button. The
   colored left border is the kind signal; the icon reinforces it
   without forcing the user to read the text. */
#toast {
  position: fixed; bottom: 22px; left: 50%;
  transform: translateX(-50%) translateY(8px);
  background: var(--navy); color: var(--beige);
  padding: 8px 14px 8px 12px; border-radius: 20px;
  font-size: 11px; font-weight: 500; letter-spacing: .3px;
  opacity: 0; pointer-events: none; white-space: nowrap;
  transition: opacity .2s, transform .2s; z-index: 9999;
  display: inline-flex; align-items: center; gap: 9px;
  border-left: 3px solid var(--beige);     /* default */
  box-shadow: 0 4px 16px rgba(0,0,0,.18);
}
#toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;                     /* allow clicking Undo */
}
.toast-icon {
  display: inline-flex; align-items: center;
  flex-shrink: 0; line-height: 0;
  color: var(--beige);
}
.toast-msg { line-height: 1.2; }
.toast-undo {
  background: none; border: 1px solid rgba(var(--beige-rgb),.35);
  color: var(--beige); font-family: var(--font);
  font-size: 9.5px; font-weight: 700; letter-spacing: 1.2px;
  text-transform: uppercase; padding: 3px 8px; border-radius: 3px;
  cursor: pointer; margin-left: 4px;
  transition: background .12s, border-color .12s;
}
.toast-undo:hover { background: rgba(var(--beige-rgb),.12); border-color: var(--beige); }

/* Per-kind border + icon tint */
#toast.toast-success { border-left-color: #4baf5a; }
#toast.toast-success .toast-icon { color: #6ed084; }
#toast.toast-delete  { border-left-color: #c0392b; }
#toast.toast-delete  .toast-icon { color: #e57463; }
#toast.toast-sync    { border-left-color: #2e6da4; }
#toast.toast-sync    .toast-icon { color: #5b9ed8; }
#toast.toast-warn    { border-left-color: #d4a437; }
#toast.toast-warn    .toast-icon { color: #f0c264; }
#toast.toast-info    { border-left-color: var(--beige); }

/* ── DIALOG / MODAL BASE ─────────────────────────────────── */
.modal-dialog {
  border: none; border-radius: 6px; padding: 0;
  box-shadow: 0 8px 32px rgba(0,0,0,.28), 0 2px 8px rgba(0,0,0,.14);
  overflow: hidden;
  max-height: 92vh;
  /* The * { margin: 0 } reset above wipes out the UA's
     `dialog { margin: auto }` rule that centers showModal() dialogs in
     the inset:0 containing block. Restore it. */
  margin: auto;
}
.modal-dialog::backdrop { background: rgba(var(--navy-rgb),.52); }
.modal-head {
  background: var(--navy); color: var(--white);
  padding: 14px 18px 13px;
  display: flex; align-items: center; justify-content: space-between;
  cursor: move;
  user-select: none;
}
.modal-head-title {
  font-size: 12px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase;
}
.modal-close {
  background: none; border: none; color: rgba(var(--beige-rgb),.6);
  font-size: 20px; cursor: pointer; line-height: 1;
  padding: 0 2px; transition: color .12s;
}
.modal-close:hover { color: var(--beige); }
.modal-body {
  padding: 20px 18px 4px;
  max-height: calc(92vh - 130px);
  overflow-y: auto;
}
.modal-foot {
  padding: 14px 18px 18px; display: flex; align-items: center;
  justify-content: space-between;
  border-top: 1px solid rgba(var(--navy-rgb),.08); margin-top: 4px;
}
.modal-foot-right { display: flex; gap: 8px; }

/* ── APP SHELL (sidebar + main content row) ──────────────── */
.app-shell {
  display: flex;
  flex: 1;
  min-height: 0;       /* allow inner scroll without ballooning */
}
.app-sidebar {
  width: 220px;
  flex-shrink: 0;
  background: var(--navy-mid);
  border-right: 1px solid rgba(0,0,0,.25);
  display: flex;
  flex-direction: column;
  box-shadow: 1px 0 0 rgba(var(--beige-rgb),.04) inset;
}
/* Main content takes the remaining width. Width comes from flex
   sizing now, not from the historical full-page assumption. */
.app-shell > .main-content {
  flex: 1;
  min-width: 0;
}

/* ── TAB STRIP — vertical sidebar nav ─────────────────────── */
.tab-strip {
  background: transparent;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 14px 0 8px;
  border-bottom: none;
  box-shadow: none;
}
.tab-strip-btn {
  position: relative;
  background: none;
  border: none;
  padding: 11px 24px 11px 25px;
  margin: 0;
  text-align: left;
  font-family: var(--font);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: rgba(var(--beige-rgb),.55);
  cursor: pointer;
  border-bottom: none;
  border-left: 3px solid transparent;
  transition: color .14s, background .14s, border-color .14s;

  /* Icon + label sit side-by-side; the SVG inherits color via
     currentColor so hover and active states tint both at once. */
  display: flex;
  align-items: center;
  gap: 12px;
}
.tab-icon {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  opacity: .85;
  transition: opacity .14s;
}
.tab-strip-btn:hover .tab-icon,
.tab-strip-btn.is-active .tab-icon {
  opacity: 1;
}
.tab-strip-btn:hover {
  color: var(--beige);
  background: rgba(var(--beige-rgb),.04);
}
.tab-strip-btn.is-active {
  color: var(--beige);
  background: rgba(var(--beige-rgb),.06);
  border-left-color: var(--beige);
  border-bottom-color: transparent;
}
/* Subtle dot on the right edge of the active item — small visual
   anchor that mirrors the left accent stripe. */
.tab-strip-btn.is-active::after {
  content: "";
  position: absolute;
  right: 16px;
  top: 50%;
  transform: translateY(-50%);
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--beige);
  opacity: .8;
}

/* ── SIDEBAR FOOTER — admin tools that aren't tabs ───────── */
.sidebar-footer {
  margin-top: auto;
  padding: 8px 0 14px;
  border-top: 1px solid rgba(var(--beige-rgb),.06);
}
.sidebar-footer-btn {
  width: 100%;
  background: none;
  border: none;
  padding: 10px 24px 10px 25px;
  text-align: left;
  font-family: var(--font);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.3px;
  text-transform: uppercase;
  color: rgba(var(--beige-rgb),.42);
  cursor: pointer;
  border-left: 3px solid transparent;
  transition: color .14s, background .14s;
  display: flex;
  align-items: center;
  gap: 12px;
}
.sidebar-footer-btn .tab-icon { opacity: .7; transition: opacity .14s; }
.sidebar-footer-btn:hover {
  color: var(--beige);
  background: rgba(var(--beige-rgb),.04);
}
.sidebar-footer-btn:hover .tab-icon { opacity: 1; }

/* ── STATUS BADGE (shared by estimate list + future uses) ── */
.status-badge {
  display: inline-block;
  padding: 2px 8px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  border-radius: 10px;
  line-height: 1.55;
}
.status-badge.status-draft    { background: #eae6de; color: rgba(var(--navy-rgb),.7); }
.status-badge.status-sent     { background: rgba(201,122,0,.14); color: #c97a00; }
.status-badge.status-approved { background: #d4edda; color: #1e6b3c; }
.status-badge.status-archived { background: #ede7d6; color: rgba(var(--navy-rgb),.5); }

/* ── MAIN SHELL ──────────────────────────────────────────── */
.main-content { padding: 28px 28px 48px; flex: 1; }

/* hide attribute */
[hidden] { display: none !important; }

/* small print helpers */
.muted { color: rgba(var(--navy-rgb),.55); }
.tiny  { font-size: 10px; letter-spacing: .2px; }

/* ── MOBILE — let the page fit narrower viewports ───────── */
@media (max-width: 1024px) {
  body { min-width: 0; }
}

/* ── Phase A: hover + focus consistency ──────────────────────
   Single hover pattern for clickable rows, list items, and
   pills: faint cream tint + slight border darkening. Three
   different hover behaviors → one. */
:root {
  --hover-tint:    #faf8f2;
  --hover-border:  rgba(var(--navy-rgb),.18);
  --focus-ring:    var(--beige);
  --focus-ring-on-light: var(--navy);
}

/* Keyboard focus ring — visible only when focused via keyboard.
   The :focus-visible pseudo-class drops the ring on mouse clicks
   (which would be annoying noise) and shows it on Tab navigation. */
button:focus-visible,
a:focus-visible,
[role="button"]:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--focus-ring-on-light);
  outline-offset: 2px;
  border-radius: 3px;
}
/* Sidebar lives on a dark surface — flip the ring color so it
   reads against navy instead of disappearing. */
.app-sidebar button:focus-visible,
.page-header button:focus-visible,
.setup-head button:focus-visible {
  outline-color: var(--focus-ring);
}

/* Unify hover state for the linked-pill buttons so their hover
   matches the row pattern (cream tint, no harsh recolor). */
.prj-link-pill.is-clickable:hover {
  background: var(--hover-tint);
  color: var(--ink);
  box-shadow: 0 0 0 1px var(--hover-border) inset;
}

/* ══════════════════════════════════════════════════════════════
   Motion — subtle, AP-appropriate transitions

   Pillars:
     - View swaps fade rather than snap.
     - Cards lift on hover (1-2px translate, softer shadow).
     - Status changes briefly pulse the affected element.
     - Newly-rendered list rows fade in instead of appearing.
     - Numeric counters tick toward new values.

   Honors prefers-reduced-motion so this never overrides the OS-
   level "tone it down" preference. Everything below is wrapped.
   ══════════════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: no-preference) {

  /* ── View swap fade ──────────────────────────────────────────
     When showView() toggles a section's hidden attribute, the new
     section pops in. A subtle 140ms fade gives the swap intent
     without slowing the UI down. Applied to the major content
     containers only — drawers/modals have their own treatment. */
  [id^="view-"] { animation: ap-view-in .14s ease-out; }
  @keyframes ap-view-in {
    from { opacity: 0; transform: translateY(2px); }
    to   { opacity: 1; transform: translateY(0);   }
  }

  /* ── Card hover lift ─────────────────────────────────────────
     The dashboard stat cards, project rows, estimate cards, etc.
     all share the same "you can click me" affordance. Standardize
     it to a 1-2px upward translate with a softer shadow at 150ms.
     Pure CSS — no JS handlers. */
  .ov-stat-card.is-clickable,
  .est-card,
  .job-card,
  .cb-project-card,
  .prj-sheet-link.is-linked,
  .prj-row.is-clickable,
  .inv-row.is-clickable,
  .cli-row.is-clickable {
    transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
  }
  .ov-stat-card.is-clickable:hover,
  .est-card:hover,
  .job-card:hover,
  .cb-project-card:hover,
  .prj-sheet-link.is-linked:hover,
  .prj-row.is-clickable:hover,
  .inv-row.is-clickable:hover,
  .cli-row.is-clickable:hover {
    transform: translateY(-1px);
    box-shadow: 0 4px 14px rgba(var(--navy-rgb),.08), 0 1px 3px rgba(var(--navy-rgb),.05);
  }

  /* ── Status pill pulse ───────────────────────────────────────
     Add `.is-justchanged` (briefly, via JS) to a status pill when
     a project / estimate / invoice moves between states. Pulses
     once over 400ms then settles. JS removes the class via
     setTimeout so the animation doesn't loop. */
  .prj-sheet-status.is-justchanged,
  .status-badge.is-justchanged {
    animation: ap-pill-pulse .4s ease-out 1;
  }
  @keyframes ap-pill-pulse {
    0%   { transform: scale(1);    box-shadow: 0 0 0 0 currentColor; }
    40%  { transform: scale(1.06); box-shadow: 0 0 0 4px rgba(var(--navy-rgb),.10); }
    100% { transform: scale(1);    box-shadow: 0 0 0 0 currentColor; }
  }

  /* ── List row enter ──────────────────────────────────────────
     When a Firebase listener delivers fresh data, the list
     re-renders synchronously. The new <innerHTML> snaps in. A
     200ms fade smooths the swap without delaying interaction. */
  .prj-grid > .prj-row,
  .inv-grid > .inv-row,
  .est-grid > .est-card,
  .cb-projects-grid > .cb-project-card,
  .ov-activity-row {
    animation: ap-row-in .2s ease-out;
  }
  @keyframes ap-row-in {
    from { opacity: 0; transform: translateY(2px); }
    to   { opacity: 1; transform: translateY(0);   }
  }

  /* ── Stat counter tick ───────────────────────────────────────
     For elements with class `.ap-tick`, brief flash + scale so a
     value change reads as "money moved." JS applies the class for
     ~300ms after a value update; CSS handles the rest. */
  .ap-tick { animation: ap-tick .3s ease-out 1; }
  @keyframes ap-tick {
    0%   { transform: scale(1);    color: inherit; }
    40%  { transform: scale(1.04); color: var(--ink); }
    100% { transform: scale(1);    color: inherit; }
  }

  /* ── Form input focus ring ──────────────────────────────────
     Adds a soft navy halo on focus alongside the existing
     border-color change. Reads as "this input is alive" without
     a heavy outline. Scoped to admin form inputs so collaborator
     side keeps its own affordance. */
  #view-project-form .text-input:focus,
  #view-project-form .select-input:focus,
  #view-estimate-form .text-input:focus,
  #view-estimate-form .select-input:focus,
  #view-invoice-form .text-input:focus,
  #view-invoice-form .select-input:focus {
    box-shadow: 0 0 0 3px rgba(var(--navy-rgb),.08);
    border-color: var(--ink);
  }
}

/* ══════════════════════════════════════════════════════════════
   Mobile / narrow-viewport overrides (Phase 1 — foundation)

   Everything below this line only applies at narrow widths
   (<= --bp-narrow, ~720px). Desktop rendering is unchanged.

   Layered approach:
     - Header collapses padding + hides the small "Administrative
       Suite" subtitle so the center title stays readable.
     - Sidebar becomes a slide-in drawer behind the hamburger.
     - Tab strip stays vertical inside the drawer (mirrors desktop
       so the keyboard/tab-order is consistent), with a backdrop
       that dismisses when tapped.
     - Main content padding tightens.
   ══════════════════════════════════════════════════════════════ */

@media (max-width: 720px) {
  /* ── Universal mobile safety net ─────────────────────────
     The next-pass approach: rather than chasing every "this is
     hanging out of the margin" individually, gate the entire
     viewport against horizontal overflow + give media + inputs
     sane max-widths so dynamically-rendered content can't
     accidentally bust the layout. Below this safety net the
     specific per-component overrides take over. */
  html, body { overflow-x: hidden; }
  body { width: 100%; max-width: 100vw; }

  /* Media elements can never exceed their container. SVG icons
     in headers/buttons set their own width/height inline so this
     is a fallback for content-embedded images only. */
  img, video { max-width: 100%; height: auto; }

  /* Inputs/selects/textareas should never exceed their parent.
     Existing rules sometimes set `width: 220px` or similar; the
     max-width cap turns those into "narrower of explicit-or-parent". */
  input:not([type="checkbox"]):not([type="radio"]),
  select,
  textarea { max-width: 100%; }

  /* Long unbroken strings (URLs, IDs, hashes) wrap rather than
     bust their container. `break-word` only breaks INSIDE words
     when the alternative would overflow — does NOT break normal
     short labels into vertical character columns the way
     `anywhere` did inside narrow grid cells (e.g. CONTRACTOR
     HOURS table at 375px collapsed "DESCRIPTION" / "Projecting
     ingest, transfer..." into one-letter-per-line stacks). */
  .setup-body, .lock-body, .ap-modal-card, .main-content {
    overflow-wrap: break-word;
  }
  /* Short uppercase letter-spaced labels (filter labels, column
     headers) must never break. They're tiny and the letter-spacing
     makes the calculated width look bigger than the rendered glyphs;
     forcing nowrap keeps them as a single visual unit. */
  label, th, .field-label, .dash-sort-label,
  .ov-panel-head, .rep-bar-label, .section-title,
  .ov-stat-label, .prj-sheet-section-title, .prj-sheet-stat-label,
  .inv-row-head > *, .prj-row-head > *, .rep-row-head > *,
  .cli-row-head > *, .arch-row-head > * {
    white-space: nowrap;
  }

  /* ── Header tightening ─────────────────────────────────────
     The 3-column grid (logo / center title / actions) is too wide
     for phones once Sign Out + Demo toggle + Theme toggle all land
     on the right. Collapse to 2 columns: hamburger+logo on left,
     all actions on right. The center title is redundant with the
     logo wordmark so it's hidden. */
  .page-header {
    grid-template-columns: auto 1fr;
    padding: 10px 12px;
    gap: 8px;
  }
  .header-center { display: none; }
  .header-left { flex-wrap: nowrap; gap: 8px; }
  .header-right { gap: 6px; }
  .logo svg.logo-full { height: 30px; }
  .header-right .btn-ghost-light { padding: 5px 9px; font-size: 11px; white-space: nowrap; letter-spacing: .4px; }

  .hamburger-btn { display: inline-flex; align-items: center; justify-content: center; }

  /* Sidebar → fixed drawer. The `hidden` attribute still wins
     when JS hides it for unauthenticated views (display: none
     via the global [hidden] rule). When it IS visible (logged in)
     but not `.is-open`, it sits off-screen until the hamburger
     toggles. */
  .app-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: 240px;
    height: 100vh;
    z-index: 60;
    transform: translateX(-100%);
    transition: transform .2s ease;
    box-shadow: 4px 0 18px rgba(0,0,0,.18);
    padding-top: 14px;
  }
  .app-sidebar.is-open { transform: translateX(0); }

  /* Backdrop only renders when the drawer is open. JS injects
     it as #app-sidebar-backdrop next to the sidebar element. */
  .app-sidebar-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.42);
    z-index: 55;
    opacity: 0;
    pointer-events: none;
    transition: opacity .18s ease;
  }
  .app-sidebar-backdrop.is-open {
    opacity: 1;
    pointer-events: auto;
  }

  /* Sidebar inside the drawer is full-width now — drop the
     legacy 220px column behavior.
     Padding tightened (was 14px sides) so cards have more room
     for their data columns; values like "$22,925.00" need every
     pixel of horizontal space they can get on a phone. */
  .app-shell > .main-content {
    padding: 16px 10px 36px;
  }

  /* For pages that use .main-content as the wrapper without an
     .app-shell container, match the inner padding so they don't
     hug the screen edge. */
  .main-content { padding: 16px 10px 36px; }

  /* Footer tightening — the 3-cell flex (left text + center date +
     right "ADMIN") was crowding the center cell, wrapping the date
     to two lines. Hide the center, drop padding, nowrap the sides. */
  .page-footer { padding: 10px 14px; }
  .footer-center { display: none; }
  .footer-text { white-space: nowrap; font-size: 8.5px; }
}

/* Even tighter at phone-only widths. Mostly typography tweaks
   so headings stop word-wrapping awkwardly. */
@media (max-width: 460px) {
  .page-header { padding: 10px 12px; }
  .header-center h1 { font-size: 13px; letter-spacing: .3px; }
  .logo svg.logo-full { height: 28px; }
}
