/* ============================================================================
   FinCheqIQ — App Shell + Pre-Auth Layout  ·  Phase 1 Foundation  ·  LOCKED
   ========================================================================== */

/* ══ APP SHELL ════════════════════════════════════════════════════════════ */
.app { display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr); min-height: 100vh; }
.app.is-rail { grid-template-columns: var(--sidebar-rail-w) minmax(0, 1fr); }

/* ── sidebar ───────────────────────────────────────────────────────────── */
.sidebar {
  position: sticky; top: 0; z-index: var(--z-shell);
  display: flex; flex-direction: column; height: 100vh;
  background: var(--chrome); color: var(--chrome-ink);
  border-inline-end: 1px solid var(--chrome);
  transition: width var(--motion-standard);
}
.sidebar-brand {
  display: flex; align-items: center; gap: var(--sp-3); height: var(--topbar-h);
  padding: 0 var(--sp-4); border-bottom: 1px solid var(--line-inverse); flex: none;
}
.brand-mark { width: 28px; height: 28px; flex: none; border-radius: var(--r-md); }
.brand-word { font-family: var(--font-head); font-size: var(--fs-lg); font-weight: 700; letter-spacing: -0.015em; color: #fff; white-space: nowrap; }
.brand-word em { font-style: normal; color: var(--focus-ring); }

.sidebar-nav { flex: 1; overflow-y: auto; overflow-x: hidden; padding: var(--sp-4) var(--sp-3) var(--sp-6); }
/* 37 destinations cannot fit an 800px viewport, so the nav must scroll — but a
   permanently-drawn thumb beside the page's own scrollbar reads as two competing
   rails. The thumb resolves on hover or keyboard focus and the track is never
   drawn, so the scroll stays discoverable without becoming chrome. */
.sidebar-nav { scrollbar-color: transparent transparent; }
.sidebar:hover .sidebar-nav,
.sidebar-nav:focus-within { scrollbar-color: var(--line-strong) transparent; }
.sidebar-nav::-webkit-scrollbar-thumb { background: transparent; }
.sidebar:hover .sidebar-nav::-webkit-scrollbar-thumb,
.sidebar-nav:focus-within::-webkit-scrollbar-thumb {
  background: var(--line-strong); background-clip: content-box;
}
@media (prefers-reduced-motion: no-preference) {
  .sidebar-nav::-webkit-scrollbar-thumb { transition: background var(--dur-micro) var(--ease-out); }
}
.nav-group + .nav-group { margin-top: var(--sp-5); }
.nav-group-label {
  padding: 0 var(--sp-3) var(--sp-2);
  font-family: var(--font-head); font-size: var(--fs-2xs); font-weight: 600;
  letter-spacing: var(--tr-wide); text-transform: uppercase; color: var(--chrome-ink-3); white-space: nowrap;
}
.nav-item {
  position: relative; display: flex; align-items: center; gap: var(--sp-3); width: 100%;
  height: 34px; padding: 0 var(--sp-3); border-radius: var(--r-md);
  font-size: var(--fs-sm); color: var(--chrome-ink-2); text-align: start; white-space: nowrap;
  transition: background var(--motion-micro), color var(--motion-micro);
}
.nav-item + .nav-item { margin-top: 1px; }
.nav-item svg { width: 16px; height: 16px; flex: none; opacity: .85; }
.nav-item .nav-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.nav-item:hover { background: var(--chrome-2); color: var(--chrome-ink); }
.nav-item[aria-current="page"] { background: var(--chrome-3); color: #fff; font-weight: 500; }
.nav-item[aria-current="page"]::before {
  content: ''; position: absolute; inset-inline-start: calc(var(--sp-3) * -1 + 1px); top: 6px; bottom: 6px;
  width: 3px; background: var(--focus-ring); border-radius: 0 var(--r-xs) var(--r-xs) 0;
}
.nav-item[aria-current="page"] svg { opacity: 1; color: var(--focus-ring); }
/* not built yet — muted, still reachable, never a broken link */
.nav-item.is-planned { color: var(--chrome-ink-3); }
.nav-item.is-planned:hover { background: var(--chrome-2); color: var(--chrome-ink-2); }
.phase-tag {
  flex: none; padding: 1px 5px; border: 1px solid var(--line-inverse); border-radius: var(--r-sm);
  font-family: var(--font-mono); font-size: var(--fs-3xs); letter-spacing: .04em; color: var(--chrome-ink-3);
  transition: opacity var(--motion-micro);
}
.nav-item:hover .phase-tag { color: var(--chrome-ink-2); border-color: var(--chrome-ink-3); }


/* rail (tablet-lg): icons only, label revealed on hover as a tooltip */
.app.is-rail .brand-word,
.app.is-rail .nav-group-label,
.app.is-rail .nav-label,
.app.is-rail .phase-tag { display: none; }
.app.is-rail .sidebar-brand { justify-content: center; padding: 0; }
.app.is-rail .nav-item { justify-content: center; padding: 0; }
.app.is-rail .nav-group + .nav-group { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--line-inverse); }
.app.is-rail .nav-item[aria-current="page"]::before { inset-inline-start: -12px; }

/* ── topbar ────────────────────────────────────────────────────────────── */
.topbar {
  position: sticky; top: 0; z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--sp-3);
  height: var(--topbar-h); padding: 0 var(--sp-5);
  /* solid, not frosted: a bank's operations header should read as chrome the
     data sits under, not as a translucent consumer-app surface */
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
/* the one navigation control: collapses the sidebar to the icon rail above
   tablet-lg, opens the off-canvas drawer at tablet and below */
.topbar-hamburger { display: inline-grid; }

.search {
  position: relative; flex: 1; max-width: 460px;
}
.search .input { height: 34px; padding-inline-start: 34px; background: var(--surface-2); box-shadow: none; }
.search .input:focus { background: var(--surface); }
.search-icon { position: absolute; inset-inline-start: 10px; top: 9px; width: 16px; height: 16px; color: var(--ink-3); pointer-events: none; }
.search-hint { position: absolute; inset-inline-end: 8px; top: 7px; pointer-events: none; }
.search-toggle { display: none; }

.topbar-right { display: flex; align-items: center; gap: var(--sp-2); margin-inline-start: auto; }

/* environment / bank instance indicator */
.env-chip {
  display: inline-flex; align-items: center; gap: var(--sp-2); height: 30px; padding: 0 var(--sp-3);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md);
  font-size: var(--fs-xs); color: var(--ink-2); white-space: nowrap;
}
.env-dot { width: 7px; height: 7px; border-radius: var(--r-full); flex: none; }
.env-dot--valid { background: var(--st-valid); box-shadow: 0 0 0 3px var(--st-valid-bg); }

.user-btn {
  display: flex; align-items: center; gap: var(--sp-2); height: 38px; padding: 0 var(--sp-2) 0 var(--sp-1);
  border-radius: var(--r-md); transition: background var(--motion-micro);
}
.user-btn:hover { background: var(--surface-3); }
.user-meta { text-align: start; line-height: 1.15; }
.user-name { font-size: var(--fs-sm); font-weight: 500; white-space: nowrap; }
.user-role { font-size: var(--fs-2xs); color: var(--ink-3); white-space: nowrap; }
.has-menu { position: relative; }

/* ── page canvas ───────────────────────────────────────────────────────── */
/* safety net: the app frame must never scroll sideways. `clip` (not `hidden`)
   because it doesn't create a scroll container, so the sticky topbar and the
   fixed mobile decision panel keep working. Real scrollers — tables, the
   payload viewer, the workflow canvas — carry their own overflow-x. */
.main-col { display: flex; flex-direction: column; min-width: 0; overflow-x: clip; }
/* No max-width: an operations screen uses the monitor it is given. A 2,560px
   ops display should show more of the queue, not the same 1,440px column with
   560px of grey either side. Prose does not run wild as a result — every
   paragraph-level element carries its own measure cap (74–78ch), so tables and
   grids take the width while sentences stay readable. */
.page { flex: 1; padding: var(--sp-6) var(--gutter) var(--sp-9); width: 100%; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-5); flex-wrap: wrap; margin-bottom: var(--sp-6); }
.page-title { font-size: var(--fs-2xl); line-height: var(--lh-2xl); margin-top: var(--sp-2); }
.page-sub { font-size: var(--fs-sm); color: var(--ink-3); margin-top: var(--sp-2); max-width: 74ch; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.section-head h2 { font-size: var(--fs-xl); }
.section-head p { font-size: var(--fs-sm); color: var(--ink-3); max-width: 78ch; margin-top: var(--sp-2); }

/* placeholder banner for nav destinations not built yet */
.planned-note {
  display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-4);
  background: var(--st-processing-bg); border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-size: var(--fs-sm); color: var(--st-processing-ink);
}
.planned-note svg { width: 17px; height: 17px; flex: none; margin-top: 1px; }

/* ── responsive: off-canvas at tablet and below ────────────────────────── */
@media (max-width: 1279px) and (min-width: 1024px) {
  .app { grid-template-columns: var(--sidebar-rail-w) minmax(0, 1fr); }
}
@media (max-width: 1023px) {
  .app, .app.is-rail { grid-template-columns: minmax(0, 1fr); }
  .sidebar {
    position: fixed; top: 0; inset-inline-start: 0; bottom: 0; width: var(--sidebar-w);
    z-index: var(--z-drawer);          /* above its own scrim (--z-overlay) */
    transform: translateX(-100%); box-shadow: none;
    transition: transform var(--dur-exit) var(--ease-inout);
  }
  .app.nav-open .sidebar { transform: none; box-shadow: var(--e-3); transition: transform var(--motion-emphasis); }
  /* rail styles must not leak into the off-canvas drawer */
  .app.is-rail .brand-word, .app.is-rail .nav-group-label,
  .app.is-rail .nav-label, .app.is-rail .phase-tag { display: revert; }
  .app.is-rail .sidebar-brand { justify-content: flex-start; padding: 0 var(--sp-4); }
  .app.is-rail .nav-item { justify-content: flex-start; padding: 0 var(--sp-3); }
  .app.is-rail .nav-group + .nav-group { border-top: 0; margin-top: var(--sp-5); padding-top: 0; }
  .topbar { padding: 0 var(--sp-4); }
  .page { padding: var(--sp-5) var(--sp-4) var(--sp-8); }
}
@media (max-width: 767px) {
  /* topbar condenses: search becomes an icon that expands over the bar */
  .search { position: absolute; inset-inline-start: var(--sp-3); inset-inline-end: var(--sp-3); max-width: none; display: none; }
  .search.is-expanded { display: block; z-index: 2; }
  .search-toggle { display: inline-flex; }
  .user-meta { display: none; }
  .user-btn { padding: 0; }
  :root { --topbar-h: 52px; }
  .page-head { align-items: flex-start; }
}
@media (max-width: 599px) {
  .page { padding: var(--sp-4) var(--sp-3) var(--sp-8); }
  .sidebar { width: min(292px, 86vw); }
  .env-chip { padding: 0 var(--sp-2); }
}

/* ══ PRE-AUTH LAYOUT ══════════════════════════════════════════════════════
   Deep-ink field with an engine-turned guilloché — the security engraving
   used on real cheque stock. One flourish for the whole product.           */
.auth {
  min-height: 100vh; display: flex; flex-direction: column;
  background: var(--chrome); color: var(--chrome-ink); position: relative; overflow: hidden;
}
.auth::before {
  content: ''; position: absolute; inset: -20%; pointer-events: none;
  background-image:
    repeating-radial-gradient(circle at 22% 34%, transparent 0 7px, rgba(190, 222, 236, .030) 7px 8px),
    repeating-radial-gradient(circle at 74% 66%, transparent 0 9px, rgba(190, 222, 236, .026) 9px 10px),
    repeating-radial-gradient(circle at 50% 110%, transparent 0 13px, rgba(190, 222, 236, .018) 13px 14px),
    repeating-linear-gradient(118deg, transparent 0 11px, rgba(190, 222, 236, .012) 11px 12px);
  animation: fq-guilloche 60s linear infinite alternate;
}
@keyframes fq-guilloche { from { transform: translate3d(0,0,0) rotate(0deg); } to { transform: translate3d(-1.5%, -1%, 0) rotate(.6deg); } }
.auth::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 90% 62% at 50% 30%, rgba(46, 143, 176, .16), transparent 72%);
}

.auth-bar {
  position: relative; z-index: 2; display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); padding: var(--sp-5) var(--sp-6); flex-wrap: wrap;
}
.auth-bar .brand-word { font-size: var(--fs-xl); }

.auth-main {
  position: relative; z-index: 2; flex: 1;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-6) var(--sp-5) var(--sp-8);
}
.auth-card {
  width: 100%; max-width: 424px; background: var(--surface); color: var(--ink);
  border-radius: var(--r-xl); box-shadow: var(--e-3); overflow: hidden;
  animation: fq-auth-in var(--motion-emphasis) both;
}
.auth-card.is-wide { max-width: 552px; }
/* A policy page is read, not filled in. It takes a reading measure rather than
   a form measure, and it starts at the top of the viewport rather than centring
   in it — a card the height of a login form centres; a document does not. */
.auth-card.is-doc { max-width: 760px; }
.auth-main.is-doc { align-items: flex-start; }

/* Document sections. A heading, its body, and a hairline between — no card per
   section, because eight stacked cards on one page reads as eight unrelated
   things rather than one document. */
.doc-sec + .doc-sec { margin-top: var(--sp-6); padding-top: var(--sp-6); border-top: 1px solid var(--line); }
.doc-sec > h2 {
  font-family: var(--font-head); font-size: var(--fs-lg); line-height: var(--lh-lg);
  color: var(--ink); margin-bottom: var(--sp-2);
}
.doc-sec > h2 .cite { margin-inline-start: var(--sp-2); }
.doc-sec p { font-size: var(--fs-sm); color: var(--ink-2); line-height: var(--lh-md); }
.doc-sec p + p { margin-top: var(--sp-3); }
.doc-lede { font-size: var(--fs-md); color: var(--ink-2); line-height: var(--lh-md); }

/* The rule list: one enforced behaviour per row, each carrying the requirement
   that makes it a rule rather than an opinion. */
.doc-list { display: flex; flex-direction: column; gap: var(--sp-4); margin-top: var(--sp-4); }
.doc-item { display: flex; align-items: flex-start; gap: var(--sp-3); }
.doc-item > svg { width: 17px; height: 17px; flex: none; margin-top: 2px; color: var(--ink-4); }
.doc-item .doc-item-body { min-width: 0; }
.doc-item strong { display: block; font-size: var(--fs-sm); color: var(--ink); margin-bottom: 2px; }
.doc-item span { font-size: var(--fs-sm); color: var(--ink-3); line-height: var(--lh-md); }
@keyframes fq-auth-in { from { opacity: 0; transform: translateY(10px) scale(.99); } to { opacity: 1; transform: none; } }
.auth-card-body { padding: var(--sp-7); }
.auth-card-foot { padding: var(--sp-4) var(--sp-7); background: var(--surface-2); border-top: 1px solid var(--line); font-size: var(--fs-xs); color: var(--ink-3); }
.auth-title { font-size: var(--fs-2xl); line-height: var(--lh-2xl); }
.auth-lead { font-size: var(--fs-sm); color: var(--ink-3); margin-top: var(--sp-2); }
.auth-link { color: var(--brand); font-weight: 500; text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--brand-tint-2); }
.auth-link:hover { text-decoration-color: var(--brand); }

/* a stamped seal above the title — reads as an issued/verified mark */
.auth-seal {
  width: 42px; height: 42px; display: grid; place-items: center;
  background: var(--brand-tint); border: 1px solid var(--brand-tint-2); border-radius: var(--r-lg); color: var(--brand);
}
.auth-seal svg { width: 21px; height: 21px; }
.auth-seal.is-warn { background: var(--st-review-bg); border-color: var(--st-review-line); color: var(--st-review); }
.auth-seal.is-danger { background: var(--st-invalid-bg); border-color: var(--st-invalid-line); color: var(--st-invalid); }
.auth-seal.is-valid { background: var(--st-valid-bg); border-color: var(--st-valid-line); color: var(--st-valid); }

.auth-foot {
  position: relative; z-index: 2; padding: var(--sp-5) var(--sp-6) var(--sp-6);
  border-top: 1px solid var(--line-inverse);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); flex-wrap: wrap;
}
.auth-notice { font-size: var(--fs-xs); color: var(--chrome-ink-3); max-width: 76ch; line-height: 1.5; }
.auth-notice strong { color: var(--chrome-ink-2); font-weight: 500; }
/* .muted-2 resolves to a dark page ink; inside the chrome footer that is
   near-invisible, so the chrome context supplies its own muted step */
.auth-foot .muted-2, .sys-foot .muted-2 { color: var(--chrome-ink-3); }
.auth-legal { display: flex; gap: var(--sp-4); font-size: var(--fs-xs); color: var(--chrome-ink-3); flex-wrap: wrap; }
.auth-legal a:hover { color: var(--chrome-ink-2); text-decoration: underline; text-underline-offset: 2px; }
/* The document you are already reading is named in its own footer. It stays
   readable rather than becoming a link that goes nowhere. */
.auth-legal a[aria-current="page"] { color: var(--chrome-ink); font-weight: 500; cursor: default; }
.auth-legal a[aria-current="page"]:hover { text-decoration: none; }

/* OTP code entry */
.otp { display: flex; gap: var(--sp-2); }
.otp input {
  width: 100%; height: 54px; min-width: 0; text-align: center;
  font-family: var(--font-mono); font-size: var(--fs-num-sm); font-weight: 600; font-variant-numeric: tabular-nums;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-md);
  box-shadow: var(--e-inset);
  transition: border-color var(--motion-micro), box-shadow var(--motion-micro), background var(--motion-micro);
}
.otp input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-tint-2); }
.otp input.is-filled { border-color: var(--ink-4); }
.otp.has-error input { border-color: var(--st-invalid); background: var(--st-invalid-bg); }
.otp.has-error { animation: fq-shake 320ms var(--ease-inout); }
@keyframes fq-shake { 0%,100% { transform: none; } 22% { transform: translateX(-5px); } 55% { transform: translateX(4px); } 80% { transform: translateX(-2px); } }
.otp.is-valid input { border-color: var(--st-valid); background: var(--st-valid-bg); }

/* password strength */
.strength { display: flex; gap: 3px; }
.strength i { flex: 1; height: 4px; background: var(--canvas-sunken); border-radius: var(--r-xs); transition: background var(--motion-standard); }
.strength[data-level="1"] i:nth-child(1) { background: var(--st-invalid); }
.strength[data-level="2"] i:nth-child(-n+2) { background: var(--st-review); }
.strength[data-level="3"] i:nth-child(-n+3) { background: var(--st-review); }
.strength[data-level="4"] i { background: var(--st-valid); }
.rule-list { display: grid; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.rule-list li { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-xs); color: var(--ink-3); transition: color var(--motion-micro); }
.rule-list li .rule-mark {
  width: 14px; height: 14px; flex: none; border-radius: var(--r-full);
  border: 1.5px solid var(--line-strong); display: grid; place-items: center;
  transition: background var(--motion-micro), border-color var(--motion-micro);
}
.rule-list li .rule-mark svg { width: 9px; height: 9px; color: #fff; opacity: 0; transition: opacity var(--motion-micro); }
.rule-list li.is-met { color: var(--st-valid-ink); }
.rule-list li.is-met .rule-mark { background: var(--st-valid); border-color: var(--st-valid); }
.rule-list li.is-met .rule-mark svg { opacity: 1; }

/* step indicator (activation) */
.steps { display: flex; align-items: center; gap: var(--sp-2); }
.step { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-xs); color: var(--ink-3); white-space: nowrap; }
.step-num {
  width: 20px; height: 20px; display: grid; place-items: center; flex: none;
  border: 1px solid var(--line-strong); border-radius: var(--r-full);
  font-family: var(--font-mono); font-size: var(--fs-micro); font-weight: 600; color: var(--ink-3);
  transition: all var(--motion-standard);
}
.step.is-current { color: var(--ink); font-weight: 500; }
.step.is-current .step-num { background: var(--brand); border-color: var(--brand); color: #fff; }
.step.is-done { color: var(--ink-3); }
.step.is-done .step-num { background: var(--st-valid); border-color: var(--st-valid); color: #fff; }
.step-line { flex: 1; min-width: 14px; height: 1px; background: var(--line-strong); }

@media (max-width: 599px) {
  .auth-bar { padding: var(--sp-4); }
  .auth-main { padding: var(--sp-4) var(--sp-3) var(--sp-6); align-items: flex-start; }
  .auth-card-body { padding: var(--sp-5); }
  .auth-card-foot { padding: var(--sp-3) var(--sp-5); }
  .auth-foot { padding: var(--sp-4) var(--sp-4) var(--sp-5); }
  .auth-title { font-size: var(--fs-xl); }
  .otp input { height: 46px; font-size: var(--fs-num-xs); }
  .step .step-label { display: none; }
}

/* ══ SHOWCASE-ONLY HELPERS (component-library.html) ═══════════════════════ */
.spec { display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); gap: var(--sp-3); }
.spec-chip { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; background: var(--surface); }
.spec-swatch { height: 46px; border-bottom: 1px solid var(--line); }
.spec-meta { padding: var(--sp-2) var(--sp-3); }
.spec-name { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--ink); }
.spec-val { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--ink-3); }
.demo-row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.demo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(224px, 1fr)); gap: var(--sp-4); }
.demo-label { font-family: var(--font-mono); font-size: var(--fs-2xs); letter-spacing: var(--tr-wide);
  text-transform: uppercase; color: var(--ink-3); margin-bottom: var(--sp-3); }
.demo-frame {
  border: 1px dashed var(--line-strong); border-radius: var(--r-md);
  padding: var(--sp-4); background: var(--surface-2);
}
.rule-table { font-size: var(--fs-sm); }
/* A reference table is still a table: below the point where its columns fit it
   scrolls inside its own wrapper, exactly as a queue table does — the alternative
   is columns clipped off the edge with nothing to say they are there. */
.rule-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
@media (max-width: 599px) { .rule-table { min-width: 520px; } }
.rule-table th { text-align: start; padding: var(--sp-2) var(--sp-4) var(--sp-2) 0; font-family: var(--font-head);
  font-size: var(--fs-2xs); letter-spacing: var(--tr-wide); text-transform: uppercase; color: var(--ink-3);
  border-bottom: 1px solid var(--line-strong); vertical-align: bottom; }
.rule-table td { padding: var(--sp-3) var(--sp-4) var(--sp-3) 0; border-bottom: 1px solid var(--line); color: var(--ink-2); vertical-align: top; }
.rule-table td:first-child, .rule-table th:first-child { padding-inline-start: 0; }
.rule-table tr:last-child td { border-bottom: 0; }
.cite { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--brand); white-space: nowrap; }

/* breakpoint demo — an iframe-free scaled preview of the shell at mobile */
.bp-demo { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: var(--sp-5); align-items: start; }
.bp-phone {
  width: 320px; border: 8px solid var(--chrome); border-radius: var(--r-device); overflow: hidden;
  background: var(--canvas); box-shadow: var(--e-2); position: relative;
}
.bp-phone-bar {
  display: flex; align-items: center; gap: var(--sp-2); height: 44px; padding: 0 var(--sp-3);
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.bp-phone-body { position: relative; height: 300px; overflow: hidden; padding: var(--sp-3); }
.bp-sheet {
  position: absolute; inset: 0 34% 0 0; background: var(--chrome); padding: var(--sp-3);
  transform: translateX(-100%); transition: transform var(--motion-emphasis); z-index: 2;
}
.bp-phone.nav-open .bp-sheet { transform: none; box-shadow: var(--e-3); }
.bp-scrim { position: absolute; inset: 0; background: rgba(14,22,30,.45); opacity: 0; pointer-events: none;
  transition: opacity var(--motion-standard); }
.bp-phone.nav-open .bp-scrim { opacity: 1; pointer-events: auto; }
@media (max-width: 1023px) { .bp-demo { grid-template-columns: minmax(0, 1fr); } .bp-phone { max-width: 100%; } }
@media (max-width: 359px) { .bp-phone { width: 100%; } }

/* ══ SYSTEM STATES — Phase 2 ══════════════════════════════════════════════
   Full-page states (splash, maintenance) reuse the pre-auth .auth chrome so
   the product has ONE out-of-shell composition, not two. In-shell states
   (404, 403, crash) use .page-center inside the normal shell.              */

/* ── the status mark: one shape language, seven family tones ───────────── */
.sys-mark {
  width: 60px; height: 60px; flex: none;
  display: grid; place-items: center; border-radius: var(--r-xl);
  border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink-3);
}
.sys-mark svg { width: 30px; height: 30px; }
.sys-mark--pending { background: var(--st-pending-bg); border-color: var(--st-pending-line); color: var(--st-pending); }
.sys-mark--hold    { background: var(--st-hold-bg);    border-color: var(--st-hold-line);    color: var(--st-hold); }
.sys-mark--review  { background: var(--st-review-bg);  border-color: var(--st-review-line);  color: var(--st-review); }
.sys-mark--invalid { background: var(--st-invalid-bg); border-color: var(--st-invalid-line); color: var(--st-invalid); }
.sys-mark--valid   { background: var(--st-valid-bg);   border-color: var(--st-valid-line);   color: var(--st-valid); }
.sys-mark--brand   { background: var(--brand-tint);    border-color: var(--brand-tint-2);    color: var(--brand); }

/* ── in-shell centred panel (404 · 403 · crash) ───────────────────────── */
.page-center {
  display: grid; place-items: center;
  min-height: calc(100vh - var(--topbar-h) - var(--sp-6) * 2);
  padding-block: var(--sp-8);
}
.sys-panel {
  width: 100%; max-width: 520px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-5);
  /* calm entrance — a settle, never an alarm */
  animation: fq-sys-in var(--motion-emphasis) both;
}
.sys-panel.is-wide { max-width: 640px; }
@keyframes fq-sys-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.sys-panel h1 { font-size: var(--fs-2xl); line-height: var(--lh-2xl); }
.sys-panel .sys-lead { font-size: var(--fs-md); color: var(--ink-3); max-width: 48ch; }
.sys-panel .sys-actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; justify-content: center; }
.sys-panel .sys-detail { width: 100%; text-align: start; }
.sys-route {
  display: inline-flex; align-items: center; gap: var(--sp-2); max-width: 100%;
  padding: var(--sp-2) var(--sp-3); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-md);
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--ink-3);
}
.sys-route span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 599px) {
  .page-center { min-height: calc(100vh - var(--topbar-h) - var(--sp-4) * 2); padding-block: var(--sp-6); }
  .sys-panel h1 { font-size: var(--fs-xl); }
  .sys-panel .sys-actions .btn { flex: 1 1 100%; }
}

/* ── copyable reference id ────────────────────────────────────────────── */
.ref-copy {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-3);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
}
.ref-copy code { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--ink); letter-spacing: .02em; }

/* ══ SPLASH ═══════════════════════════════════════════════════════════════
   The mark draws its own signature stroke, then settles. On-brand, not a
   stock spinner. Loops until the shell is ready.                           */
.splash {
  position: relative; z-index: 2;      /* above .auth::before / ::after guilloché */
  flex: 1; display: grid; place-items: center; padding: var(--sp-8) var(--sp-6);
}
.splash-inner { display: flex; flex-direction: column; align-items: center; gap: var(--sp-6); text-align: center; width: 100%; max-width: 340px; }
.splash-mark { width: 72px; height: 72px; }
.splash-mark .sp-plate { animation: fq-plate 2600ms var(--ease-inout) infinite; transform-origin: center; }
.splash-mark .sp-rule { stroke-dasharray: 14; stroke-dashoffset: 14; animation: fq-rule 2600ms var(--ease-out) infinite; }
.splash-mark .sp-rule-2 { animation-delay: 120ms; }
.splash-mark .sp-sign { stroke-dasharray: 26; stroke-dashoffset: 26; animation: fq-sign 2600ms var(--ease-inout) infinite; }
@keyframes fq-plate { 0%, 100% { opacity: .92; } 50% { opacity: 1; } }
@keyframes fq-rule {
  0%   { stroke-dashoffset: 14; }
  22%  { stroke-dashoffset: 0; }
  76%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: 14; }
}
@keyframes fq-sign {
  0%   { stroke-dashoffset: 26; }
  16%  { stroke-dashoffset: 26; }
  48%  { stroke-dashoffset: 0; }
  80%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: 26; }
}
/* reduced motion: hold the finished mark, no drawing loop */
@media (prefers-reduced-motion: reduce) {
  .splash-mark .sp-rule, .splash-mark .sp-sign { stroke-dashoffset: 0; }
}

.splash-word { font-family: var(--font-head); font-size: var(--fs-2xl); font-weight: 700; letter-spacing: -0.015em; color: #fff; }
.splash-word em { font-style: normal; color: var(--focus-ring); }

/* slim indeterminate progress rail */
.splash-rail { position: relative; width: 100%; height: 3px; background: rgba(255, 255, 255, .10); border-radius: var(--r-xs); overflow: hidden; }
.splash-rail::after {
  content: ''; position: absolute; inset: 0 auto 0 0; width: 38%;
  background: linear-gradient(90deg, transparent, var(--focus-ring), transparent);
  border-radius: var(--r-xs);
  animation: fq-rail 1600ms var(--ease-inout) infinite;
}
@keyframes fq-rail { from { transform: translateX(-100%); } to { transform: translateX(300%); } }
.splash-rail.is-determinate::after { animation: none; width: var(--pct, 0%); background: var(--focus-ring); transition: width var(--motion-standard); }

.splash-status { font-size: var(--fs-sm); color: var(--chrome-ink-2); min-height: 20px; transition: color var(--motion-standard); }
.splash-status.is-extended { color: var(--st-review-line); }
.splash-extra { opacity: 0; transform: translateY(4px); pointer-events: none;
  transition: opacity var(--motion-standard), transform var(--motion-standard); }
.splash-extra.is-shown { opacity: 1; transform: none; pointer-events: auto; }

/* ══ MAINTENANCE ETA ══════════════════════════════════════════════════════ */
.eta-block {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-4); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-md); text-align: start;
}
.eta-value { font-family: var(--font-mono); font-size: var(--fs-2xl); font-weight: 600; font-variant-numeric: tabular-nums;
  line-height: 1.1; letter-spacing: -0.02em; color: var(--ink); }
.eta-label { font-size: var(--fs-xs); color: var(--ink-3); margin-top: 2px; }
/* auto-retry ring — pure CSS, so it never re-renders the page every second */
.retry-ring { width: 34px; height: 34px; flex: none; position: relative; }
.retry-ring svg { width: 34px; height: 34px; transform: rotate(-90deg); }
.retry-ring circle { fill: none; stroke-width: 3; }
.retry-ring .rr-track { stroke: var(--canvas-sunken); }
.retry-ring .rr-fill { stroke: var(--brand); stroke-linecap: round; stroke-dasharray: 94.2;
  animation: fq-retry 60s linear infinite; }
@keyframes fq-retry { from { stroke-dashoffset: 94.2; } to { stroke-dashoffset: 0; } }
@media (max-width: 599px) { .eta-block { flex-wrap: wrap; } }

/* ── bare shell — used only by the error boundary when the failure happened
   BEFORE the shell existed (startup). Sidebar and topbar are removed rather
   than shown non-functional, because a nav that does nothing is worse than
   no nav at all. ─────────────────────────────────────────────────────────── */
.app.is-bare { grid-template-columns: minmax(0, 1fr); }
.app.is-bare .sidebar, .app.is-bare .topbar { display: none; }
.app.is-bare .page-center { min-height: 100vh; }

/* Wide reference tables scroll rather than crush at mobile/mobile-lg.
   Applies retroactively to every .rule-table in every phase. */
@media (max-width: 767px) {
  .card-body:has(> .rule-table) { overflow-x: auto; }
  .rule-table { min-width: 480px; }
}

/* ── RTL: the shell ────────────────────────────────────────────────────────
   The sidebar moves to the right edge on its own: .app is a CSS grid, and a
   grid's column order follows the inline axis, so `dir="rtl"` reverses it
   without a media query. Its fixed off-canvas position follows too, because the
   offset is now inset-inline-start. Only the slide transform is physical. */
/* the off-canvas slide only exists below tablet-lg — at desktop the sidebar is
   docked and the grid has already placed it on the right, so transforming it
   here would push it off the viewport entirely */
@media (max-width: 1023px) {
  [dir="rtl"] .sidebar { transform: translateX(100%); }
  [dir="rtl"] .app.nav-open .sidebar { transform: none; }
}
/* the rail collapses toward the right edge, so the chevron points the other way */
[dir="rtl"] .topbar-hamburger svg { transform: scaleX(-1); }
/* the loading sweep travels with the reading direction */
[dir="rtl"] .splash-rail::after { animation-name: fq-rail-rtl; }
@keyframes fq-rail-rtl { from { transform: translateX(100%); } to { transform: translateX(-300%); } }
/* the pre-auth guilloché is a security pattern, not text — it does not mirror */
[dir="rtl"] .auth::before, [dir="rtl"] .auth::after { direction: ltr; }
