/* ============================================================================
   archer-app.css — production custom sheet for the Archer "Pixel Hard Stamp /
   Pixel Worlds" theme. Load AFTER archer-bootstrap.css (it consumes that
   sheet's --* tokens).

   Folded from the ArcherRethemifizer sandbox, where it was split only to avoid
   a collision during preview:
     1. content surfaces (sandbox archer-app.css)
     2. app-shell chrome  (sandbox preview-shell.css): topbar + rail + sidebar +
        bottom nav + welcome-bar scene + responsive tiers + community switcher.

   Port adjustments:
     - The self-hosted @font-face block was dropped; Inter + VT323 are provided
       by the Google Fonts CDN @import compiled into archer-bootstrap.css.
     - Welcome-bar scene-image refs (../scenes/ PNGs) were removed 2026-07-07:
       the placeholder scenes were never delivered and the art is being reshaped.
       The welcome-bar keeps its --scene-bg canvas until new art lands.
   ============================================================================ */

/* ====================== Content surfaces (sandbox archer-app.css) ========== */

[x-cloak] { display: none !important; }

/* Single-slide announcement media (no swiper carousel) */
.announcement-single .swiper-size-wrapper {
  width: 100%;
  max-width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--bg-3);
  border: 1px solid var(--border-1);
  border-radius: var(--radius-default);
  overflow: hidden;
  position: relative;
}

/* Section head — sits ABOVE a panel (not inside it), so panels in different
   columns line up at the same y-position. Same height in both columns. */
/* Section subheaders carry their OWN consistent top margin (~mt-2 / 8px) so the gap above
   a subheader does not depend on flaky wrapper/flex-gap inheritance. It is additive to any
   surrounding rhythm. Do NOT add a :first-child zero here: section heads are near-universally
   the first child of a per-section wrapper div, so a :first-child rule zeroes essentially all
   of them. Accordion toggle heads override this to margin:0 (a full-width band) in _accordion.scss. */
.archer-section-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0.5rem 0 0.5rem;
  min-height: 32px;
}
.archer-section-head h2,
.archer-section-head h3 {
  font-family: var(--font-pixel);
  font-size: var(--fs-h4);
  color: var(--archer-green-dark);   /* primary-dark #315025 — pixel header text */
  margin: 0;
  line-height: 1;
}
/* Direct form `<h2 class="archer-section-head">` (no wrapper div / icon) — sanctioned by LP-TYPE /
   LP-SECTION. The descendant rule above only matches a CHILD h2/h3, so without this the direct form
   renders ink at full h2 size instead of the green section-head size. Pixel font comes from the
   _baselines element rule; bottom margin stays from the .archer-section-head block above. */
h2.archer-section-head,
h3.archer-section-head {
  font-size: var(--fs-h4);
  color: var(--archer-green-dark);
  line-height: 1;
}
.archer-section-head .archer-section-head-icon { color: var(--archer-green-dark); }

/* ----------------------------------------------------------------------------
   Reputation pin board (home page) — felt-pennant treatment (mirrors the physical
   pin banner). A quiet "display board" holding the earned Great Place to Work badge
   + a "this could be you" placeholder for the un-earned Great Place to Live cert +
   the Google rating as a clickable enamel pin. Consumes tokens; the felt dowel uses
   two literal wood colours (no wood token yet). PRODUCTION: branch to MANAGEMENT
   VIEW only. Built to grow into a community + employee accomplishment board.
   The reputation-variants.html comparison page also exercises .rep / .bg-surface-
   sunken (Option A) and the bare rail (Option C); keep until the page is retired. -- */
.rep { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 1rem; }
.rep-cap { text-align: center; font-size: var(--fs-caption); color: var(--fg-3); margin: 0.5rem 0 0; }

/* Size badges by a shared HEIGHT (not width) so every award reads at the same vertical
   size regardless of its aspect ratio (the tall Great Place to Work banner vs the wide
   U.S. News shields). 76px sits between the old per-award extremes. */
img.rep-badge { height: 76px; width: auto; display: block; flex: 0 0 auto; }

/* Aggregate (Arrow mode) badge tile: the award badge with a community-count
   indicator pinned to its corner, like the topbar notification bell's `.count`.
   The count is tied to its own badge so each award (Great Place to Work, and
   Great Place to Live later) carries its own number. */
.rep-tile { position: relative; flex: 0 0 auto; display: inline-flex; }
.rep-count {
  position: absolute; top: -9px; right: -11px;
  min-width: 32px; height: 28px; padding: 0 7px;
  display: inline-flex; align-items: center; justify-content: center;
  background: #ffffff; color: #000000;
  border: 2px solid #000000; border-radius: 999px;
  font-family: var(--font-pixel); font-size: 0.95rem; font-weight: 700; line-height: 1;
  box-shadow: var(--shadow-panel-sm);
}
.rep-placeholder {
  height: 76px; width: 64px; flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: 0.15rem; padding: 0.3rem 0.2rem;
  border: 1.5px dashed var(--slate-light); border-radius: var(--radius-default);
  background: var(--secondary-light); color: var(--fg-3);
}
.rep-placeholder-title { font-family: var(--font-pixel); font-size: 0.8rem; line-height: 0.88; color: var(--fg-2); }
.rep-placeholder-note { font-size: 0.46rem; letter-spacing: 0.02em; text-transform: uppercase; }

/* Google rating as an enamel pin — round, ink-edged, hard-stamp lift, clickable */
.rep-google {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  width: 60px; height: 60px; flex: 0 0 auto; gap: 1px;
  border: 2px solid var(--border-2); border-radius: var(--radius-pill);
  background: var(--bg-2); color: var(--fg-1); text-decoration: none;
  box-shadow: var(--shadow-panel-sm); line-height: 1;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.rep-google:hover { transform: translate(-1px,-1px); box-shadow: var(--shadow-panel); border-color: var(--bs-primary); }
.rep-google-star { color: var(--gold-deep); font-size: 0.72rem; }
.rep-google-num { font-family: var(--font-pixel); font-size: 1.05rem; font-weight: 700; }
.rep-google-cap { font-size: 0.5rem; letter-spacing: 0.02em; color: var(--fg-3); }

.rep-eyebrow { font-family: var(--font-pixel); font-size: 1.05rem; color: var(--fg-2); letter-spacing: 0.02em; margin-bottom: 0.4rem; }

/* Felt pennant board — the chosen home treatment. (The variants page also composes
   `.rep.bg-surface-sunken.p-3` for its Option A shelf.) */
.rep--felt {
  position: relative; margin-top: 14px;
  background: var(--secondary-dark); color: var(--fg-invert);
  padding: 1.4rem 0.75rem 2rem; border-radius: var(--radius-default);
  clip-path: polygon(0 0, 100% 0, 100% 84%, 50% 100%, 0 84%);
}
.rep--felt .rep-placeholder { background: rgba(255,255,255,0.07); border-color: rgba(255,255,255,0.45); color: var(--fg-invert); }
.rep--felt .rep-placeholder-title { color: var(--fg-invert); }
.rep--felt .rep-cap { color: rgba(255,255,255,0.7); }
.rep--felt::before { /* dowel */
  content: ""; position: absolute; top: -9px; left: 7%; right: 7%; height: 7px;
  border-radius: 4px; background: linear-gradient(#caa46e, #9c7440);
}
.rep--felt::after { /* cord stub */
  content: ""; position: absolute; top: -22px; left: 50%; width: 2px; height: 15px;
  background: rgba(0,0,0,0.3); transform: translateX(-50%);
}

/* Awards settings eligibility / earned toggle: hint the click by lifting the icon and
   shifting it to the primary colour on hover / keyboard focus. */
.award-toggle { cursor: pointer; transition: transform var(--dur-fast) var(--ease-out); }
.award-toggle:hover, .award-toggle:focus-visible { transform: scale(1.18); }
.award-toggle:hover .ci, .award-toggle:focus-visible .ci { color: var(--bs-primary) !important; }
.award-toggle:focus-visible { outline: 2px solid var(--bs-primary); outline-offset: 2px; border-radius: var(--radius-default); }

/* ---------------------------------------------------------------- pre-login layout */
body.prelogin {
  background: var(--greige-subtle);
  min-height: 100vh;
  margin: 0;
}
.prelogin-shell {
  display: flex;
  min-height: 100vh;
}
.prelogin-card-col {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3rem 1.5rem;
}
@media (min-width: 992px) {
  .prelogin-card-col {
    flex: 1 1 auto;
    padding: 3rem 3rem;
  }
}
/* The brand collage is portrait (650x1200). To keep it flush (no green beside it) AND whole (not
   cropped), the column sizes to the image: a full-height portrait strip (the original vh-100
   handling, width:auto), with the card column taking the rest. */
.prelogin-brand-col {
  flex: 0 0 auto;
  max-height: 100vh;
  background: var(--bs-primary);
  overflow: hidden;
}
.prelogin-brand-image {
  width: auto;
  height: 100vh;
  max-height: 100vh;
  display: block;
}
.prelogin-card {
  width: 100%;
  max-width: 480px;
  padding: 2.5rem 2.5rem 2rem;
  background: var(--bg-2);
  border: 2px solid var(--ink);
  border-radius: var(--radius-default);
  box-shadow: var(--shadow-card-stamp);
}

/* Before-login headings are a semantic <h1> on every page; the visual size is set here (not via a
   mismatched .hN size class), and the colour is pinned to the shared --archer-green-dark token that
   all themed headers use (so the success-alert heading can't drift to the alert's own green). */
.prelogin-card h1,
.prelogin-error h1 { color: var(--archer-green-dark); }
.prelogin-card h1 { font-size: var(--fs-h4); }

/* Password field: the show/hide eye sits INSIDE a single input (absolutely positioned), so the field
   is a plain .form-control identical to the other inputs - one rounded hard-stamp shadow, no input-group
   seam and no sharp-cornered/detached group shadow. */
.prelogin-pw { position: relative; }
.prelogin-pw .form-control { padding-right: 3rem; }
.prelogin-pw-toggle {
  position: absolute;
  top: 2px;
  right: 2px;
  bottom: 2px;
  width: 2.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--fg-2);
  cursor: pointer;
  border-radius: var(--radius-default);
}
.prelogin-pw-toggle:hover { color: var(--ink); }
.prelogin-pw-toggle:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }
.prelogin-pw input::-ms-reveal,
.prelogin-pw input::-ms-clear { display: none; }

/* Faux-bold guard (global): the pixel/display font (VT323) ships a single weight, so any fw-* applied
   to it gets browser-synthesised ("faux bold"), which smears the pixels and inflates perceived weight.
   Disable synthesis app-wide so it renders at its true weight; fonts with real weights (Inter) are
   unaffected because synthesis only kicks in when the requested weight is missing. */
body { font-synthesis: none; }

/* Alerts (global): the leading icon is borderless and a little larger. The compiled theme already
   top-aligns alerts that carry an .alert-heading and centers single-line ones, so a multi-line/bulleted
   alert gets the icon on the header row simply by marking its heading .alert-heading. No icon-tile box. */
.alert > .ci { font-size: 1.5rem; flex-shrink: 0; }
.alert .icon-tile { border: 0; background: transparent; width: auto; height: auto; }
/* Alert text, headings, and icon share the one contextual dark colour (--bs-alert-color). Body text
   and the icon already inherit it; headings were picking up the global heading colours, so pin them. */
.alert :is(.alert-heading, h1, h2, h3, h4, h5, h6) { color: var(--bs-alert-color); }

/* Forced-colors / Windows High Contrast: the hard-stamp focus is a box-shadow,
   which WHCM drops. Restore a visible outline on the before-login inputs. */
@media (forced-colors: active) {
  body.prelogin .form-control:focus {
    outline: 3px solid Highlight;
    outline-offset: 2px;
  }
}

/* Below the splash breakpoint (brand column hidden), the auth canvas becomes the
   primary-DARK green so it matches the PWA/theme-color chrome (#315025) and reads
   as one seamless surface on phones. */
@media (max-width: 991.98px) {
  body.prelogin-auth { background: var(--archer-green-dark); }
}

/* ---------------------------------------------------------------- environment indicator (.app-env-chip) */
/* Subtle dev/staging signal: a small ink-bordered chip pinned to a corner. Less pronounced than a
   full banner but still noticeable. Color comes from --env-color (set inline from ENVIRONMENT_COLOR). */
.app-env-chip {
  position: fixed;
  display: inline-block;
  bottom: 20px;
  right: 20px;
  padding: 8px 16px;
  background: var(--env-color, var(--bs-primary));
  border: 2px solid var(--ink);
  border-radius: var(--radius-default);
  box-shadow: 2px 2px 0 var(--ink);
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  z-index: 1040;
  pointer-events: none;
}
.app-env-chip-staging { color: var(--ink); }
.app-env-chip-dev { color: var(--fg-invert); }
/* Before-login has no bottom nav, so pin to the bottom-left corner. */
body.prelogin .app-env-chip { left: 20px; right: auto; }

/* ---------------------------------------------------------------- before-login error pages */
.prelogin-error-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2.5rem 1.5rem;
}
.prelogin-error {
  width: 100%;
  max-width: 32rem;
  text-align: center;
}
.prelogin-error h1 {
  font-size: 2.75rem;
  line-height: 1.05;
  margin-bottom: .5rem;
}
.prelogin-error p {
  font-size: 1.1rem;
}
.prelogin-error-character {
  width: 9rem;
  max-width: 60%;
  height: auto;
  image-rendering: pixelated;
  margin-bottom: 1rem;
}

/* ---------------------------------------------------------------- PWA splash */
/* Brand green to blend edge-to-edge with the green splash images, matching the
   native iOS launch image so the handoff between the two is seamless. */
.splash-screen {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #4a773b;
  justify-content: center;
  align-items: center;
  z-index: 9999;
  opacity: 1;
  transition: opacity 0.5s ease-out;
}
.splash-screen.show { display: flex; }
.splash-screen.fade-out { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .splash-screen { transition: none; }
}
@media all and (display-mode: standalone) {
  .splash-screen { display: flex; }
  @media screen and (max-width: 600px) {
    .splash-screen img { width: 80%; margin: 0 auto; }
  }
}

/* ---------------------------------------------------------------- legacy: archer-grid-wrap (migrated from app.css) */

.ra-add-select { display: none; }

/* ---------------------------------------------------------------- legacy: utility classes (migrated from app.css) */
.border-none { border: none; }
.text-medium { font-size: 0.78em; }
.img-cover { object-fit: cover; object-position: center; }
.vt323-regular { font-family: "VT323", monospace; font-weight: 400; font-style: normal; }

/* ---------------------------------------------------------------- legacy: animations (migrated from app.css) */
@keyframes approveTransition {
  0%   { color: var(--warning);  transform: scale(1); }
  40%  { color: var(--success);  transform: scale(1.4); }
  100% { color: var(--success);  transform: scale(1); }
}
.animate-approve {
  animation: approveTransition 0.5s ease-out forwards;
  display: inline-block;
}

/* Re-establishes container constraints lost when style.css was removed.
   Swiper handles the announcement gallery (image + embedded video); mux
   handles uploaded video; plyr is the YouTube provider. All consume tokens. */

.swiper {
  width: 100%;
  max-width: 100%;
  overflow: hidden;
}
.swiper-wrapper {
  display: flex;
  align-items: stretch;
}
.swiper-slide {
  flex: 0 0 100%;
  width: 100%;
  height: auto;
  display: flex;
  align-items: stretch;
  justify-content: center;
}

/* Aspect-ratio container for slide content (image / iframe / mux-player). */
.swiper-size-wrapper {
  width: 100%;
  max-width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--bg-3);
  border: 1px solid var(--border-1);
  border-radius: var(--radius-default);
  overflow: hidden;
  position: relative;
}
.swiper-size-wrapper > img,
.swiper-size-wrapper > iframe,
.swiper-size-wrapper > .image-wrapper,
.swiper-size-wrapper > .video-wrapper {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.swiper-size-wrapper iframe {
  border: 0;
}

/* Plyr theme overrides — drive Plyr's CSS variables from our tokens so
   the player chrome reads as intentional and brand-consistent. */
.plyr {
  --plyr-color-main: var(--bs-primary);
  --plyr-control-radius: var(--radius-default);
  --plyr-control-icon-size: 16px;
  --plyr-video-control-color: var(--fg-invert);
  --plyr-video-control-color-hover: var(--fg-invert);
  --plyr-video-control-background-hover: var(--bs-primary);
  --plyr-menu-background: var(--bg-2);
  --plyr-menu-color: var(--fg-1);
  --plyr-menu-radius: var(--radius-default);
  --plyr-menu-arrow-color: var(--bs-primary);
  --plyr-tooltip-background: var(--ink);
  --plyr-tooltip-color: var(--fg-invert);
  --plyr-tooltip-radius: var(--radius-default);
  --plyr-range-fill-background: var(--bs-primary);
  --plyr-range-thumb-background: var(--bs-primary);
  --plyr-range-thumb-active-shadow-width: 3px;
  --plyr-video-progress-buffered-background: rgba(255, 255, 255, 0.25);
  --plyr-video-controls-background: linear-gradient(
    rgba(43, 39, 32, 0) 0%,
    rgba(43, 39, 32, 0.65) 100%
  );
  --plyr-font-family: var(--font-mono);
  font-family: var(--font-mono);
}
.plyr__control--overlaid {
  background: var(--bs-primary) !important;
  border: 2px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  border-radius: var(--radius-default);
  transition: background 160ms, box-shadow 160ms;
}
.plyr__control--overlaid:hover {
  background: var(--archer-green-dark) !important;
}

/* Hide swiper nav + pagination when there's only a single slide. Swiper.js
   adds .swiper-button-disabled to both nav buttons and only renders one
   pagination bullet when there's a single slide; we belt-and-suspenders. */
.swiper:not(.announcement-swiper-multi) .swiper-button-prev,
.swiper:not(.announcement-swiper-multi) .swiper-button-next,
.swiper:not(.announcement-swiper-multi) .swiper-pagination {
  display: none;
}
.swiper-button-prev.swiper-button-lock,
.swiper-button-next.swiper-button-lock,
.swiper-pagination-lock {
  display: none !important;
}

/* Swiper navigation arrows — themed buttons with hard-offset shadow. */
.swiper-button-prev,
.swiper-button-next {
  width: 36px;
  height: 36px;
  background: var(--bs-primary);
  color: var(--fg-invert);
  border: 2px solid var(--ink);
  border-radius: var(--radius-default);
  box-shadow: 3px 3px 0 var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 10;
  cursor: pointer;
  transition: transform 120ms, box-shadow 120ms;
}
.swiper-button-prev { left: 12px; }
.swiper-button-next { right: 12px; }
.swiper-button-prev:hover,
.swiper-button-next:hover {
  transform: translate(-1px, calc(-50% - 1px));
  box-shadow: 4px 4px 0 var(--ink);
  color: var(--fg-invert);
}
.swiper-button-prev::after,
.swiper-button-next::after {
  font-family: 'swiper-icons';
  font-size: 14px;
  font-weight: 700;
}

/* Swiper pagination dots. */
.swiper-pagination {
  text-align: center;
  margin: 0.5rem 0;
}
.swiper-pagination-bullet {
  width: 8px;
  height: 8px;
  display: inline-block;
  background: var(--border-1);
  opacity: 1;
  border-radius: 999px;
  margin: 0 4px;
  cursor: pointer;
  transition: background 120ms;
}
.swiper-pagination-bullet-active,
.swiper-pagination-bullet.active {
  background: var(--bs-primary);
}

/* Mux Player web component — width/aspect-ratio + theme accent. */
mux-player {
  display: block;
  width: 100%;
  max-width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-default);
  overflow: hidden;
  --media-primary-color: var(--bs-primary);
  --media-secondary-color: var(--ink);
  --media-accent-color: var(--bs-primary);
}

/* Mux Uploader web component. */
mux-uploader {
  display: block;
  width: 100%;
  max-width: 100%;
  --uploader-primary-color: var(--bs-primary);
}

/* Page pagination (locked Variant C: hard-offset square buttons). Used by
   the announcement carousel and any paged list. Class names kept as
   .pagination-circles / .circle-btn for backward template compatibility,
   but the visual treatment is square hard-offset. */
.pagination-circles {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
}
.circle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-default);
  background: var(--bg-2);
  color: var(--fg-1);
  border: 2px solid var(--ink);
  text-decoration: none;
  font-family: var(--font-mono);
  font-size: 13px;
  cursor: pointer;
  transition: transform 120ms, box-shadow 120ms;
}
.circle-btn.small { width: 30px; height: 30px; font-size: 12px; }
.circle-btn:hover {
  transform: translate(-1px, -1px);
  box-shadow: 3px 3px 0 var(--ink);
  background: var(--bg-2);
}
.circle-btn.active {
  background: var(--bs-primary);
  color: var(--fg-invert);
}
.circle-btn.active:hover {
  transform: none;
  box-shadow: none;
  background: var(--bs-primary);
}
.circle-btn.disabled,
.circle-btn[aria-disabled="true"] {
  opacity: 0.4;
  pointer-events: none;
}
.circle-btn.extra {
  border: none;
  background: transparent;
  cursor: default;
}
.circle-btn.extra:hover {
  transform: none;
  box-shadow: none;
}

/* ---------------------------------------------------------------- legacy: container-wide (migrated from style.css) */
.container-wide {
  width: 100%;
  margin-right: auto;
  margin-left: auto;
  padding-right: 0.75rem;
  padding-left: 0.75rem;
}
@media (min-width: 1400px) {
  .container-wide { max-width: 1600px; }
}


/* ====================== App-shell chrome (sandbox preview-shell.css) ======= */
/* ============================================================================
   archer-redesign · preview-shell.css
   Standalone designer preview of Pixel Hard Stamp · Pixel Worlds.

   Loaded after archer-bootstrap.css + bootstrap-icons.css. Adds:
     - Self-hosted webfonts (loaded from ../fonts/*.woff2)
     - The locked app shell (topbar + rail + sidebar + main) with a
       seamless continuous L (no visible corner between topbar and rail)
     - Pixel Hard Stamp main canvas chrome: full-bleed welcome scene,
       1.5px ink-bordered + 3px-offset-shadow cards, VT323 titles
     - The rich community switcher styling
     - Inter caps for rail labels and sidebar section heads (working
       placeholder; the nav-font decision page is where this gets settled)

   Continuity rule (locked): the topbar and rail must read as a single L.
   Implementation: rail is given position:relative + z-index:2 so its
   solid green surface paints OVER any topbar box-shadow that bleeds
   downward. The corner has no border, no tonal shift, no inner shadow.
   ============================================================================ */

/* ---------- Self-hosted webfonts dropped on port: Inter + VT323 come from the
     CDN @import compiled into archer-bootstrap.css; the other faces were unused. */
/* ---------- Reset + locked tokens ----------
   Brand-aligned where the difference doesn't earn its keep. See
   /_Arrow Branding.pdf and the brand-cohesion review for rationale.
   --------------------------------------------------------------------- */
:root {
  --ar-chrome:        #315025;   /* archer-green-dark (= primary-dark) — the
                                    deeper green step under the #4a773b primary,
                                    carrying the swing-04 contrast against the
                                    parchment canvas. LOCKED — not decision-driven. */
  --ar-chrome-fg:     #ffffff;
  /* --ar-canvas, --ar-content-bg, --ar-sidebar-bg, --ar-ink-soft,
     --ar-surface-1, --ar-surface-2, --ar-input-bg, --ar-input-border are
     all decision-driven via scss/preview/_neutral-palette-variants.scss
     (one unified surface family). Declaring them here would re-set them
     with equal specificity at a later load order and silently override
     the decision cascade. Defaults live in that variant SCSS at :root
     for the un-stamped case. */
  --ar-ink:           #1a1d24;   /* brand warm brown-ink — pairs with the
                                    parchment cream + green palette without
                                    the cool clash of a generic blue-black.
                                    LOCKED — not driven by any decision. */
  --ar-text:          #1a1d24;   /* same warm ink as --ar-ink */
  --ar-text-muted:    #5e677a;   /* warm-neutral muted — matches the warm ink */
  --ar-divider:       #e1e5ec;
  --ar-primary:       #4a773b;   /* archer-green — the $primary (rgb 74,119,59);
                                    matches prod --archer-green. Pixel header text
                                    uses the darker --ar-chrome / archer-green-dark. */
  --ar-primary-soft:  rgba(74, 119, 59, 0.10);

  /* IMPORTANT: decision-driven font vars (--ar-font-body, --ar-font-display,
     --scene-font, --heading-font, --canvas-emphasis-font, --eyebrow-font,
     --nav-rail-label-font, --nav-sidebar-section-font) intentionally LIVE IN
     preview.css under [data-*=...] blocks. Declaring them here would re-set
     them with equal specificity at a later load order and silently override
     the decision cascade. Defaults live as second args in each consumer's
     var(--name, fallback). */
}
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
/* Root canvas = green chrome. With no background on <html>, the body's light
   --ar-canvas propagates to the page canvas, so the safe-area inset behind the
   Android gesture bar (and any overscroll) rendered as a light/white strip in the
   standalone PWA. Giving <html> the chrome green stops that propagation: the
   gesture-bar inset and overscroll now read as the app's green. The content area
   stays light because .app-main paints its own --ar-canvas over this. */
html { background: var(--ar-chrome); }
body {
  font-family: var(--ar-font-body);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ar-text);
  background: var(--ar-canvas);
  min-height: 100vh;
}
/* Installed PWA (standalone): Chrome colours the Android system navigation bar
   from the <body> background, so a light body left that bar white even after the
   html fix above (which only greens the canvas the browser tab shows in the
   gesture area). Paint the body the chrome green in standalone so the WebAPK nav
   bar matches. The visible content stays light because .app-main paints its own
   --ar-canvas over the middle; only the nav bar / insets read green. */
@media (display-mode: standalone) {
  body { background: var(--ar-chrome); }
}
a { text-decoration: none; }
/* Standard links are primary (the theme's --bs-link-color green) with a semibold hover affordance.
   Buttons keep their own treatment (excluded). Component links (.nav-link, .dropdown-item, etc.) set
   their own colour, so they are unaffected by dropping the old `color: inherit` reset. */
a:not(.btn):hover { font-weight: 600; }
button { font: inherit; cursor: pointer; }

/* ---------- App shell grid ---------- */
.app-shell {
  display: grid;
  /* --sat = the iOS standalone-PWA top safe-area inset (Dynamic Island / notch). Off-device it
     resolves to 0, so the whole L reduces to the 56px topbar; on a notched phone the topbar row +
     the sticky rail/sidebar tops grow by the inset so the green chrome clears the island. [Q1] */
  --sat: env(safe-area-inset-top, 0px);
  grid-template-columns: 72px 280px 1fr;
  grid-template-rows: calc(56px + var(--sat)) 1fr;
  grid-template-areas:
    "topbar topbar topbar"
    "rail   sidebar main";
  min-height: 100vh;
  background: var(--ar-chrome);
}

/* Employee shell has NO sidebar (base.html renders only topbar + rail + bottom for
   role_mode == 'employee'). At lg+ the base grid above reserves an empty 280px sidebar
   column, so the green chrome bleeds beside the rail and the rail reads ~352px wide. Drop
   the sidebar column so the canvas meets the rail directly, and carry the same 18px inner
   curve the sidebar gives managers (the rail's rounded notch). Tablet (md to lg) already
   collapses to a no-sidebar 64px grid + curve generically; phones hide the rail. */
@media (min-width: 992px) {
  .app-shell--employee {
    grid-template-columns: 72px 1fr;
    grid-template-areas:
      "topbar topbar"
      "rail   main";
  }
  .app-shell--employee .app-main { border-top-left-radius: 18px; }
}

/* ============================================================================
   TOPBAR — locked green; no border at bottom (rail covers shadow at corner)
   ============================================================================ */
.app-nav {
  grid-area: topbar;
  display: flex;
  align-items: center;
  background: var(--ar-chrome);
  color: var(--ar-chrome-fg);
  padding: 0 18px 0 0;
  padding-top: var(--sat, 0px);   /* clear the Dynamic Island / notch in standalone PWA [Q1] */
  position: sticky;
  top: 0;
  z-index: 5;
  /* NO box-shadow on the topbar — any shadow here paints over the rail at the
     inner corner and breaks the L. Depth lives on the sidebar + main canvas
     as inset shadows (--canvas-cast-shadow) which simulate the L casting
     onto them instead. */
  box-shadow: var(--topbar-shadow, none);
}
.app-nav-brand {
  width: 72px;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}
.app-nav-brand-a {
  height: 34px;
  width: auto;
  display: block;
}
.app-nav-utility {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0;
}
.app-util-btn {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: #ffffff;
  border-radius: 4px;
  position: relative;
  line-height: 1;
  transition: background 120ms;
}
.app-util-btn:hover { background: rgba(255, 255, 255, 0.12); }
.app-util-btn i { font-size: 18px; }
.app-util-btn .ci { width: 24px; height: 24px; }   /* Carbon `ci` topbar icons */
.app-util-btn .count {
  position: absolute;
  top: 4px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  background: #C7344C;
  color: #ffffff;
  border: 1.5px solid currentColor;
  border-radius: 999px;
  font-family: var(--ar-font-body);
  font-size: 9px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 4px;
}
/* Nav avatar composes the shared `.avatar` atom (shape/size/border/font — identical to
   every other avatar). Its ONLY opt-out is colour: a FIXED fill via the base `.bg-warning`
   utility in the markup (instead of a varying identity colour, which won't read on the
   green topbar), plus ink text for contrast on the light fill.
   Markup: `.avatar .app-avatar .bg-warning`. */
.app-avatar {
  margin-left: 6px;
  color: var(--ar-ink);
  cursor: pointer;
  padding: 0;
}

/* ============================================================================
   RAIL — locked green; z-index:2 + solid bg covers topbar's shadow at corner
   so the L reads as one piece with no seam.
   ============================================================================ */
.app-rail {
  grid-area: rail;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: var(--ar-chrome);
  color: var(--ar-chrome-fg);
  padding: 12px 0 8px;
  gap: 4px;
  /* The KEY rule for the seamless L: rail paints OVER the topbar's
     downward shadow within the rail's bounds. */
  position: relative;
  z-index: 2;
  /* Sticky so rail stays put when main canvas scrolls. (grows with the safe-area inset) */
  position: sticky;
  top: calc(56px + var(--sat, 0px));
  align-self: start;
  height: calc(100vh - 56px - var(--sat, 0px));
}
.app-rail-item {
  width: 56px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 8px 0 6px;
  border: 0;
  background: transparent;
  color: rgba(255, 255, 255, 0.86);
  border-radius: 6px;
  cursor: pointer;
  transition: background 120ms, color 120ms;
}
.app-rail-item:hover {
  background: rgba(255, 255, 255, 0.10);
  color: #ffffff;
}
.app-rail-item.is-active {
  background: var(--rail-active-bg, rgba(255, 255, 255, 0.18));
  color: #ffffff;
  box-shadow: var(--rail-active-shadow, none);
}
/* `is-current` marks the bucket of the page actually loaded while a DIFFERENT bucket's panel is
   being previewed (the filled `is-active` marker has moved to the peeked bucket). A quiet hollow
   outline, distinct from the filled selected state, so the loaded location is never lost while
   peeking (NN/g "you are here"). The two classes never co-occur (template guards b.key != selected),
   but :not(.is-active) keeps this self-documenting and safe. */
.app-rail-item.is-current:not(.is-active) {
  color: #ffffff;
  box-shadow: inset 0 0 0 1.5px var(--rail-current-outline, rgba(255, 255, 255, 0.42));
}
.app-rail-item i,
.app-rail-item svg {
  font-size: 22px;
  width: 24px;
  height: 24px;
  display: block;
}
.app-rail-item svg path,
.app-rail-item svg rect,
.app-rail-item svg circle,
.app-rail-item svg line { fill: currentColor; stroke: currentColor; }
.app-rail-label {
  font-family: var(--nav-rail-label-font, var(--ar-font-body));
  font-size: var(--nav-rail-size, 10px);
  font-weight: var(--nav-font-weight, 600);
  letter-spacing: var(--nav-rail-tracking, 0.06em);
  text-transform: uppercase;
  line-height: 1;
  text-align: center;
  width: 100%;
}
.app-rail-spacer { flex: 1; }

/* ============================================================================
   SIDEBAR — locked white; rich community switcher + section list
   ============================================================================ */
/* The sidebar is a RESPONSIVE OFFCANVAS (`.app-sidebar.offcanvas-lg.offcanvas-start`
   in markup): a persistent grid column at lg+, an on-demand drawer below lg (phone +
   tablet), toggled from the topbar. One markup block, no duplication, native Bootstrap
   JS — see responsive-mobile-governance.md.
   Surface (bg/colour) applies to BOTH presentations; `!important` on the background beats
   Bootstrap's `.offcanvas-lg { background:transparent !important }` that fires at lg+. */
.app-sidebar {
  background: var(--ar-sidebar-bg) !important;
  color: var(--ar-text);
  --bs-offcanvas-width: 300px;   /* drawer width below lg; ≈ the 280px desktop pane */
}
/* Desktop column (lg+): the sticky white pane of the L. BELOW lg these rules are absent,
   so Bootstrap's `.offcanvas-lg` positioning (fixed drawer) governs instead — that is the
   whole trick. */
@media (min-width: 992px) {
  .app-sidebar {
    grid-area: sidebar;
    padding: 14px 0 24px;
    overflow-y: auto;
    border-top-left-radius: 18px;
    border-right: var(--sidebar-border-right, 0 none);
    /* Inset shadow simulates the L (topbar + rail) casting onto the sidebar.
       Shadow lives INSIDE the sidebar's box so the L never carries a shadow
       itself. Tunable per shadowing decision via --sidebar-cast-shadow. */
    box-shadow: var(--sidebar-cast-shadow, inset 6px 8px 12px -6px rgba(0, 0, 0, 0.20));
    /* Sticky so sidebar stays put when main canvas scrolls. (grows with the safe-area inset) */
    position: sticky;
    top: calc(56px + var(--sat, 0px));
    align-self: start;
    height: calc(100vh - 56px - var(--sat, 0px));
  }
}
/* Below lg the sidebar IS the fixed offcanvas drawer (Bootstrap makes it a flex column);
   let its content scroll, and keep the drawer header — hidden at lg+ — from compressing.
   The section content carries its own horizontal padding, so the drawer needs none. */
@media (max-width: 991.98px) {
  .app-sidebar { overflow-y: auto; }
  .app-sidebar .offcanvas-header { flex: 0 0 auto; }
}
.app-sidebar-section {
  font-family: var(--nav-sidebar-section-font, var(--ar-font-body));
  font-size: var(--nav-section-size, 10px);
  font-weight: var(--nav-section-weight, 700);
  letter-spacing: var(--nav-section-tracking, 0.10em);
  text-transform: uppercase;
  color: var(--ar-text-muted);
  padding: 14px 18px 2px;
  margin: 0;
  border-top: var(--sidebar-section-border-top, 0 none);
  border-bottom: var(--sidebar-section-border-bottom, 0 none);
}
.app-sidebar-section:first-of-type { padding-top: 14px; border-top: 0 none; }
.app-sidebar-list {
  list-style: none;
  padding: 0;
  margin: 0 0 2px;
}
.app-sidebar-item {
  display: flex;
  align-items: center;
  padding: 3px 18px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ar-text);
  text-decoration: none;
  border-top: var(--sidebar-item-border-top, 0 none);
  transition: background 120ms, color 120ms;
}
.app-sidebar-list > li:first-child .app-sidebar-item { border-top: 0 none; }
.app-sidebar-item:hover {
  background: var(--ar-primary-soft);
  color: var(--ar-primary);
}
.app-sidebar-item.is-active {
  background: var(--ar-primary-soft);
  color: var(--ar-primary);
  font-weight: 600;
}
/* Production-illustrative items (href="#") read like the real production
   sidebar but don't fire navigation. Behavior is the only differentiator
   from the real Preview section above — no visual demotion. */
.app-sidebar-item[href="#"] {
  cursor: default;
  pointer-events: none;
}
.app-sidebar-item[href="#"]:hover {
  background: transparent;
  color: var(--ar-text);
}

/* Community switcher placement. The .archer-dropdown--community component is full-width by
   default (it sits in form rows too); inside the sidebar the shell insets it like the
   retired switcher card did — the sidebar has 0 horizontal padding, so the 14px lives here.
   The inset also carries to the fused panel, which is positioned to this relative box. */
.app-sidebar > .archer-dropdown--community {
  margin: 0 14px 14px;
}

/* ============================================================================
   MAIN CANVAS — locked light grey; swing-04 Hard Stamp content chrome
   ============================================================================ */
.app-main {
  grid-area: main;
  background: var(--ar-canvas);
  overflow-y: auto;
  min-height: 100%;
  /* Inset shadow simulates the topbar casting onto the canvas from above.
     Only top inset (the sidebar is between the rail and the canvas, so the
     rail's "cast" is handled by the sidebar's own inset). Tunable per
     shadowing decision via --main-cast-shadow. */
  box-shadow: var(--main-cast-shadow, inset 0 8px 12px -6px rgba(0, 0, 0, 0.18));
}
/* CONTAINER SYSTEM — THREE page tiers, all centred (margin: 0 auto). Lean generous:
   fill common screens, only go full-bleed when content truly needs it.
   • WIDE   = .content-inner (default): capped at --content-max-width (1760px), centred.
              Fills common screens (<=1920 fully; caps only larger). The default for most
              pages: dashboards, lists, personal schedule, detail, card grids.
   • FLUID  = .content-inner--fluid: NO cap, full-bleed — uses the whole screen incl. 4K.
              Reserved for dense 2D data that earns every pixel / would otherwise scroll on
              the largest screens: the dept-schedule matrix, payroll rollups.
   • NARROW = .content-inner--narrow: fixed --content-max-width-narrow (840px), centred.
              For read / fill-down: forms, single-entity record, wizards.
   Reading line-length is governed separately by a global p { max-width: 100ch }
   (scss/_baselines.scss), applied inside any tier. Rule of thumb: most pages WIDE;
   genuinely dense/2D data FLUID; forms NARROW. Never hardcode a width in markup. */
/* This `gap` is the canonical page-section RHYTHM, spent between the DIRECT
   children of .content-inner (page head -> toolbar -> content section). Page
   content must therefore sit DIRECTLY here: a single wrapper around everything
   (an Alpine x-data root, a layout div) is a plain block with no flex gap, so it
   SWALLOWS the rhythm and the sections collapse to ~0px. Such a wrapper must be
   layout-transparent (display:contents) or re-establish the rhythm
   (d-flex flex-column gap-*). See pattern-playbook.md LP-CONTAINER-03/04. */
.content-inner {
  max-width: var(--content-max-width, 1760px);
  margin: 0 auto;
  padding: 12px var(--content-pad-x, 32px) 64px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.content-inner--narrow {
  --content-max-width: var(--content-max-width-narrow, 840px);
}
.content-inner--fluid {
  --content-max-width: none;
  --content-pad-x: 40px;
}

/* The mobile setup-quests nudge (#mobileSetupQuests, pwa_nudge.html) always renders
   its wrapper div when the PushNotifications feature is on (it hosts the Alpine state);
   x-show is on the inner card, not the wrapper. As a 0-height flex child of
   .content-inner the wrapper still consumes a 24px column gap, pushing the first page
   header down on EVERY breakpoint. display:contents drops the empty wrapper from the
   flex flow; when the nudge activates its .card (own mb-3) renders normally. Global,
   not phone-only. #desktopSiteNudge does not need this (its wrapper is the x-show
   target, so it is display:none and adds no gap when inactive). */
#mobileSetupQuests { display: contents; }

/* ---------- Welcome bar: full-bleed pixel scene + right-side text overlay ----
   Scaling rules:
   - Image is positioned via per-realm --scene-position so subjects don't crop
     at the head when the bar narrows. Default is center center.
   - A gradient overlay (.welcome-scene::after) fades to high-opacity bg under
     the text overlay, guaranteeing text readability regardless of image content
     underneath. Light realms fade to white; dark realms (spire, sundial) fade
     to ink so white text stays readable.
   - Mobile (max-width 600px) drops the scene entirely; the welcome bar reads
     as text-only with the chrome signature intact. Production-style precedent
     for any future scene-bearing surface.
   ---------------------------------------------------------------------- */
/* The pixel scenes are generated against a byte-exact sky color. The bar bg,
   the gradient fade target, and the scene's own bg color all reference
   --scene-bg so they stay locked together; if the scene art is regenerated
   against a different sky tone, override --scene-bg per realm and the seam
   stays invisible. Matched to the delivered scene-square art, whose flat sky
   sampled to #DDD5CC (a light warm greige). That's lighter than the toned surface
   family cream #CFCEC6 (greige #CFCCC6 / grey #C6CACF, all L≈79%; see
   _review/greige-swatches.html) — the bar just tracks whatever sky the art ships
   with so the seam stays invisible. Recolor the PNG sky + reset these to pull it
   onto the exact family value if desired. */
.welcome-bar {
  /* Canvas + fade target = the lighter greige surface variant (#f4f0ea) so the
     art dissolves into the page's default background. The art's own right/top
     sky is pale near-white of similar lightness, so the fade masks the small
     hue shift with no PNG edit. */
  --scene-bg: var(--greige-subtle);
  --scene-bg-rgb: 244, 240, 234;
  /* Scale the art by HEIGHT and pin it bottom-left so overflow:hidden crops the
     top sky. Height-based sizing makes the vertical crop CONSTANT at every bar
     width, so the full tops of the buildings are always in frame on desktop (a
     width-based scale would crop tighter as the bar widened and shear the
     rooflines). The rooflines sit ~53% down the art; 185% leaves a small sky
     strip above them (the top fade below softens it). Tune by eye. */
  --scene-size: auto 185%;
  position: relative;
  min-height: 180px;
  border: 1.5px solid var(--ar-ink);
  border-radius: 2px;
  box-shadow: 3px 3px 0 var(--ar-ink);
  background: var(--scene-bg);
  overflow: hidden;
}
.welcome-scene {
  position: absolute;
  inset: 0;
  image-rendering: pixelated;
  background-color: var(--scene-bg);
  background-image: url("../img/welcome-banner.png");
  /* Default `contain` + bottom-left: the scene is fit to the bar's (existing) height
     and shows its full width at that scale, pinned to the left; the bar's flat sky bg
     (--scene-bg, matched byte-for-byte to the art's own sky) extends naturally to the
     right under the welcome text, so there's no seam and nothing clips. Per-scene
     tweaks via --scene-size / --scene-position. */
  background-size: var(--scene-size, contain);
  background-position: var(--scene-position, left bottom);
  background-repeat: no-repeat;
}
.welcome-scene::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Two stacked fades to solid --scene-bg so the art dissolves into the page:
       1. a TOP vertical fade so any sky revealed on a tall resize melts into the
          background instead of showing a hard band (transparent by ~34%, above
          the rooftops, so the buildings stay crisp);
       2. a RIGHT horizontal fade to ~65% (just before the text column at ~67.5%;
          see .welcome-text) so the streetscape dissolves under the greeting and
          the text stays legible.
     The mid-stops avoid banding on the pixelated layer. Tune the stops by eye. */
  background: var(
    --scene-fade,
    linear-gradient(
      180deg,
      rgba(var(--scene-bg-rgb), 1)   0%,
      rgba(var(--scene-bg-rgb), 0)   15%
    ),
    linear-gradient(
      90deg,
      rgba(var(--scene-bg-rgb), 0)   45%,
      rgba(var(--scene-bg-rgb), 0.9) 58%,
      rgba(var(--scene-bg-rgb), 1)   65%
    )
  );
}
/* Per-realm scene art (../scenes/*.png) removed 2026-07-07: the placeholder
   pixel scenes were never delivered and the welcome-bar art is being reshaped
   (it will not be the old square/registry/heartlands/sundial/trail/spire/library
   set). The .welcome-scene layer renders on its --scene-bg canvas until the new
   art lands, at which point its background-image is set here again. */

/* Dark-realm scenes override --scene-bg to ink so white overlay text stays
   readable; the bar bg, scene bg, and gradient fade all follow automatically.
   Color tracks --ar-ink (brand warm brown-ink). */
.is-realm-spire .welcome-bar,
.is-realm-sundial .welcome-bar {
  --scene-bg: #1a1d24;
  --scene-bg-rgb: 26, 29, 36;
}

.welcome-text {
  position: relative;
  z-index: 1;
  margin-left: auto;
  /* Left text edge aligns with the right column's content-start in the row below:
     col-lg-8 (66.667%) + the row's half-gutter (0.75rem) − this block's own 28px
     padding-left. Self-correcting at any width; verify the welcome-name's left edge
     lines up with the "Heads up" section head below. (Overridden to 100% < 600px.) */
  width: calc(33.3333% + 0.75rem - 28px);
  padding: 22px 28px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 180px;
}
.welcome-greeting {
  font-family: var(--ar-font-body);
  font-size: 11px;
  letter-spacing: 0.10em;
  color: var(--ar-text-muted);
  text-transform: uppercase;
  font-weight: 700;
  margin: 0;
}
.welcome-name {
  font-family: var(--scene-font);
  font-size: 32px;
  font-weight: 400;
  color: var(--ar-ink);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1;
  margin: 6px 0;
}
.welcome-sub {
  font-family: var(--ar-font-body);
  font-size: 13px;
  color: var(--ar-text-muted);
  margin: 0;
}
/* Dark realms — invert overlay text to white. Pairs with the dark gradient fade
   defined under --scene-fade above. */
.is-realm-spire .welcome-text,
.is-realm-spire .welcome-name,
.is-realm-sundial .welcome-text,
.is-realm-sundial .welcome-name { color: #ffffff; }
.is-realm-spire .welcome-greeting,
.is-realm-spire .welcome-sub,
.is-realm-sundial .welcome-greeting,
.is-realm-sundial .welcome-sub { color: rgba(255, 255, 255, 0.82); }

/* Mobile: drop the scene image; welcome bar collapses to text-only with chrome
   signature intact. Precedent for any future scene-bearing surface. */
@media (max-width: 600px) {
  .welcome-scene { display: none; }
  .welcome-text {
    width: 100%;
    margin-left: 0;
    padding: 18px 20px;
  }
}

/* ============================================================================
   RESPONSIVE SHELL — three tiers on Bootstrap's md + lg seams
     • Desktop  (≥ lg 992)   — full L: rail + persistent sidebar + main.
     • Tablet   (md–lg 768–991) — navigation-RAIL tier: rail stays, the secondary
       sidebar collapses to an on-demand offcanvas drawer (`.offcanvas-lg`, toggled
       from the topbar). Only 64px of chrome → content keeps near-full width, the
       core move against horizontal scroll on a portrait tablet.
     • Phone    (< md 768)   — bottom nav for primary; the SAME drawer for the
       secondary sidebar (so the community switcher + section nav are reachable on a
       phone, where they used to vanish). Content fluid full-width.
   Primary-nav and content share ONE boundary (md/768); the only tablet-specific rule
   is "sidebar is a drawer below lg, persistent at lg+". See responsive-mobile-governance.md.
   ============================================================================ */

/* TABLET (md–lg): the navigation-rail tier. Rail + main only; the sidebar is the drawer. */
@media (min-width: 768px) and (max-width: 991.98px) {
  .app-shell {
    grid-template-columns: 64px 1fr;
    grid-template-areas:
      "topbar topbar"
      "rail   main";
  }
  .content-inner {
    --content-pad-x: 24px;
  }
  /* No sidebar column at tablet, so the canvas meets the rail directly. Round its inner corner
     the same 18px the sidebar carries at lg+, so the green L keeps its rounded notch (not square). */
  .app-main {
    border-top-left-radius: 18px;
  }
}

/* Environment indicator: de-emphasized ink-bordered hard-stamp corner chip (carried over from
   visual-overhaul-2, replacing the full-width fixed-top banner). --env-color is set inline from
   settings.ENVIRONMENT_COLOR via the body Alpine x-data. Hidden on phones (bottom nav owns it). */
.app-env-chip {
  position: fixed;
  bottom: 18px;
  right: 18px;
  z-index: 1040;
  padding: 6px 14px;
  background: var(--env-color, var(--success));
  border: 1.5px solid var(--border-2);
  border-radius: var(--radius-default, 2px);
  box-shadow: 3px 3px 0 var(--border-2);
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
}
.app-env-chip-dev { color: #fff; }
.app-env-chip-staging { color: var(--ink); }
@media (max-width: 767.98px) { .app-env-chip { display: none; } }
body.prelogin .app-env-chip { left: 18px; right: auto; }

/* ---------------------------------------------------------------- community / archer dropdown
   Replicates the ArcherRethemifizer rules verbatim. My archer-bootstrap predates the shadow/state
   additions and the legacy app.css overrides border/caret/divider/check, so re-assert here (loads last).
   Shadow behavior (per reference): NONE at rest (border only); var(--shadow-panel) on focus and when
   open; the open control KEEPS its shadow so the input's right edge stays shadowed under the panel. */
.archer-dropdown-control {
  border: 2px solid var(--ink) !important;
  box-shadow: none;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.archer-dropdown:not(.is-open) .archer-dropdown-control:hover { border-color: var(--ink-soft) !important; }
.archer-dropdown:not(.is-open) .archer-dropdown-control:focus-within,
.archer-dropdown:not(.is-open) .archer-dropdown-control:focus {
  outline: none !important;
  border-color: var(--ink) !important;
  box-shadow: var(--shadow-panel);
}
.archer-dropdown.is-open .archer-dropdown-control {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  box-shadow: var(--shadow-panel);
  outline: none !important;
}
.archer-dropdown-control:focus-visible { outline: none !important; }
/* Static (non-switchable) community indicator: same surface as the control, but inert. Same idiom as
   the non-navigating .app-sidebar-item[href="#"] rule above. */
.archer-dropdown--community.is-static .archer-dropdown-control {
  cursor: default;
  pointer-events: none;
}
.archer-dropdown--community.is-static .archer-dropdown-control:hover {
  border-color: var(--ink) !important;
  box-shadow: none;
}
.archer-dropdown-panel {
  border: 2px solid var(--ink) !important;
  border-top: 0 !important;
  box-shadow: var(--shadow-panel);
}
/* Caret: restore the themed 1rem mask-icon (legacy app.css collapses it to a width:0 border-triangle).
   It sits flush-right because .community-meta grows; re-assert that flex in case legacy drops it. */
.archer-dropdown--community .community-meta { flex: 1 !important; min-width: 0; }
.archer-dropdown-caret {
  flex: 0 0 auto !important;
  position: static !important;
  width: 1rem !important;
  height: 1rem !important;
  border: 0 !important;
  transform: none !important;
  background-color: var(--ink);
  -webkit-mask-image: var(--ci-caret-down); mask-image: var(--ci-caret-down);
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
}
.archer-dropdown-caret.open { transform: rotate(180deg) !important; }
/* Option dividers: the reference grey (--table-border-cool, #d6dae0); legacy app.css sets a faint
   rgba(0,0,0,.075) that otherwise wins. */
.archer-dropdown-option { border-bottom-color: var(--table-border-cool) !important; }
.archer-dropdown-option:last-child { border-bottom: 0 !important; }
.archer-dropdown-divider { border-top-color: var(--table-border-cool) !important; }
/* Single current-marker: keep the themed ::after check (right), drop the legacy app.css ::before duplicate. */
.archer-dropdown-option.current::before { content: none !important; display: none !important; }

/* ---------------------------------------------------------------- media components (swiper / plyr / mux)
   Picked up from visual-overhaul-2: re-establishes the container constraints + themes the player
   chrome. Uses --bs-primary (the green primary; supersedes the legacy --archer-green token). */
.swiper { width: 100%; max-width: 100%; overflow: hidden; }
.swiper-wrapper { display: flex; align-items: stretch; }
.swiper-slide { flex: 0 0 100%; width: 100%; height: auto; display: flex; align-items: stretch; justify-content: center; }
.swiper-size-wrapper {
  width: 100%; max-width: 100%; aspect-ratio: 16 / 9;
  background: var(--bg-3); border: 1px solid var(--border-1); border-radius: var(--radius-default);
  overflow: hidden; position: relative;
}
.swiper-size-wrapper > img,
.swiper-size-wrapper > iframe,
.swiper-size-wrapper > .image-wrapper,
.swiper-size-wrapper > .video-wrapper,
.swiper-size-wrapper > .archer-player { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.swiper-size-wrapper iframe { border: 0; }
.archer-player.video-wrapper { width: 100%; height: 100%; position: absolute; inset: 0; }
.archer-player.video-wrapper iframe,
.archer-player.video-wrapper video { width: 100% !important; height: 100% !important; position: absolute; inset: 0; border: 0; }
.plyr {
  --plyr-color-main: var(--bs-primary);
  --plyr-control-radius: var(--radius-default);
  --plyr-control-icon-size: 16px;
  --plyr-video-control-color: var(--fg-invert);
  --plyr-video-control-color-hover: var(--fg-invert);
  --plyr-video-control-background-hover: var(--bs-primary);
  --plyr-menu-background: var(--bg-2);
  --plyr-menu-color: var(--fg-1);
  --plyr-menu-radius: var(--radius-default);
  --plyr-menu-arrow-color: var(--bs-primary);
  --plyr-tooltip-background: var(--ink);
  --plyr-tooltip-color: var(--fg-invert);
  --plyr-tooltip-radius: var(--radius-default);
  --plyr-range-fill-background: var(--bs-primary);
  --plyr-range-thumb-background: var(--bs-primary);
  --plyr-range-thumb-active-shadow-width: 3px;
  --plyr-video-progress-buffered-background: rgba(255, 255, 255, 0.25);
  --plyr-video-controls-background: linear-gradient(rgba(43, 39, 32, 0) 0%, rgba(43, 39, 32, 0.65) 100%);
  --plyr-font-family: var(--font-mono);
  font-family: var(--font-mono);
}
/* Square the player container itself (the outer .swiper-size-wrapper supplies the 2px card corner). */
.plyr,
.plyr__video-wrapper,
.plyr__poster { border-radius: 0 !important; }

/* Centered play overlay: the primary hard-stamp (hero) button + the standard button raise on hover
   and press on active (matching every other button). The "click me" ripple pulse is killed below. */
.plyr__control--overlaid {
  background: var(--bs-primary) !important;
  color: #fff !important;
  border: 2px solid var(--ink) !important;
  border-radius: var(--radius-default) !important;
  box-shadow: 3px 3px 0 var(--ink) !important;
  padding: 12px 16px;
  transform: translate(-50%, -50%);
  transition: transform 120ms, box-shadow 120ms, background 120ms;
}
.plyr__control--overlaid:hover {
  background: var(--bs-primary) !important;
  color: #fff !important;
  transform: translate(calc(-50% - 1px), calc(-50% - 1px));
  box-shadow: 4px 4px 0 var(--ink) !important;
}
.plyr__control--overlaid:hover svg { color: #fff !important; fill: #fff !important; }
.plyr__control--overlaid:active {
  transform: translate(calc(-50% + 2px), calc(-50% + 2px));
  box-shadow: 1px 1px 0 var(--ink) !important;
}
/* Kill the legacy "click me" ripple rings (style.css .plyr__control--overlaid::before/::after run
   animation: ripple-1/ripple-2 infinite) - the circular pulse over the square play button. */
.plyr__control--overlaid::before,
.plyr__control--overlaid::after {
  content: none !important;
  animation: none !important;
  display: none !important;
}

/* Bottom toolbar controls: square; the active/hover affordance is a square primary fill. */
.plyr__controls .plyr__control { border-radius: var(--radius-default) !important; }
.plyr__controls .plyr__control:hover,
.plyr__controls .plyr__control.plyr__tab-focus,
.plyr__controls .plyr__control[aria-expanded="true"],
.plyr__controls .plyr__control[aria-pressed="true"] {
  background: var(--bs-primary) !important;
  color: var(--fg-invert) !important;
  border-radius: var(--radius-default) !important;
}

/* Progress + volume: square track / fill / buffer (no rounding, no border). The fill stays green
   (--plyr-range-fill-background = --bs-primary); only the thumb (the slide) goes white. */
.plyr--full-ui input[type="range"],
.plyr--full-ui input[type="range"]::-webkit-slider-runnable-track,
.plyr--full-ui input[type="range"]::-moz-range-track,
.plyr--full-ui input[type="range"]::-moz-range-progress,
.plyr--full-ui input[type="range"]::-ms-track,
.plyr--full-ui input[type="range"]::-ms-fill-lower,
.plyr--full-ui input[type="range"]::-ms-fill-upper,
.plyr__progress__buffer,
.plyr__progress__buffer::-webkit-progress-value,
.plyr__progress__buffer::-moz-progress-bar { border-radius: 0 !important; }

/* Scrub thumb (the slide): square, WHITE like the rest of the controls, ink border. */
.plyr--full-ui input[type="range"]::-webkit-slider-thumb {
  border-radius: 0 !important;
  border: 1.5px solid var(--ink) !important;
  background: #fff !important;
  box-shadow: none !important;
}
.plyr--full-ui input[type="range"]::-moz-range-thumb {
  border-radius: 0 !important;
  border: 1.5px solid var(--ink) !important;
  background: #fff !important;
  box-shadow: none !important;
}
.plyr--full-ui input[type="range"]::-ms-thumb {
  border-radius: 0 !important;
  border: 1.5px solid var(--ink) !important;
  background: #fff !important;
}
.swiper:not(.announcement-swiper-multi) .swiper-button-prev,
.swiper:not(.announcement-swiper-multi) .swiper-button-next,
.swiper:not(.announcement-swiper-multi) .swiper-pagination { display: none; }
.swiper-button-prev.swiper-button-lock,
.swiper-button-next.swiper-button-lock,
.swiper-pagination-lock { display: none !important; }
.swiper-button-prev, .swiper-button-next {
  width: 36px; height: 36px; background: var(--bs-primary); color: var(--fg-invert);
  border: 2px solid var(--ink); border-radius: var(--radius-default); box-shadow: 3px 3px 0 var(--ink);
  display: inline-flex; align-items: center; justify-content: center; position: absolute; top: 50%;
  transform: translateY(-50%); z-index: 10; cursor: pointer; transition: transform 120ms, box-shadow 120ms;
}
.swiper-button-prev { left: 12px; } .swiper-button-next { right: 12px; }
.swiper-button-prev:hover, .swiper-button-next:hover { transform: translate(-1px, calc(-50% - 1px)); box-shadow: 4px 4px 0 var(--ink); color: var(--fg-invert); }
.swiper-button-prev::after, .swiper-button-next::after { font-family: 'swiper-icons'; font-size: 14px; font-weight: 700; }
.swiper-pagination { text-align: center; margin: 0.5rem 0; }
.swiper-pagination-bullet { width: 8px; height: 8px; display: inline-block; background: var(--border-1); opacity: 1; border-radius: 999px; margin: 0 4px; cursor: pointer; transition: background 120ms; }
.swiper-pagination-bullet-active, .swiper-pagination-bullet.active { background: var(--bs-primary); }
mux-player {
  display: block; width: 100%; max-width: 100%; aspect-ratio: 16 / 9; border-radius: var(--radius-default); overflow: hidden;
  --media-primary-color: var(--bs-primary); --media-secondary-color: var(--ink); --media-accent-color: var(--bs-primary);
}

/* PHONE (< md): collapse to a single column, bottom nav for primary. The sidebar is NOT
   hidden — it is the `.offcanvas-lg` drawer (fixed + hidden until summoned by Bootstrap),
   so only the rail is removed here. */
@media (max-width: 767.98px) {
  .app-shell {
    grid-template-columns: 1fr;
    grid-template-areas:
      "topbar"
      "main";
  }
  .app-rail {
    display: none;
  }
  /* Phone/PWA chrome: don't render a persistent root scrollbar; scrolling still
     works (touch-swipe on device, wheel/trackpad on desktop). Mobile only —
     desktop keeps its scrollbar. */
  html { scrollbar-width: none; }
  html::-webkit-scrollbar { display: none; }
  .content-inner,
  .content-inner--fluid,
  .content-inner--narrow {
    --content-max-width: none;
    --content-pad-x: 16px;
    padding-top: 6px;
    /* clear the fixed mobile .app-bottom-nav (+ PWA home-indicator inset) */
    padding-bottom: calc(84px + env(safe-area-inset-bottom));
  }
}

/* ============================================================================
   BOTTOM NAV (mobile, < md) — the rail re-laid out as a fixed bottom button
   bar. It IS the rail: same destinations (minus Home — the brand "A" is Home
   on mobile), same icons/labels, same Alpine `activeRail` state. The only
   primary nav on phones — no hamburger / offcanvas. Horizontally SWIPEABLE
   with the scrollbar NOT rendered (touch-swipe on mobile, wheel/trackpad scroll
   on desktop). PWA-safe via env(safe-area-inset-bottom). Sanctioned divergence
   per responsive-mobile-governance.md (bottom nav vs rail); folds into a Django
   partial (nav_mobile_*.html) at port.
   ============================================================================ */
.app-bottom-nav { display: none; }

/* Brand "A" doubles as Home (needed on mobile; harmless on desktop). */
.app-nav-brand-home { display: inline-flex; align-items: center; line-height: 0; border-radius: 4px; }

/* Chrome focusables sit on the dark green / ink L-chrome, where the slate :focus-visible
   ring (tuned for paper surfaces) drops below the 3:1 indicator floor. Override the ring
   COLOUR to cream so the focus indicator clears the floor on the chrome; the 3px width +
   offset still come from the global :focus-visible rule. [a11y #focus] */
.app-rail-item:focus-visible,
.app-bottom-nav .app-rail-item:focus-visible,
.app-util-btn:focus-visible,
.app-nav-brand-home:focus-visible,
.app-avatar:focus-visible {
  outline-color: var(--cream, #fbf9f1);
}

@media (max-width: 767.98px) {
  .app-bottom-nav {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1030;
    display: flex;
    align-items: stretch;
    gap: 2px;
    padding: 6px 8px;
    padding-bottom: calc(6px + env(safe-area-inset-bottom));
    background: var(--ar-chrome);
    color: var(--ar-chrome-fg);
    border-top: 2px solid var(--ar-ink, #1a1d24);
    /* swipe to move; scrollbar hidden; desktop scroll still works */
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;                 /* Firefox */
    scroll-snap-type: x proximity;
  }
  .app-bottom-nav::-webkit-scrollbar { display: none; }   /* Chromium / WebKit */

  /* reuse .app-rail-item so the buttons ARE rail buttons, just horizontal.
     flex:1 fills the bar evenly when the items fit; they shrink to min-width
     then overflow + swipe when a page has more destinations than fit. */
  .app-bottom-nav .app-rail-item {
    flex: 1 1 auto;
    width: auto;
    min-width: 62px;
    padding: 7px 10px 5px;
    scroll-snap-align: start;
  }
}

/* Narrowest phones (iPhone SE / 320px): 5 labelled items at min-width 62 + padding overflow
   the 320 bar and the labels collide with no gutter. Let the items shrink to share the width
   evenly and pare the label a step so the five targets keep clear separation. */
@media (max-width: 374.98px) {
  .app-bottom-nav .app-rail-item {
    min-width: 0;
    padding-inline: 4px;
  }
  .app-bottom-nav .app-rail-label {
    font-size: 9px;
    letter-spacing: 0;
  }
}

/* ============================================================================
   NAV REFINEMENTS — applied 2026-06-16 from the mobile-nav review (employee + manager).
   Decisions: see _review/mobile-nav-mode-review-report.html.
   ============================================================================ */

/* Q1 — phone: grow the top utility targets to the 44px touch-comfort bar (was 36px, gap:0),
   so the bell + avatar are comfortable and clear of the island once --sat applies. */
@media (max-width: 767.98px) {
  .app-nav-utility { gap: 4px; }
  .app-util-btn,
  .app-avatar { width: 44px; height: 44px; }
}

/* Q3 / obs 5 — the offcanvas drawer is the phone/tablet breadth surface, so its destination rows
   get a real 44px touch target + a hairline divider below lg. The dense 13px rows stay only on the
   lg+ persistent sidebar column (a pointer target). */
@media (max-width: 991.98px) {
  .app-sidebar-item {
    min-height: 44px;
    padding-top: 0;
    padding-bottom: 0;
    border-bottom: 1px solid var(--table-border-cool, #e6e7ea);
  }
  .app-sidebar-list > li:last-child .app-sidebar-item { border-bottom: 0; }
}

/* Q3 / Q4 — the drawer SEARCH field. It is NOT a bespoke control: it is the prevailing app search
   pattern (Bootstrap .input-group + .input-group-text[ci-search] + input.form-control[type=search],
   the same as the 02-list-table control row), inset to the drawer gutter and sized to a comfortable
   mobile target. The .input-group stretches its members, so min-height on the group floors the field
   + addon to 44px. This is the surface the future "Ask Archer" assistant upgrades in place. */
.app-sidebar-search { margin: 12px 14px; }
/* Floor the GROUP and BOTH its members to the same 44px so the icon addon and the field read as one
   control (a comfortable drawer touch target, matching the 44px .app-sidebar-item rows). The theme
   floors every input-group member to 40px at `.input-group:not(-sm):not(-lg) > *` specificity, and a
   group-only min-height leaves 4px of flex slack the field absorbs but the addon does not, so the
   addon renders 4px short. Matching that selector's :not guards here (higher specificity) lifts both
   members together. */
.app-sidebar-search .input-group { min-height: 44px; }
.app-sidebar-search .input-group:not(.input-group-sm):not(.input-group-lg) > .form-control,
.app-sidebar-search .input-group:not(.input-group-sm):not(.input-group-lg) > .input-group-text { min-height: 44px; }
/* MOBILE-ONLY: search lives in the offcanvas drawer (phone + tablet); it is NOT shown on the
   desktop persistent sidebar (lg+), where the rail + sidebar carry navigation. */
@media (min-width: 992px) { .app-sidebar-search { display: none; } }

/* The drawer's full-tree surface (nav_sidebar_full.html, below lg) stacks EVERY bucket, since the
   rail bucket switcher is hidden on phone. The bucket name therefore needs more weight than its
   sub-section headings to read as the group parent, and adjacent buckets get a divider. Scoped to
   .app-nav-bucket, so the lg+ persistent pane (one bucket, rail-driven) is untouched. */
.app-nav-bucket > .app-sidebar-section:first-child {
  font-size: var(--nav-bucket-size, 12px);
  color: var(--ar-text);
  padding-top: 16px;
}
.app-nav-bucket + .app-nav-bucket > .app-sidebar-section:first-child {
  border-top: 1px solid var(--table-border-cool, #e6e7ea);
  margin-top: 4px;
}

/* AVATAR DROPDOWN (account menu) — reads as the canonical dropdown and descends FLUSH from the nav-bar
   bottom. The hero shadow + 2px ink edge come from the global .dropdown-menu override (scss/overrides/_forms.scss).
   Position: the toggle carries data-bs-display="static" (Popper off), and .app-nav .dropdown is made
   position:static so the menu's offset parent is the sticky .app-nav — then it's pinned to the bar bottom. */
.app-nav .dropdown { position: static; }
.app-nav .dropdown-menu {
  position: absolute;
  top: calc(56px + var(--sat, 0px));   /* flush to the nav-bar bottom (grows with the safe-area inset) */
  right: 18px;                          /* aligns to the bar's right gutter / the avatar's right edge */
  left: auto;
  margin: 0;
}
/* Items aligned to the nav sizing: sidepanel font-size on desktop, 44px touch rows on mobile. */
.app-nav .dropdown-menu .dropdown-item { font-size: 13px; }
@media (max-width: 991.98px) {
  .app-nav .dropdown-menu .dropdown-item { min-height: 44px; display: flex; align-items: center; }
}

/* ---------- Section heads (VT323 + pixel cap glyph) ---------- */
.section-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin: 6px 0 -8px;
}
.section-head h2,
.section-head h3 {
  font-family: var(--heading-font);
  font-size: 22px;
  font-weight: 400;
  color: var(--ar-ink);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding-left: 18px;
  position: relative;
  margin: 0;
}
.section-head h2::before,
.section-head h3::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 2px;
  height: 2px;
  margin-top: -7px;
  background: var(--ar-ink);
  box-shadow:
    0 2px 0 var(--ar-ink), 0 4px 0 var(--ar-ink), 0 6px 0 var(--ar-ink),
    0 8px 0 var(--ar-ink), 0 10px 0 var(--ar-ink), 0 12px 0 var(--ar-ink),
    2px 0 0 var(--ar-ink), 2px 12px 0 var(--ar-ink),
    4px 0 0 var(--ar-ink), 4px 12px 0 var(--ar-ink),
    6px 4px 0 var(--ar-ink), 6px 8px 0 var(--ar-ink);
}
.section-meta {
  font-family: var(--canvas-emphasis-font);
  font-size: 14px;
  color: var(--ar-text-muted);
  letter-spacing: 0.04em;
}
.section-meta::before { content: "[ "; color: var(--ar-ink); }
.section-meta::after  { content: " ]"; color: var(--ar-ink); }

/* ---------- Quest grid + cards (Hard Stamp body chrome) ---------- */
.quest-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
.quest-card {
  position: relative;
  background: #ffffff;
  border: 1.5px solid var(--ar-ink);
  border-radius: 2px;
  box-shadow: 3px 3px 0 var(--ar-ink);
  padding: 16px 20px 48px;
}
.quest-card-banner {
  background: var(--ar-ink);
  color: #ffffff;
  font-family: var(--canvas-emphasis-font);
  font-size: 14px;
  letter-spacing: 0.06em;
  padding: 2px 10px;
  text-transform: uppercase;
  font-weight: 400;
  display: inline-block;
  margin-bottom: 10px;
}
.quest-card-title {
  font-family: var(--heading-font);
  font-size: 22px;
  font-weight: 400;
  letter-spacing: 0.01em;
  line-height: 1.1;
  margin: 0 0 8px;
}
.quest-card-body {
  font-size: 14px;
  color: var(--ar-text-muted);
  margin: 0 0 12px;
}
.quest-card-action {
  background: var(--ar-primary);
  border: 1.5px solid var(--ar-ink);
  box-shadow: 2px 2px 0 var(--ar-ink);
  color: #ffffff;
  font-family: var(--canvas-emphasis-font);
  font-size: 16px;
  letter-spacing: 0.04em;
  padding: 6px 14px;
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
}
.quest-card-action:hover {
  transform: translate(1px, 1px);
  box-shadow: 1px 1px 0 var(--ar-ink);
}
.quest-card::after {
  content: "URGENT";
  position: absolute;
  bottom: 10px;
  right: 10px;
  background: var(--ar-ink);
  color: #ffffff;
  font-family: var(--canvas-emphasis-font);
  font-size: 12px;
  letter-spacing: 0.10em;
  padding: 2px 8px;
  text-transform: uppercase;
  line-height: 1;
}
.quest-grid > .quest-card:nth-child(2)::after { content: "DUE FRI"; background: #6a4e0e; }
.quest-grid > .quest-card:nth-child(3)::after { content: "REVIEW"; background: var(--ar-primary); }

/* ---------- Good-to-know cards ---------- */
.gtk-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}
.gtk-card {
  background: #ffffff;
  border: 1.5px solid var(--ar-ink);
  border-radius: 2px;
  box-shadow: 3px 3px 0 var(--ar-ink);
  padding: 16px 20px;
  display: flex;
  gap: 14px;
  align-items: flex-start;
}
.gtk-card-icon {
  background: var(--ar-ink-soft);
  color: var(--ar-ink);
  border: 1.5px solid var(--ar-ink);
  border-radius: 2px;
  padding: 6px;
  display: inline-flex;
}
.gtk-card-icon i { font-size: 16px; }
.gtk-card-title {
  font-family: var(--heading-font);
  font-size: 18px;
  font-weight: 400;
  margin: 0 0 4px;
}
.gtk-card-body {
  font-size: 13px;
  color: var(--ar-text-muted);
  margin: 0;
}

/* ---------- Useful links ---------- */
.useful-links {
  background: #ffffff;
  border: 1.5px solid var(--ar-ink);
  border-radius: 2px;
  box-shadow: 3px 3px 0 var(--ar-ink);
  padding: 4px 0;
}
.useful-link {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 20px;
  border-top: 1px solid var(--ar-ink);
  font-weight: 500;
  color: var(--ar-text);
  text-decoration: none;
}
.useful-link:first-child { border-top: 0; }
.useful-link-arrow i::before {
  content: "▸";
  font-family: var(--ar-font-display);
  font-style: normal;
  font-size: 16px;
  color: var(--ar-ink);
}

/* ---------- Table card ---------- */
.table-card {
  background: #ffffff;
  border: 1.5px solid var(--ar-ink);
  border-radius: 2px;
  box-shadow: 3px 3px 0 var(--ar-ink);
  overflow: hidden;
}
.data-table {
  width: 100%;
  border-collapse: collapse;
}
.data-table thead th {
  background: var(--ar-ink);
  color: #ffffff;
  font-family: var(--canvas-emphasis-font);
  font-size: 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 400;
  padding: 10px 14px;
  text-align: left;
}
.data-table tbody td {
  padding: 10px 14px;
  border-top: 1px solid #d6dae0;
  font-size: 13px;
}
.status-pill {
  font-family: var(--canvas-emphasis-font);
  font-size: 14px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0 8px;
  font-weight: 400;
  border-radius: 0;
  display: inline-block;
}
.status-pill.status-success { background: #e3f2dd; color: #2f5520; }
.status-pill.status-warning { background: #fcefc5; color: #6a4e0e; }
.status-pill.status-info    { background: #dde6ec; color: #2b4359; }
.status-pill.status-danger  { background: #f5d8de; color: #6e1b2a; }

/* ============================================================================
   DARK MODE — content-chrome surfaces that carry raw light hex (no token).
   Additive / dark-only (light rules above untouched); consumes the dark token
   palette emitted by archer-bootstrap.css. Folds into archer-app.css at port.
   ============================================================================ */
/* Welcome bar: darken the bar bg + text-zone fade so the greeting sits on dark
   (the scene PNG art stays bright — deferred). */
/* Shell --ar-* namespace: preview-shell.css :root (above) re-declares these LOCKED brand
   values and loads AFTER archer-bootstrap.css, so its light values were beating the
   [data-bs-theme="dark"] overrides in scss/_shell-tokens.scss (equal specificity, later
   source wins). The dark overrides for exactly those re-declared tokens must therefore live
   HERE. This is what makes the sidebar nav + chrome text legible on dark. (--ar-canvas /
   --ar-sidebar-bg / --ar-divider / --ar-ink-soft are NOT re-declared above, so their
   _shell-tokens dark values already win — not repeated here.) */
[data-bs-theme="dark"] {
  --ar-text:         #e8eaef;   /* chrome + sidebar nav item text → off-white */
  --ar-text-muted:   #aab3c1;   /* sidebar section labels / muted chrome text → light */
  --ar-ink:          #d7dbe2;   /* chrome ink edges → light */
  --ar-primary:      #7fd9b3;   /* lighter MINT — active-nav TEXT reads on the dark sidebar (variant C) */
  --ar-primary-soft: rgba(127, 217, 179, 0.22);   /* mint tint for the active nav fill — lifted from .16
                                                      so the filled active row reads as a state on its own
                                                      (the dark chrome carries less tint-vs-bg separation
                                                      than light); label stays the lighter mint --ar-primary */
  --ar-chrome:       #242932;   /* variant C — neutral GRAY L-chrome (recedes so content leads) */
}

/* Chrome DIRECTION variants for the review panel — switch via <html data-chrome="a|b|c">.
   No attribute = the default block above (B). Higher specificity (0,2,0) so a set attribute wins. */
[data-bs-theme="dark"][data-chrome="a"] {   /* A — brand green held CONSTANT */
  --ar-chrome: #315025; --ar-primary: #88c071; --ar-primary-soft: rgba(136, 192, 113, 0.16);
}
[data-bs-theme="dark"][data-chrome="b"] {   /* B — darker / desaturated green */
  --ar-chrome: #29381f; --ar-primary: #88c071; --ar-primary-soft: rgba(136, 192, 113, 0.16);
}
[data-bs-theme="dark"][data-chrome="c"] {   /* C — neutral GRAY rail + MINT primary accent (the chosen default) */
  --ar-chrome: #242932; --ar-primary: #7fd9b3; --ar-primary-soft: rgba(127, 217, 179, 0.22);   /* .22 — kept in sync with the default dark block (line ~1034) so explicit data-chrome=c does not silently revert the active-nav lift */
}

/* NO dark-only active-state treatment — the rail tile + sidebar item use the SAME active pattern as
   light (rail = tonal patch + chrome-fg; sidebar = --ar-primary-soft fill + --ar-primary text), and
   those flip via tokens. A dark-only edge-bar would be a forked decision, so it is intentionally
   absent. */

[data-bs-theme="dark"] .welcome-bar { --scene-bg: var(--canvas); --scene-bg-rgb: 20, 22, 27; }
[data-bs-theme="dark"] .welcome-name { color: var(--fg-1); }
[data-bs-theme="dark"] .welcome-greeting,
[data-bs-theme="dark"] .welcome-sub { color: var(--fg-2); }
/* Home dashboard content chrome (port classes — not on the current styleguide index,
   which uses .card; included so the sheet folds in clean for dark). */
[data-bs-theme="dark"] .quest-card,
[data-bs-theme="dark"] .gtk-card,
[data-bs-theme="dark"] .useful-links,
[data-bs-theme="dark"] .table-card { background: var(--bg-2); }
[data-bs-theme="dark"] .quest-card-banner { background: var(--bg-3); color: var(--fg-1); }
[data-bs-theme="dark"] .status-pill.status-success { background: var(--success-bg); color: var(--success-dark); }
[data-bs-theme="dark"] .status-pill.status-warning { background: var(--warning-bg); color: var(--warning-dark); }
[data-bs-theme="dark"] .status-pill.status-info    { background: var(--info-bg); color: var(--info-dark); }
[data-bs-theme="dark"] .status-pill.status-danger  { background: var(--danger-bg); color: var(--danger-dark); }

/* ============================================================================
   Skip link (a11y) — only visible on focus
   ============================================================================ */
.skip-link {
  position: absolute;
  top: -100px;
  left: 6px;
  padding: 8px 12px;
  background: var(--ar-ink);
  color: #ffffff;
  z-index: 100;
  border-radius: 4px;
}
.skip-link:focus { top: 6px; }

/* ============================================================================
   RAIL ICONS — the --rail-icon-* mask URLs are populated by preview.css under
   [data-icons="..."] selectors. Changing <html data-icons="tabler"> at runtime
   swaps the family across every page that consumes these classes.
   ============================================================================ */
/* Carbon `ci` icons used in the rail render at rail size. (The legacy
   .rail-icon / .app-icon rules below remain so the baseline index.html
   reference keeps rendering against preview.css.) */
.app-rail-item .ci { width: 24px; height: 24px; }

.rail-icon {
  display: block;
  width: 24px;
  height: 24px;
  background-color: currentColor;
  -webkit-mask-size: contain;          mask-size: contain;
  -webkit-mask-repeat: no-repeat;      mask-repeat: no-repeat;
  -webkit-mask-position: center;       mask-position: center;
}
.rail-icon-home { -webkit-mask-image: var(--rail-icon-home); mask-image: var(--rail-icon-home); }
.rail-icon-team { -webkit-mask-image: var(--rail-icon-team); mask-image: var(--rail-icon-team); }
.rail-icon-residents { -webkit-mask-image: var(--rail-icon-residents); mask-image: var(--rail-icon-residents); }
.rail-icon-schedule { -webkit-mask-image: var(--rail-icon-schedule); mask-image: var(--rail-icon-schedule); }
.rail-icon-action-items { -webkit-mask-image: var(--rail-icon-action-items); mask-image: var(--rail-icon-action-items); }
.rail-icon-insights { -webkit-mask-image: var(--rail-icon-insights); mask-image: var(--rail-icon-insights); }
.rail-icon-settings { -webkit-mask-image: var(--rail-icon-settings); mask-image: var(--rail-icon-settings); }

/* ============================================================================
   APP ICONS — every non-rail icon in the preview (topbar utility cluster,
   canvas section heads, inline announcement / quest / list glyphs) is a
   .app-icon span with a per-glyph class that consumes --app-icon-<name>.
   URL vars are set by preview.css [data-icons="..."]. Drop-in replacement
   for <i class="bi-X"> font icons.
   ============================================================================ */
.app-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  background-color: currentColor;
  -webkit-mask-size: contain;          mask-size: contain;
  -webkit-mask-repeat: no-repeat;      mask-repeat: no-repeat;
  -webkit-mask-position: center;       mask-position: center;
  vertical-align: -0.125em;            /* baseline align like a bi font glyph */
}
.app-icon-lightbulb              { -webkit-mask-image: var(--app-icon-lightbulb);              mask-image: var(--app-icon-lightbulb); }
.app-icon-question-circle        { -webkit-mask-image: var(--app-icon-question-circle);        mask-image: var(--app-icon-question-circle); }
.app-icon-bell                   { -webkit-mask-image: var(--app-icon-bell);                   mask-image: var(--app-icon-bell); }
.app-icon-newspaper              { -webkit-mask-image: var(--app-icon-newspaper);              mask-image: var(--app-icon-newspaper); }
.app-icon-mortarboard            { -webkit-mask-image: var(--app-icon-mortarboard);            mask-image: var(--app-icon-mortarboard); }
.app-icon-balloon-heart          { -webkit-mask-image: var(--app-icon-balloon-heart);          mask-image: var(--app-icon-balloon-heart); }
.app-icon-shield-exclamation     { -webkit-mask-image: var(--app-icon-shield-exclamation);     mask-image: var(--app-icon-shield-exclamation); }
.app-icon-chevron-right          { -webkit-mask-image: var(--app-icon-chevron-right);          mask-image: var(--app-icon-chevron-right); }
.app-icon-link-45deg             { -webkit-mask-image: var(--app-icon-link-45deg);             mask-image: var(--app-icon-link-45deg); }
.app-icon-flag                   { -webkit-mask-image: var(--app-icon-flag);                   mask-image: var(--app-icon-flag); }
.app-icon-exclamation-triangle-fill { -webkit-mask-image: var(--app-icon-exclamation-triangle-fill); mask-image: var(--app-icon-exclamation-triangle-fill); }
.app-icon-calendar-check         { -webkit-mask-image: var(--app-icon-calendar-check);         mask-image: var(--app-icon-calendar-check); }
.app-icon-pen                    { -webkit-mask-image: var(--app-icon-pen);                    mask-image: var(--app-icon-pen); }
.app-icon-megaphone              { -webkit-mask-image: var(--app-icon-megaphone);              mask-image: var(--app-icon-megaphone); }

/* ============================================================================
   MOBILE REVIEW FIXES — content-surface rules (fold into archer-app.css at port).
   ============================================================================ */

/* 2A — priority-badge glyph. A leading shape cue (caret-up / dash / caret-down)
   inside the priority badge so rank survives grayscale / colour-blindness; sized
   down so it reads as a small mark, not a full icon. */
.badge .ci { width: .85em; height: .85em; margin-right: .22em; vertical-align: -.08em; }

/* 1A — compact KPI strip (phone). The count cards collapse to a tight, stamped
   mini-tile band so the table/schedule reaches the first phone screen. Each card's
   own markup base is col-6, so the strip is 2-UP on phone (the sanctioned
   row-cols-sm-2 stat-grid posture) — NOT a forced 4-up. 2-up gives long labels
   ("Scheduled", "Total variance") room to sit on one or two centred lines without
   clipping the card, while two rows of two still fit above the fold. The icon tile
   stacks above the value (column layout, centred) so the value reads as the lead.
   Value/label SIZE comes from the shared _kpi.scss phone rung (.archer-stat__value
   / .archer-stat__label), not from here. */
@media (max-width: 575.98px) {
  .archer-kpi { --bs-gutter-x: .5rem; }
  .archer-kpi .card-body {
    flex-direction: column; align-items: stretch; justify-content: flex-start;
    flex-wrap: nowrap;     /* phone owns the column stack; neutralise the _kpi.scss container-query wrap */
    text-align: center; gap: .25rem; padding: .6rem .4rem;
  }
  .archer-kpi .icon-tile { width: 2rem; height: 2rem; align-self: center; }
  .archer-kpi .icon-tile .ci { width: 1.1rem; height: 1.1rem; }
  /* the value/label block is a stretched (full card-body width) centred column, so
     the label wraps within the card edge (e.g. "Total variance" -> two lines at
     320px) instead of shrink-wrapping to one line and spilling past the card. The
     flex reset undoes the _kpi.scss narrow-tile rule (flex:1 1 100%) which, in this
     column stack, would mis-size the block. align-self:stretch (not width:100%)
     keeps the block inside the body's content box. */
  .archer-kpi .card-body > :not(.icon-tile) {
    flex: 0 0 auto; align-self: stretch; min-width: 0; max-width: 100%;
    overflow-wrap: anywhere;
  }
  .archer-kpi .archer-stat__value { max-width: 100%; }
  .archer-kpi .archer-stat__label { line-height: 1.15; white-space: normal; }
}

/* Dual card/table — on phones the dense ledger collapses to a flush single-column
   card list (`.archer-card-list.d-md-none`); the table stays for md+. The list is
   one stamped panel (ink edge + hard shadow), rows divided by a cool hairline, so
   it reads as the ledger laid out vertically — and nothing scrolls sideways. */
.archer-card-list {
  background: var(--bg-2);   /* surface token (#fff light / #20242c dark) — was a raw #fff that
                                stranded the mobile card-list white in dark; #no-raw-values, light-inert.
                                PORT: the production .archer-card-list must consume var(--bg-2), not #fff. */
  border: 2px solid var(--border-2, #1a1d24);
  border-radius: var(--radius-default, 2px);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
}
.archer-row-card {
  display: block;
  padding: 12px 14px;
  color: inherit;
  text-decoration: none;
  border-top: 1px solid var(--table-border-cool, #d6dae0);
}
.archer-row-card:first-child { border-top: 0; }
.archer-row-card:hover { background: var(--row-hover, #ebedf1); }   /* neutral darken — matches the desktop table row hover */
.archer-row-card:focus-visible { outline: 2px solid var(--ar-ink, #1a1d24); outline-offset: -2px; }
.archer-row-card .avatar { flex-shrink: 0; }

/* Mobile: pare the list toolbar to essentials. Status tabs + export hide below md
   (filter via search / the cards); search and the New action item stack full-width
   so the content is reached sooner. */
@media (max-width: 767.98px) {
  .archer-list-toolbar,
  .archer-list-toolbar > .d-flex { flex-direction: column; align-items: stretch; width: 100%; }
  .archer-list-toolbar .input-group { max-width: none !important; }
  .archer-list-toolbar .btn-primary { width: 100%; justify-content: center; }
  /* The stacked column keeps every direct action full-width; a desktop
     right-pin (ms-auto) otherwise shoves the button off the stretched
     column's edge and clips it. */
  .archer-list-toolbar > .ms-auto { margin-left: 0 !important; }
  .archer-list-toolbar > .btn { width: 100%; justify-content: center; }
}

/* Notifications toolbar keeps the category dropdown and the Unread/Read toggle on
   ONE row on phones (the shared .archer-list-toolbar rule above otherwise stacks
   every child full-width). Scoped to #notifToolbar so the ~50 other list pages are
   untouched. "Mark all read" wraps to its own full-width line below. */
@media (max-width: 767.98px) {
  #notifToolbar { flex-direction: row; flex-wrap: wrap; align-items: center; }
  #notifToolbar > #notifCategoryFilter { flex: 1 1 auto; min-width: 0; }
  #notifToolbar > .btn-group { flex: 0 0 auto; width: auto; }
  #notifToolbar > .btn { width: 100%; justify-content: center; }
}

/* Quarterly-review community filters: keep the department and status dropdowns on ONE
   row on phones (the shared .archer-list-toolbar rule above otherwise stacks them). The
   search input wraps to its own full-width line above the two dropdowns. Scoped to
   #qrFilterRow so other list pages are untouched (same precedent as #notifToolbar). */
@media (max-width: 767.98px) {
  #qrFilterRow { flex-direction: row; flex-wrap: wrap; align-items: center; }
  #qrFilterRow > .input-group { flex: 1 1 100%; }
  #qrFilterRow > .qr-filter-field { flex: 1 1 0; min-width: 0; }
  #qrFilterRow > .qr-filter-field .archer-dropdown { min-width: 0; }
}

/* Toolbar field width clamps — tokenized out of inline styles (#no-raw-values).
   A search input / dropdown filter has no intrinsic width, so it needs a floor;
   the search is also capped so it doesn't sprawl. The phone rule above relaxes
   the cap to full-width. */
.archer-field-cap { min-width: 220px; max-width: 480px; }
.archer-field-min { min-width: 220px; }

/* Django messages: legacy style.css pins #messagesframework as position:fixed with
   margin-top:-66px and z-index:2000, a holdover from the old header. In the redesign
   shell messages are included in-flow at the top of the content region, and that fixed
   pin makes the alert paint over the sticky nav and, worse, cut through the open mobile
   offcanvas menu (z-index 1045). Render it in normal flow below the nav so nothing
   app-rendered overlays the menu. */
#messagesframework {
  position: static;
  margin-top: 0;
  max-width: none;
  z-index: auto;
}

/* Validity-aware week-nav (#date-nav / #states-visual): a disabled stepper or a past
   picker option reads dimmed + inert, so it is OBVIOUS which periods can't be reached
   (e.g. a past week on the open-shifts pickup surface). */
.emp-weeknav-mobile a[aria-disabled="true"] { opacity: 0.4; pointer-events: none; }
.weeknav-period-panel .archer-dropdown-option:disabled { opacity: 0.45; cursor: not-allowed; }

/* ============================================================================
   "Why this page" panel (STAGING ONLY) — rendered by _assets/js/preview-why.js
   from the embedded #archer-page-manifest. A dev-facing overlay that surfaces the
   page-local what/how/controls/why/deviations beside the live example. Consumes
   tokens only (no raw page values); folds into archer-app.css at port (or is
   dropped if the port renders the panel server-side). Hard-stamp grammar so it
   reads as Archer chrome, but clearly a meta overlay.
   ============================================================================ */
.archer-why-toggle {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 1090;
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.5rem 0.85rem; font-weight: 600; font-size: 0.875rem; line-height: 1;
  color: #fff; background: var(--bs-primary); border: 1.5px solid var(--border-2);
  border-radius: 2px; box-shadow: 3px 3px 0 var(--ink); cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}
.archer-why-toggle .ci { width: 1em; height: 1em; }
.archer-why-toggle:hover { background: var(--archer-green-dark); }
.archer-why-toggle:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
.archer-why-toggle:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
body.archer-why-open .archer-why-toggle { right: calc(min(420px, 92vw) + 1rem); }

.archer-why-panel {
  position: fixed; top: 0; right: 0; z-index: 1090;
  width: min(420px, 92vw); height: 100vh; height: 100dvh; overflow-y: auto;
  background: var(--bs-body-bg); color: var(--bs-body-color);
  border-left: 1.5px solid var(--border-2); box-shadow: -3px 0 0 var(--ink);
  font-size: 0.8125rem; line-height: 1.45;
}
.archer-why-head {
  position: sticky; top: 0; z-index: 1;
  display: grid; grid-template-columns: 1fr auto; align-items: start; gap: 0.25rem 0.5rem;
  padding: 0.85rem 1rem; background: var(--bs-primary); color: #fff;
  border-bottom: 1.5px solid var(--ink);
}
.archer-why-eyebrow { grid-column: 1; margin: 0; font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.85; }
.archer-why-title { grid-column: 1; margin: 0; font-size: 1.05rem; }
.archer-why-close {
  grid-column: 2; grid-row: 1 / span 2; align-self: center;
  width: 2rem; height: 2rem; display: inline-flex; align-items: center; justify-content: center;
  color: #fff; background: transparent; border: 1.5px solid rgba(255,255,255,0.55); border-radius: 2px; cursor: pointer;
}
.archer-why-close:hover { background: rgba(0,0,0,0.15); }
.archer-why-close:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }

.archer-why-body { padding: 0.5rem 1rem 2rem; }
.archer-why-sec { margin-top: 1.1rem; }
.archer-why-sec-h {
  margin: 0 0 0.4rem; font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--bs-secondary-color); border-bottom: 1px dashed var(--bs-border-color); padding-bottom: 0.25rem;
}
.archer-why-dl { margin: 0; }
.archer-why-dl dt { font-weight: 700; font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--bs-secondary-color); margin-top: 0.5rem; }
.archer-why-dl dd { margin: 0.1rem 0 0; }
.archer-why-list { list-style: none; margin: 0; padding: 0; }
.archer-why-item { padding: 0.5rem 0; border-bottom: 1px dashed var(--bs-border-color); }
.archer-why-item:last-child { border-bottom: 0; }
.archer-why-item-h { margin: 0; }
.archer-why-why { margin: 0.2rem 0 0; color: var(--bs-secondary-color); }
.archer-why-how { color: var(--bs-body-color); }
.archer-why-atom {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 700;
  color: var(--bs-primary); text-decoration: none; border-bottom: 1px solid currentColor;
}
.archer-why-atom:hover { color: var(--archer-green-dark); }
.archer-why-dev {
  display: inline-block; margin-right: 0.4rem; padding: 0.05rem 0.4rem; border-radius: 2px;
  font-size: 0.625rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  border: 1px solid var(--ink); color: #fff; background: var(--bs-secondary);
}
.archer-why-dev--fix { background: var(--bs-danger); }
.archer-why-dev--verify { background: var(--bs-warning); color: var(--ink); }
.archer-why-dev--inferred { background: var(--bs-info); color: var(--ink); }
.archer-why-dev--intentional { background: var(--bs-success); }
.archer-why-dev--documented { background: var(--bs-secondary); }
.archer-why-foot { margin-top: 1.2rem; padding-top: 0.6rem; border-top: 1.5px solid var(--ink); color: var(--bs-secondary-color); }
.archer-why-foot-lbl { font-weight: 700; }
.archer-why-chip { display: inline-block; margin: 0.1rem 0.2rem 0.1rem 0; padding: 0 0.3rem; background: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: 2px; font-size: 0.6875rem; }
.archer-why-note { margin: 0.6rem 0 0; font-size: 0.6875rem; font-style: italic; }
@media (prefers-reduced-motion: reduce) {
  .archer-why-toggle { transition: none; }
}
