* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--color-parchment);
  color: var(--color-ink);
  font-family: var(--font-body);
  line-height: 1.5;
  min-height: 100vh;
}
h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 300; margin: 0 0 var(--space-3); color: var(--color-franciscan-green); }
a { color: var(--color-franciscan-green); }
p { margin: 0 0 var(--space-3); }

/* ---- Header / nav ---- */
.site-header {
  background: var(--color-franciscan-green);
  color: #f4f1e8;
  padding: var(--space-3) var(--space-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
  /* 2026-08-16, item 9: the green top bar should never scroll out of
     view on any screen. z-index above .impersonation-bar's own 100 (also
     sticky, top:0) so the two don't fight for the same spot when both are
     visible at once (impersonating/previewing while scrolled) -- a real
     but rare edge case, deliberately left as-is for now per Jay. */
  position: sticky;
  top: 0;
  z-index: 150;
}
.site-header .brand {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  color: #f4f1e8;
  text-decoration: none;
  letter-spacing: 0.02em;
}
.site-header nav { display: flex; align-items: center; gap: var(--space-4); }
.site-header nav a { color: #d8d2c0; text-decoration: none; font-size: 0.92rem; }
.site-header nav a:hover { color: var(--color-gold-light); }

.entity-badge {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-3);
}
.entity-badge select {
  background: transparent;
  color: #f4f1e8;
  border: none;
  font-family: var(--font-body);
  font-size: 0.9rem;
  cursor: pointer;
}
.entity-badge select option { color: var(--color-ink); }
.entity-badge .label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-gold-light); }

/* ---- Notification bell (In-App Notifications, 2026-08-02) -- same
   click-to-reveal popup mechanism as .status-pill-btn/.status-popup (My
   Requests' status-history popup), adapted for the site header rather
   than a table cell. See static/js/notifications.js for the toggle/
   outside-click/mark-read-on-click behavior. ---- */
.notif-bell-wrap { position: relative; display: flex; align-items: center; }
.notif-bell-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  font-size: 1.2rem;
  line-height: 1;
  color: #d8d2c0;
  position: relative;
}
.notif-bell-btn:hover { color: var(--color-gold-light); }
.notif-badge {
  position: absolute;
  top: -7px;
  right: -10px;
  background: var(--color-danger);
  color: #fff;
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1;
  padding: 2px 5px;
  min-width: 16px;
  text-align: center;
  font-family: var(--font-body);
}
.notif-popup {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 300;
  margin-top: var(--space-2);
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card-hover);
  padding: var(--space-3);
  width: 380px;
  max-width: min(90vw, 380px);
  max-height: 480px;
  overflow-y: auto;
  color: var(--color-ink);
  font-family: var(--font-body);
  white-space: normal;
}
.notif-popup-title { font-weight: 700; font-size: 0.9rem; margin-bottom: var(--space-2); color: var(--color-franciscan-green); }
.notif-empty { font-size: 0.85rem; color: var(--color-text-muted); padding: var(--space-2) 0; }
.notif-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-2) 0;
  cursor: pointer;
  font-family: var(--font-body);
}
.notif-item:last-child { border-bottom: none; }
.notif-item.unread .notif-message { font-weight: 700; }
.notif-item .notif-message { font-size: 0.85rem; color: var(--color-ink); }
.notif-item .notif-time { font-size: 0.72rem; color: var(--color-text-muted); margin-top: 2px; }

/* User menu dropdown (2026-08-08 feedback batch) -- reuses .notif-popup's
   positioning/card styling directly, .user-menu-popup only adds the
   width/padding that differ from the notification list. */
.user-menu-btn {
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  color: #d8d2c0; font-size: 0.92rem; font-family: inherit;
}
.user-menu-btn:hover { color: var(--color-gold-light); }
.user-menu-popup { min-width: 200px; padding: var(--space-2) 0; }
/* 2026-08-16, Jay, live test: "almost impossible to see the options" --
   real bug, not a new one: `.site-header nav a` (line ~33, a pale color
   meant for links sitting directly on the dark header bar) has higher
   specificity (1 class + 2 type selectors) than a bare `.user-menu-item`
   (1 class), so every <a>-based item in this white popup was silently
   rendering in that pale header color instead of --color-ink -- pale text
   on a white popup background. Scoping the selector under
   `.user-menu-popup` (2 classes) beats it regardless of source order. */
.user-menu-popup .user-menu-item {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: var(--space-2) var(--space-4); font-size: 0.88rem; color: var(--color-ink);
  text-decoration: none; cursor: pointer; font-family: inherit;
}
.user-menu-popup .user-menu-item:hover { background: #f4f1e8; }
.user-menu-item-form { margin: 0; }

/* ---- Full-screen toggle (2026-08-02) -- fixed-positioned just under the
   green header bar, on EVERY page including /login (Jay: "for all
   screens"), so it can't be gated inside `.site-header`'s own
   `{% if user %}` block. See static/js/fullscreen.js for the toggle/icon-
   swap/fullscreenchange-listener behavior; the button starts `hidden` in
   the markup and JS only reveals it once document.fullscreenEnabled is
   confirmed true, so a context that disallows fullscreen (e.g. an
   embedding iframe) never shows a button that would just error. ---- */
.fullscreen-toggle-btn {
  position: fixed;
  top: 66px;
  right: var(--space-5);
  z-index: 400;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: var(--color-card-bg);
  color: var(--color-franciscan-green);
  box-shadow: var(--shadow-card);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
}
.fullscreen-toggle-btn:hover { box-shadow: var(--shadow-card-hover); color: var(--color-gold); }

/* ---- Impersonation bar -- deliberately NOT gold/green, must read as a
   distinct, unmissable "you are not really you right now" state. ---- */
.impersonation-bar {
  background: var(--color-warning-bg);
  color: var(--color-warning);
  font-weight: 600;
  font-size: 0.9rem;
  padding: var(--space-2) var(--space-5);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  text-align: center;
  position: sticky;
  top: 0;
  z-index: 100;
}
.impersonation-bar form { display: inline; }
.impersonation-bar button {
  background: var(--color-warning);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  padding: 2px 12px;
  font-size: 0.85rem;
  font-weight: 600;
  font-family: var(--font-body);
  cursor: pointer;
}
.impersonation-bar button:hover { opacity: 0.88; }

/* ---- Parish Mode theme (2026-08-08) -- overriding the variable itself,
   not individual rules, retints the header/links/buttons/badges everywhere
   they already reference --color-franciscan-green with zero duplication.
   See tokens.css for why these exact values are a placeholder. ---- */
body.parish-mode {
  --color-franciscan-green: var(--color-parish-red);
  --color-franciscan-green-dark: var(--color-parish-red-dark);
}

/* ---- Cornerstone Mode theme (2026-08-16) -- same override mechanism as
   Parish Mode above, deliberately a distinct color (rich dark blue, not
   red) since it's a different concept: a Cornerstone Franciscan Ministries
   staffer working a served parish's own full AP process, not a parish's
   own portal view. Mutually exclusive with body.parish-mode in practice
   (main.py only ever applies one class), but written as its own selector
   rather than an :not(.parish-mode) guard for clarity. ---- */
body.cornerstone-mode {
  --color-franciscan-green: var(--color-cornerstone-blue);
  --color-franciscan-green-dark: var(--color-cornerstone-blue-dark);
}

/* ---- Dev environment banner (Dev/Prod Split Plan.md, 2026-07-31) --
   BEACON_ENV=dev is allowed to post real Bills/Vendors to the real, live
   production QuickBooks company (there is no QBO sandbox to test against
   safely), so this must be unmistakable app-wide, not a small label --
   Jay's explicit requirement. Rendered on EVERY page (base.html, not gated
   on `user`) as plain normal-flow content -- deliberately NOT `position:
   sticky`/`fixed` like .impersonation-bar, to avoid a z-index/stacking fight
   with that bar when both are visible at once (a real, already-documented
   edge case for .impersonation-bar itself); scrolling away with the rest of
   the page is an acceptable tradeoff for something shown at the top of
   every single page load. The site-header's own red bottom border is the
   second, persistent-while-scrolling reinforcement. ---- */
.dev-env-banner {
  background: var(--color-dev-env);
  color: #fff;
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.01em;
  text-align: center;
  padding: var(--space-2) var(--space-5);
}
body.beacon-env-dev .site-header {
  border-bottom: 4px solid var(--color-dev-env);
}

.page-shell { max-width: 1100px; margin: 0 auto; padding: var(--space-6) var(--space-5); }

/* 2026-08-02 feedback batch, Item 3: "all screens need a back hyperlink
   underneath the name of the screen" -- one consistent style for that link
   across every screen. */
.back-link { margin: 2px 0 var(--space-3); font-size: 0.9rem; }
.back-link a { color: var(--color-muted, #667); text-decoration: none; }
.back-link a:hover { text-decoration: underline; }

/* 2026-08-02 feedback batch, Item 5/7: the standing entity-filter rule for
   any screen showing cross-entity data, plus All Requests' own vendor/
   submitted-by/status filters. One shared row layout. */
.filter-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3);
              margin-bottom: var(--space-4); padding: var(--space-3); background: var(--color-surface, #f7f7f5);
              border-radius: var(--radius-md); }
.filter-bar .field { margin-bottom: 0; min-width: 160px; }
.filter-bar label { display: block; font-size: 0.8rem; font-weight: 600; margin-bottom: 2px; }
.filter-bar select, .filter-bar input[type="text"] { width: 100%; }

/* ---- Buttons ---- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  border: none;
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.08s ease, box-shadow 0.15s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary {
  background: linear-gradient(135deg, var(--color-gold-light), var(--color-gold));
  color: var(--color-franciscan-green-dark);
  box-shadow: 0 2px 6px rgba(201,162,39,0.35);
}
.btn-primary:hover { box-shadow: 0 4px 12px rgba(201,162,39,0.45); }
.btn-secondary {
  background: #fff;
  color: var(--color-franciscan-green);
  border: 1px solid var(--color-border);
}
.btn-secondary:hover { border-color: var(--color-franciscan-green); }
.btn-block { width: 100%; }
/* Compact button for in-table actions (Approve/Reject/Post to QBO/etc.) --
   moved here from new_request.css (2026-07-29): My Approvals, AP Review,
   and Vendor Approvals all use class="btn ... btn-sm" but none of them
   load new_request.css, so this rule was silently never applying on any of
   them -- every action button on those 3 pages had been rendering at full
   default .btn padding (12px/24px) crammed into a table row, which is
   exactly the "buttons look off/way out of whack" bug reported repeatedly
   across all three pages. */
.btn-sm { padding: 4px 12px; font-size: 0.82rem; }

/* ---- Cards / forms (shared) ---- */
.card {
  background: var(--color-card-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-5);
}
.form-card { background: var(--color-card-bg); border-radius: var(--radius-lg); box-shadow: var(--shadow-card); padding: var(--space-5); margin-bottom: var(--space-5); }
.form-card h2 { font-size: 1.1rem; border-bottom: 1px solid var(--color-border); padding-bottom: var(--space-2); margin-bottom: var(--space-4); }
.field { margin-bottom: var(--space-4); }
.field label { display: block; font-size: 0.85rem; font-weight: 600; margin-bottom: var(--space-1); color: var(--color-ink); }
.field .hint { font-size: 0.8rem; color: var(--color-text-muted); margin-top: var(--space-1); }
input[type=text], input[type=email], input[type=number], input[type=date], select, textarea {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: 0.95rem;
  background: #fff;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--color-gold);
  box-shadow: 0 0 0 3px rgba(201,162,39,0.18);
}
.field-readonly {
  padding: var(--space-2) var(--space-3);
  background: #f4f0e4;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  font-size: 0.95rem;
}
.field-readonly a { margin-left: var(--space-2); font-size: 0.85rem; }

/* Generic muted small-text utility -- .entity-picker .sub (portal.css) predates
   this and stays scoped there; this is the reusable version for any other page
   (added 2026-07-25 for vendor_requests.html / vendor_w9_upload.html). */
.sub { color: var(--color-text-muted); font-size: 0.85rem; }

.banner { padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); margin-bottom: var(--space-4); font-size: 0.92rem; }
.banner-error { background: #fbe9e9; color: var(--color-danger); border: 1px solid #f0c2c2; }
.banner-success { background: #e9f5ee; color: var(--color-success); border: 1px solid #b9e0c8; }
.banner-info { background: #eaf1ec; color: var(--color-franciscan-green); border: 1px solid #c9dccf; }

.badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 0.78rem; font-weight: 600; }
.badge-draft { background: #ece7da; color: var(--color-text-muted); }
.badge-submitted { background: #eaf1ec; color: var(--color-franciscan-green); }
/* "Submitted" (the payment_requests.status VALUE) was renamed "UnderReview"
   2026-07-26 (Task 3) -- badge-submitted kept above too, harmlessly unused,
   in case any historical audit_log-derived display ever needs the old
   status word rendered as a badge again. */
.badge-underreview { background: #eaf1ec; color: var(--color-franciscan-green); }
.badge-pending, .badge-pending-approval { background: #fdf0d6; color: #8a5a00; }
.badge-approved, .badge-cfo-override { background: #e0f0e6; color: var(--color-success); }
.badge-rejected { background: #fbe6e6; color: var(--color-danger); }
.badge-posted-to-qbo { background: #dfe9e2; color: var(--color-franciscan-green); }
.badge-cancelled { background: #ece7da; color: var(--color-text-muted); }
/* AP Review Workflow Plan.md (2026-07-26): 'Returned by AP' -- a payment_
   requests.status value, distinct from mid-chain 'Rejected' (Decision 1).
   'skipped' -- a checkreq.approval_actions row status (superseded-by-edit
   or the rest of a chain once one reject fires), used in the chain-history
   popup on /my-approvals. */
.badge-returned-by-ap { background: #fdf0d6; color: #8a5a00; }
.badge-skipped { background: #ece7da; color: var(--color-text-muted); }

/* Budget Overspend Tracking Plan.md (2026-07-26): a GL line exceeded its
   QBO-native annual budget but was allowed through (allow_overspend=true) --
   surfaced on /admin/ap-review per the plan's Section 6 resolution. */
.badge-overspend { background: #fbe6e6; color: var(--color-danger); }
.overspend-detail { color: var(--color-danger); }

table.simple-table { width: 100%; border-collapse: collapse; }
table.simple-table th { text-align: left; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); border-bottom: 2px solid var(--color-border); padding: var(--space-2) var(--space-3); }
table.simple-table td { padding: var(--space-3); border-bottom: 1px solid var(--color-border); font-size: 0.92rem; }

/* ---- My Requests table (2026-07-26 redesign, Task 1) ----
   table-layout:fixed + explicit per-column widths so a normal row fits on
   one line and never wraps to 2 lines (Jay's real screenshot showed this
   happening under the old auto-layout table, mostly on Program Area and
   the full org name -- which is why Entity now shows organizations.code
   ("EDOM"/"Claggett") instead of the full name too). white-space:nowrap on
   the columns that are short by nature (request #, type, entity, amount,
   status, date) so they can never wrap even under a narrow viewport;
   Program Area is deliberately the one column allowed to wrap (long
   program-area titles are the one field genuinely too long to guarantee
   single-line at every viewport width, and it's the least harmful one to
   wrap since it isn't a number/code/date the eye needs on one line). */
table.my-requests-table { table-layout: fixed; }
table.my-requests-table .col-reqnum { width: 10%; }
table.my-requests-table .col-type { width: 4%; }
table.my-requests-table .col-entity { width: 7%; }
table.my-requests-table .col-program { width: 15%; }
/* Vendor column (Task 3, 2026-07-26) -- who the payment is actually to,
   previously missing entirely from this table. */
table.my-requests-table .col-vendor { width: 12%; }
table.my-requests-table .col-submitter { width: 11%; }
table.my-requests-table .col-amount { width: 8%; }
table.my-requests-table .col-status { width: 11%; }
table.my-requests-table .col-date { width: 7%; }
/* Widened 13% -> 17% and right-aligned (2026-07-26, Jay: actions were
   getting silently ellipsis-truncated in the old 13% column -- "if you
   allow [truncation] then you have to allow the user to hover over it and
   see what is not displayed," which nothing here actually supported. Real
   estate borrowed from Type/Entity/Vendor/Submitter/Amount/Date, none of
   which need their full previous width for realistic real content -- Type
   is 2 chars, Entity is a short code, Date is a fixed-format string. */
table.my-requests-table .col-actions { width: 17%; }
table.my-requests-table td, table.my-requests-table th { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The status cell must never clip -- it's the popup's own positioning
   anchor (position:relative), and .status-popup's own overflow (set below)
   is independent of this cell's overflow:hidden regardless, but this avoids
   any ambiguity. */
table.my-requests-table td.status-cell { overflow: visible; }
/* Actions must show completely, never truncate -- a chopped "Edi…" link
   would be non-functional, not just ugly. Right-aligned per Jay's explicit
   preference (2026-07-26): "the options on each row to the right." If a
   future action gets added and this still doesn't fit even with the width
   above, that's the signal to revisit (Jay: "we will remove it later if
   you can't show it completely") -- not to silently let it truncate again. */
table.my-requests-table td.col-actions { overflow: visible; text-overflow: clip; text-align: right; }
/* Program Area is the one column allowed to actually wrap onto a second
   line rather than truncate with an ellipsis (see comment above). Targeted
   by class, not nth-child, since Program Area's column position shifts
   when the CFO "All Requests" view inserts the Submitted By column. */
table.my-requests-table td.program-area-cell { white-space: normal; }

.type-legend { color: var(--color-text-muted); font-size: 0.82rem; margin-bottom: var(--space-3); }

/* ---- Admin review tables (My Approvals / AP Review / Vendor Approvals) ----
   2026-07-28: first attempt at My Approvals (fixed-percentage columns,
   ellipsis truncation) genuinely made things worse -- Jay's own screenshot
   showed truncated HEADER TEXT ("REQU...", "SUBMIT...") and the Chain
   column's "View chain" link text overflowing sideways into the Submitted
   column, because a too-narrow fixed-width cell with overflow:visible (kept
   for the chain popup's own positioning) lets its OWN text bleed into the
   neighbor, not just the popup. Rebuilt 2026-07-29 on a fundamentally
   different, more robust approach: auto table layout (browser sizes columns
   to real content, not a guessed percentage), generous min-widths as a
   floor rather than a hard cap, wrap instead of truncate everywhere, and
   the whole table wrapped in a horizontally-scrolling container (see
   .table-scroll below) as the actual safety net for a narrow viewport --
   instead of fighting to cram 10+ columns into a fixed 1100px page width. */
.page-shell-wide { max-width: 1500px; margin: 0 auto; padding: var(--space-6) var(--space-5); }
.table-scroll { overflow-x: auto; }
table.approvals-table { table-layout: auto; width: 100%; }
table.approvals-table th, table.approvals-table td { white-space: normal; vertical-align: middle; }
table.approvals-table .col-reqnum { min-width: 95px; }
table.approvals-table .col-reqnum a { font-weight: 600; white-space: nowrap; }
table.approvals-table .col-type, table.approvals-table .col-entity { min-width: 55px; white-space: nowrap; }
table.approvals-table .col-program { min-width: 150px; }
table.approvals-table .col-neededby, table.approvals-table .col-date { min-width: 95px; white-space: nowrap; }
table.approvals-table .col-vendor { min-width: 200px; }
table.approvals-table .col-submitter { min-width: 130px; }
table.approvals-table .col-amount { min-width: 85px; white-space: nowrap; }
table.approvals-table .col-chain { min-width: 90px; white-space: nowrap; }
table.approvals-table .col-status { min-width: 130px; }
table.approvals-table .col-actions { min-width: 260px; text-align: right; white-space: nowrap; }
/* The chain-popup/status-pill cell must never clip -- .status-popup itself
   is position:absolute and needs to render outside this cell's own bounds. */
table.approvals-table td.status-cell { overflow: visible; position: relative; }

/* ---- CFO "My Requests" / "All Requests" toggle (Task 6) ---- */
.view-toggle { display: flex; gap: var(--space-2); margin-bottom: var(--space-4); }
.view-toggle-tab {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-text-muted);
  background: #ece7da;
  border: 1px solid var(--color-border);
  border-bottom: none;
}
.view-toggle-tab.active { background: var(--color-card-bg); color: var(--color-franciscan-green); }

/* ---- Status pill popup (Task 4) -- the first real modal-style UI in this
   app, per Jay's explicit instruction ("a real popup... not inline
   expansion"). Positioned relative to .status-cell, dismissed by
   my_requests.js's outside-click listener, never by CSS alone. ---- */
.status-cell { position: relative; }
.status-pill-btn { background: none; border: none; padding: 0; cursor: pointer; font: inherit; }
.status-popup {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 200;
  margin-top: var(--space-1);
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card-hover);
  padding: var(--space-3);
  /* Task 9 (2026-07-26 batch): the original 340px min-width forced every
     column (Date/Action/Status/Comment) to wrap onto multiple lines even
     for a typical short row. Widened substantially and anchored the popup
     to the RIGHT edge of .status-cell (was left:0) -- Status is one of the
     later columns in this table, so a much wider popup growing left
     instead of right stays inside the page rather than overflowing off the
     right edge of the viewport. max-width still caps it on a narrow screen.
     A first attempt left table-layout as auto (letting columns size to
     their own content) -- live-tested and found this actually made things
     WORSE: auto layout's percentage-width hint on the Comment column
     doesn't reliably translate to real space in practice, and a real
     26-character comment ("Group 1: mdickson-patrick") wrapped onto 3
     lines even at 460px wide (confirmed via getClientRects() in a live
     browser check, not assumed). Switched to table-layout:fixed with
     explicit pixel widths per column instead -- fixed layout's widths are a
     hard guarantee, not a hint. */
  min-width: 800px;
  max-width: min(90vw, 800px);
  white-space: normal;
}
.status-popup-title { font-weight: 700; font-size: 0.85rem; margin-bottom: var(--space-2); color: var(--color-franciscan-green); }
table.status-history-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; table-layout: fixed; }
table.status-history-table th { text-align: left; color: var(--color-text-muted); border-bottom: 1px solid var(--color-border); padding: var(--space-1) var(--space-2); }
table.status-history-table td { padding: var(--space-1) var(--space-2); border-bottom: 1px solid var(--color-border); }
/* Date/Action/Status are short, fixed-format strings -- given a generous
   fixed pixel width each (live-measured against real data to comfortably
   fit the longest real values in this app -- "2026-07-25 23:53", "Edited —
   Approval Reset", "Submitted → Submitted" -- all confirmed one line at
   these widths) and never wrap. Comment gets whatever's left (fixed
   layout: an unspecified column absorbs remaining table width -- live-
   measured at ~210px of real usable text width here, enough for a typical
   short comment like "Group 1: mdickson-patrick" to render on one line)
   and is the only column still free to wrap when a comment genuinely is
   long (e.g. the vendor/amount-change audit comment, which will always
   need multiple lines regardless of column width). */
table.status-history-table th:nth-child(1), table.status-history-table td:nth-child(1) { width: 150px; white-space: nowrap; }
table.status-history-table th:nth-child(2), table.status-history-table td:nth-child(2) { width: 195px; white-space: nowrap; }
table.status-history-table th:nth-child(3), table.status-history-table td:nth-child(3) { width: 205px; white-space: nowrap; }
table.status-history-table th:nth-child(4), table.status-history-table td:nth-child(4) { white-space: normal; word-break: break-word; }

/* ---- Action modal (2026-07-29) ----
   Jay: "not a Chrome dialogue box." Replaces window.prompt()/confirm() for
   Approve-with-Note/Reject/Return-to-Submitter -- a real full-screen
   overlay + centered dialog, styled to match the rest of the app. Built
   once here, shared by any page via action_modal.js (approve.confirm()
   works the same as approve.prompt() did, minus the native chrome). */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 28, 22, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}
.modal-box {
  background: var(--color-card-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card-hover);
  padding: var(--space-5);
  width: 100%;
  max-width: 440px;
  margin: var(--space-4);
}
.modal-title { font-weight: 700; font-size: 1.05rem; margin-bottom: var(--space-2); color: var(--color-franciscan-green); }
.modal-hint { font-size: 0.85rem; color: var(--color-text-muted); margin-bottom: var(--space-3); }
.modal-box textarea {
  width: 100%;
  font-family: var(--font-body);
  font-size: 0.92rem;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  resize: vertical;
  box-sizing: border-box;
}
.modal-error { color: var(--color-danger); font-size: 0.82rem; margin-top: var(--space-2); min-height: 1.2em; }
.modal-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-3); }

/* Approve split-button (Approve / Approve with Note options) */
.split-btn { position: relative; display: inline-flex; }
.split-btn .btn-caret {
  padding: 4px 6px;
  border-left: 1px solid rgba(0,0,0,0.15);
}
.split-btn-menu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 50;
  margin-top: 2px;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-card-hover);
  white-space: nowrap;
}
.split-btn-menu button {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: var(--space-2) var(--space-3);
  font-size: 0.82rem;
  font-family: var(--font-body);
  cursor: pointer;
}
.split-btn-menu button:hover { background: #f4f0e4; }

/* ---- Plain link-styled buttons (Cancel, Task 2) -- a <button> inside a
   <form> so the POST can't be triggered by a bare GET link, but styled to
   read exactly like the adjacent PDF/Edit <a> links. ---- */
.cancel-form { display: inline; }
.link-button { background: none; border: none; padding: 0; margin: 0; font: inherit; color: var(--color-franciscan-green); text-decoration: underline; cursor: pointer; }
.cancel-btn { color: var(--color-danger); }

/* ---- Feedback (Task 10) -- Jay's exact required header text, styled to
   read as a warm callout, not a plain paragraph. ---- */
.feedback-card { max-width: 900px; } /* widened 2026-08-02 feedback batch, Item 4 -- was 640px */
.feedback-gift-line {
  font-family: var(--font-display);
  font-size: 1.15rem;
  color: var(--color-franciscan-green);
  background: #eaf1ec;
  border: 1px solid #c9dccf;
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
}
.feedback-card textarea { width: 100%; }

/* ---- Conversational Feedback (2026-08-02) -- replaced the one-shot
   textarea with a real back-and-forth. Simple bubble list, no framework. ---- */
.chat-messages {
  max-height: 420px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-2);
  margin-bottom: var(--space-3);
  background: #faf8f2;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.chat-msg { display: flex; }
.chat-msg-assistant { justify-content: flex-start; }
.chat-msg-user { justify-content: flex-end; }
.chat-bubble {
  max-width: 78%;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  font-size: 0.92rem;
  line-height: 1.45;
  white-space: pre-wrap;
}
.chat-msg-assistant .chat-bubble { background: #eaf1ec; color: var(--color-ink); border: 1px solid #c9dccf; }
.chat-msg-user .chat-bubble { background: var(--color-franciscan-green); color: #f4f1e8; }
.chat-input-row { display: flex; gap: var(--space-2); align-items: flex-end; margin-bottom: var(--space-3); }
.chat-input-row textarea { flex: 1; resize: vertical; }
.chat-actions { display: flex; align-items: center; gap: var(--space-3); }

/* ---- Parish Portal S4+S5 (2026-08-08): announcements feed, document
   listings, parish-request cards. Reuses existing tokens/card shape --
   nothing here overrides the parish-mode red theme, it just inherits it via
   --color-franciscan-green like everything else already does. ---- */
.announcement-item { border-bottom: 1px solid var(--color-border); padding: var(--space-3) 0; }
.announcement-item:last-child { border-bottom: none; }
.announcement-title { font-weight: 700; color: var(--color-franciscan-green); }
.announcement-date { font-size: 0.78rem; color: var(--color-text-muted); margin-bottom: var(--space-1); }
.announcement-body { font-size: 0.92rem; white-space: pre-wrap; }

.doc-area { margin-bottom: var(--space-5); }
.doc-area h2 { font-size: 1.05rem; margin-bottom: var(--space-2); }
/* Collapsible Documents-page sections (2026-08-16, Jay: "allow for
   collapsing the sections so you don't have to scroll so much") -- same
   proven <details>/<summary> pattern as admin_setup.css's .setup-section,
   reused rather than reinvented. Each .doc-area is now a <details>, its
   <h2> moved inside a <summary>. Includes the same author-rule-beats-UA-
   rule fix that pattern's own comment already documents: the browser's UA
   stylesheet hides a closed <details>'s children for free, but ANY author
   rule setting `display` on a child (e.g. .doc-upload-row{display:flex})
   overrides that regardless of specificity -- re-asserting display:none as
   an author rule of our own is what actually makes collapse work. */
.doc-area > summary { display: flex; align-items: center; gap: var(--space-2); cursor: pointer; list-style: none; padding: var(--space-1) 0; }
.doc-area > summary::-webkit-details-marker { display: none; }
.doc-area > summary::before { content: '\25be'; display: inline-block; transition: transform 0.15s ease; }
.doc-area:not([open]) > summary::before { transform: rotate(-90deg); }
.doc-area > summary h2 { margin: 0; }
.doc-area:not([open]) > *:not(summary) { display: none !important; }
table.doc-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
table.doc-table th { text-align: left; text-transform: uppercase; letter-spacing: 0.03em; font-size: 0.72rem; color: var(--color-text-muted); border-bottom: 2px solid var(--color-border); padding: var(--space-2) var(--space-3); }
table.doc-table td { border-bottom: 1px solid var(--color-border); padding: var(--space-2) var(--space-3); vertical-align: middle; }
.doc-empty { color: var(--color-text-muted); font-size: 0.88rem; padding: var(--space-2) 0; }

/* Recursive file-tree browser (2026-08-16, replacing the click-into-a-
   folder/breadcrumb design per Jay's direct correction: "load the entire
   tree... let the user open the tree and select files"). Nested <details>
   give free expand/collapse with no JS -- same reasoning as .doc-area's
   own collapsible-section pattern, just one level deeper and repeatable. */
.doc-tree, .doc-tree ul { list-style: none; margin: 0; padding-left: 0; }
.doc-tree ul { padding-left: var(--space-4); }
.doc-tree li { margin: 1px 0; }
.doc-tree details > summary { cursor: pointer; list-style: none; padding: 3px 0; font-size: 0.88rem; }
.doc-tree details > summary::-webkit-details-marker { display: none; }
.doc-tree details > summary::before { content: '\25b8'; display: inline-block; width: 1em; transition: transform 0.15s ease; }
.doc-tree details[open] > summary::before { transform: rotate(90deg); }
.doc-tree-file { display: flex; align-items: center; gap: var(--space-2); font-size: 0.88rem; padding: 3px 0; }
.doc-tree-name { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.doc-tree-size { color: var(--color-text-muted); font-size: 0.78rem; white-space: nowrap; }
.doc-tree-actions { display: flex; gap: var(--space-2); white-space: nowrap; align-items: center; }
.doc-tree-actions form { display: inline; }

.doc-upload-row { display: flex; gap: var(--space-2); align-items: center; margin-top: var(--space-3); }
.doc-upload-row input[type=file] { flex: 1; }

.badge-request-open { background: #eaf1ec; color: var(--color-franciscan-green); }
.badge-request-reviewed { background: #f5c04a33; color: var(--color-warning); }
.badge-request-closed { background: #ece7da; color: var(--color-text-muted); }

.btn-link { background: none; border: none; padding: 0; margin-left: var(--space-2); color: var(--color-danger); text-decoration: underline; font-size: 0.85rem; cursor: pointer; }
.btn-link:hover { opacity: 0.8; }

/* Moved here from portal.css (2026-08-08) -- these were previously only
   defined in portal.css, which only /portal itself loads. Any other page
   using the same .module-grid/.module-tile pattern (parish_view.html, and
   whatever comes next) rendered them as bare unstyled <a> tags -- Jay:
   "why did you just do options as underscored lines... use buttons like
   you did there." Belongs in base.css (loaded by every page) since it's a
   general-purpose tile-button pattern, not portal-specific. */
.module-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-5);
}
.module-tile {
  background: var(--color-card-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-5);
  text-decoration: none;
  color: var(--color-ink);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  position: relative;
  transition: box-shadow 0.15s ease, transform 0.1s ease;
}
a.module-tile:hover { box-shadow: var(--shadow-card-hover); transform: translateY(-3px); }
.module-tile .icon { width: 40px; height: 40px; color: var(--color-gold); }
.module-tile h3 { margin: 0; font-size: 1.05rem; }
.module-tile .desc { color: var(--color-text-muted); font-size: 0.88rem; margin: 0; }
.module-tile.disabled { opacity: 0.55; cursor: default; }
.module-tile.disabled .icon { color: var(--color-text-muted); }

/* Tile unread-count badges (2026-08-08, Jay: "put a number in the upper
   right hand of the box... bold the number [if new/unseen]"). Count always
   shows (matches every other badge in this app); bold + a slightly
   stronger color is the only "unseen" signal, not a second number. */
.tile-badge {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: 999px;
  background: #ece7da;
  color: var(--color-text-muted);
  font-size: 0.78rem;
  font-weight: 500;
  display: flex;
  align-items: center;
  justify-content: center;
}
.tile-badge-new {
  background: var(--color-danger);
  color: #fff;
  font-weight: 800;
}

/* Diocese-name brand suffix, next to "Beacon" (2026-08-08) -- shown only
   when the entity switcher is suppressed (parish_view set), replacing the
   context it would otherwise show. */
/* Wraps .brand + .brand-diocese so .site-header's own space-between rule
   treats them as ONE item, not two spread across the row (real bug, Jay:
   "it is rendering, just too far to the right" -- .site-header's flex
   children were .brand/.brand-diocese/nav as 3 separate items). */
.brand-group { display: flex; align-items: center; }
.brand-diocese {
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 500;
  color: rgba(255,255,255,0.85);
  margin-left: var(--space-3);
  padding-left: var(--space-3);
  border-left: 1px solid rgba(255,255,255,0.35);
}
