/* ==========================================================================
   CIBL Obsidian — shared theme layer (portal + staff console)
   Dark glass on a near-black ground, ember-orange accents.
   Tokens first; components below only consume tokens.

   This file is the single source of the token layer and every component
   used by more than one app. App-specific rules stay in the app's own
   stylesheet (apps/portal/static/portal/portal.css,
   apps/console/static/console/console.css) loaded AFTER this one.
   ========================================================================== */

:root {
  /* ground + surfaces */
  --ground: #0B0A09;
  --ground-raise: #121110;
  --glass: rgba(255, 255, 255, 0.035);
  --glass-strong: rgba(255, 255, 255, 0.06);
  --hairline: rgba(255, 255, 255, 0.055);
  --hairline-strong: rgba(255, 255, 255, 0.12);

  /* ink */
  --ink: #F4EFE9;
  --ink-dim: rgba(244, 239, 233, 0.62);
  /* 0.52 alpha: >= 5:1 on --ground / --ground-raise / glass cards (WCAG AA
     for the 10-11px mono eyebrows, table headers and ccy codes it paints —
     small text needs 4.5:1; the old 0.38 measured 3.2:1). */
  --ink-faint: rgba(244, 239, 233, 0.52);

  /* ember accent */
  --ember-hi: #FFA333;
  --ember-lo: #FF6B00;
  --ember-grad: linear-gradient(135deg, var(--ember-hi), var(--ember-lo));
  --ember-soft: rgba(255, 138, 26, 0.14);
  --ember-ring: rgba(255, 138, 26, 0.45);

  /* signals */
  --ok: #55C98A;
  --ok-soft: rgba(85, 201, 138, 0.14);
  --bad: #FF6B6B;
  --bad-soft: rgba(255, 107, 107, 0.12);

  /* type */
  --font-display: "Bricolage Grotesque", "Inter", system-ui, sans-serif;
  --font-body: "Inter", system-ui, -apple-system, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* geometry */
  --radius: 14px;
  --radius-sm: 9px;
  --rail-w: 236px;
  --gap: 20px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
}

body {
  background:
    radial-gradient(1100px 500px at 85% -10%, rgba(255, 107, 0, 0.07), transparent 60%),
    radial-gradient(900px 600px at -10% 110%, rgba(255, 163, 51, 0.05), transparent 55%),
    var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--ember-hi); text-decoration: none; }
a:hover { text-decoration: underline; }

/* a11y: one ember focus ring everywhere */
:focus-visible {
  outline: 2px solid var(--ember-ring);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ---- layout: fixed rail + content ------------------------------------- */

.shell { display: flex; min-height: 100vh; }

.rail {
  width: var(--rail-w);
  flex: 0 0 var(--rail-w);
  border-right: 1px solid var(--hairline);
  background: rgba(255, 255, 255, 0.015);
  padding: 22px 14px;
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.brand { display: flex; align-items: center; gap: 10px; padding: 0 8px; }
.brand-mark {
  width: 30px; height: 30px; border-radius: 8px;
  background: var(--ember-grad);
  box-shadow: 0 0 18px rgba(255, 107, 0, 0.35);
  display: grid; place-items: center;
  color: #14100A; font-family: var(--font-display); font-weight: 800; font-size: 15px;
}
.brand-name {
  font-family: var(--font-display);
  font-weight: 700; font-size: 16px; letter-spacing: 0.01em;
}
.brand-sub {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-faint);
}

.nav { display: flex; flex-direction: column; gap: 4px; }
.nav a {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: var(--radius-sm);
  color: var(--ink-dim); font-size: 14px; font-weight: 500;
  border: 1px solid transparent;
}
.nav a:hover { color: var(--ink); background: var(--glass); text-decoration: none; }
.nav a.active {
  color: var(--ink);
  background: var(--ember-soft);
  border-color: rgba(255, 138, 26, 0.25);
}
.nav a.active .nav-dot { background: var(--ember-grad); }
.nav-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--hairline-strong); flex: 0 0 6px;
}

.rail-foot { margin-top: auto; padding: 0 8px; }
.rail-user {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--ink-faint); margin-bottom: 8px; word-break: break-all;
}
.btn-ghost {
  display: inline-block; width: 100%;
  padding: 8px 12px; border-radius: var(--radius-sm);
  background: transparent; border: 1px solid var(--hairline-strong);
  color: var(--ink-dim); font: inherit; font-size: 13px; cursor: pointer;
  text-align: center;
}
.btn-ghost:hover { color: var(--ink); border-color: var(--ember-ring); }

.main { flex: 1; min-width: 0; padding: 34px 40px 60px; }
.main-inner { max-width: 1040px; margin: 0 auto; }

/* ---- page chrome -------------------------------------------------------- */

.page-head { margin-bottom: 26px; }
.kicker {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ember-hi); margin: 0 0 6px;
}
h1.page-title {
  font-family: var(--font-display);
  font-size: 30px; font-weight: 700; letter-spacing: -0.015em;
  margin: 0;
}
.page-sub { color: var(--ink-dim); margin: 6px 0 0; font-size: 14px; }

/* k-label: mono uppercase eyebrow used above facts/values everywhere. */
.k-label {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-faint);
}

/* ---- glass cards -------------------------------------------------------- */

.card {
  background: var(--glass);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 20px 22px;
  backdrop-filter: blur(10px);
  transition: border-color 180ms ease, transform 180ms ease, box-shadow 180ms ease;
}
/* interactive cards lift 2px with a warming border */
a.card:hover, .card-lift:hover {
  transform: translateY(-2px);
  border-color: rgba(255, 163, 51, 0.28);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35);
}
.card + .card, .section { margin-top: var(--gap); }
.card-title {
  font-family: var(--font-display);
  font-size: 15px; font-weight: 600; margin: 0 0 14px;
}

.grid { display: grid; gap: var(--gap); }
.grid-totals { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

.total-card { position: relative; overflow: hidden; }
.total-card::after {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: var(--ember-grad); opacity: 0.85;
}

/* ---- money -------------------------------------------------------------- */

.money, .mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
.money-lg { font-size: 26px; font-weight: 600; letter-spacing: -0.01em; }
.money-md { font-size: 18px; font-weight: 600; }
.ccy {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em;
  color: var(--ink-faint);
}
.amount-in { color: var(--ok); }
.amount-out { color: var(--ink); }

/* ---- tables ------------------------------------------------------------- */

.table-wrap { overflow-x: auto; }
table.ledger { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.ledger th {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-faint);
  text-align: left; padding: 8px 12px;
  border-bottom: 1px solid var(--hairline-strong);
}
table.ledger td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--hairline);
  vertical-align: top;
}
table.ledger tr:last-child td { border-bottom: none; }
table.ledger td.num, table.ledger th.num { text-align: right; }
table.ledger tbody tr { transition: background-color 120ms ease; }
table.ledger tbody tr:hover { background: rgba(255, 163, 51, 0.035); }
table.ledger tbody tr:hover:has(td.empty-cell) { background: transparent; }
.ref { font-family: var(--font-mono); font-size: 12px; color: var(--ink-dim); }
.deal-ref {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--ember-hi); background: var(--ember-soft);
  border-radius: 5px; padding: 1px 6px;
}

/* ---- state chips --------------------------------------------------------- */

.chip {
  display: inline-block;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 2px 8px; border-radius: 999px;
  background: var(--glass-strong);
  border: 1px solid var(--hairline-strong);
  color: var(--ink-dim);
  white-space: nowrap;
}
.chip-ok { background: var(--ok-soft); border-color: rgba(85, 201, 138, 0.4); color: var(--ok); }
.chip-bad { background: var(--bad-soft); border-color: rgba(255, 107, 107, 0.4); color: var(--bad); }
.chip-ember { background: var(--ember-soft); border-color: rgba(255, 138, 26, 0.4); color: var(--ember-hi); }

/* ---- buttons (actions) --------------------------------------------------- */

.btn-ember {
  width: 100%; padding: 11px 16px; margin-top: 6px;
  border: none; border-radius: var(--radius-sm);
  background: var(--ember-grad); color: #14100A;
  font: inherit; font-weight: 700; cursor: pointer;
  transition: filter 150ms ease, box-shadow 200ms ease, transform 90ms ease;
}
.btn-ember:hover {
  filter: brightness(1.06);
  box-shadow: 0 6px 22px rgba(255, 107, 0, 0.32);
}
.btn-ember:active { transform: translateY(1px); box-shadow: 0 2px 10px rgba(255, 107, 0, 0.25); }

/* compact inline action buttons (console rows, htmx actions) */
.btn {
  display: inline-block;
  padding: 6px 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--hairline-strong);
  background: var(--glass-strong);
  color: var(--ink); font: inherit; font-size: 13px; font-weight: 500;
  cursor: pointer; white-space: nowrap;
}
.btn { transition: border-color 150ms ease, box-shadow 200ms ease, filter 150ms ease, transform 90ms ease; }
.btn:hover { border-color: var(--ember-ring); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn-primary { background: var(--ember-grad); border-color: transparent; color: #14100A; font-weight: 700; }
.btn-primary:hover {
  filter: brightness(1.06);
  box-shadow: 0 5px 18px rgba(255, 107, 0, 0.3);
}
.btn-danger { color: var(--bad); border-color: rgba(255, 107, 107, 0.4); background: var(--bad-soft); }
.btn:disabled, .btn[disabled] {
  opacity: 0.45; cursor: not-allowed; filter: none;
  transform: none; box-shadow: none;
}

/* ---- form fields ---------------------------------------------------------- */

.field { margin-bottom: 16px; }
.field label {
  display: block; margin-bottom: 6px;
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-dim);
}
.field input {
  width: 100%; padding: 10px 12px;
  background: rgba(255, 255, 255, 0.045);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  color: var(--ink); font: inherit;
}
.field input:focus {
  outline: none; border-color: var(--ember-ring);
  box-shadow: 0 0 0 3px rgba(255, 138, 26, 0.15);
}
.form-error {
  background: rgba(255, 107, 107, 0.1);
  border: 1px solid rgba(255, 107, 107, 0.35);
  color: var(--bad);
  border-radius: var(--radius-sm);
  padding: 10px 12px; font-size: 13px; margin-bottom: 16px;
}

/* ---- toasts (Django messages, htmx-refreshed) ----------------------------- */

.toasts {
  position: fixed; right: 20px; bottom: 20px; z-index: 100;
  display: flex; flex-direction: column; gap: 8px;
  max-width: 380px;
}
.toast {
  background: var(--ground-raise);
  border: 1px solid var(--hairline-strong);
  border-left: 3px solid var(--ember-hi);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  font-size: 13px; color: var(--ink);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
}
.toast-error { border-left-color: var(--bad); }
.toast-success { border-left-color: var(--ok); }
.toast-warning { border-left-color: var(--ember-hi); }
@media (prefers-reduced-motion: no-preference) {
  .toast { animation: toast-in 240ms cubic-bezier(0.22, 1, 0.36, 1) both; }
  .toast.toast-hide { animation: toast-out 320ms ease both; }
}
.toast.toast-hide { opacity: 0; }
@keyframes toast-in {
  from { opacity: 0; transform: translateX(18px); }
  to { opacity: 1; transform: none; }
}
@keyframes toast-out {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translateY(8px); }
}

/* ---- meter bars (coverage, match strength) -------------------------------- */

.bar {
  height: 6px; border-radius: 999px; overflow: hidden;
  background: var(--glass-strong);
  border: 1px solid var(--hairline);
}
.bar > span {
  display: block; height: 100%;
  background: var(--ember-grad);
}
.bar-ok > span { background: var(--ok); }
.bar-bad > span { background: var(--bad); }

/* ---- empty states / placeholders ---------------------------------------- */

.empty {
  text-align: center; padding: 56px 20px;
  border: 1px dashed var(--hairline-strong);
  border-radius: var(--radius);
  color: var(--ink-dim);
}
.empty h2 { font-family: var(--font-display); color: var(--ink); margin: 0 0 8px; }

/* faint CSS glyph that heads every designed empty state */
.empty-glyph {
  display: block; width: 40px; height: 40px; margin: 0 auto 14px;
  border: 1px dashed var(--hairline-strong); border-radius: 50%;
  position: relative;
}
.empty-glyph::after {
  content: ""; position: absolute; inset: 11px; border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, rgba(255, 163, 51, 0.35), rgba(255, 107, 0, 0.08));
}
.empty > .empty-glyph { width: 48px; height: 48px; }

/* designed empty state inside a table (single full-width cell) */
table.ledger td.empty-cell {
  text-align: center; padding: 34px 16px 30px;
  color: var(--ink-faint); font-size: 13px;
}
td.empty-cell .empty-glyph { width: 32px; height: 32px; margin-bottom: 10px; }
td.empty-cell .empty-glyph::after { inset: 9px; }
td.empty-cell a { font-weight: 500; }

/* ---- login -------------------------------------------------------------- */

.login-body { display: grid; place-items: center; min-height: 100vh; padding: 24px; }

/* layered background: static gradient ground -> slow ember drift (CSS
   fallback) -> optional muted looping video, all behind the glass card. */
.login-bg { position: fixed; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.login-ember-drift {
  position: absolute; inset: -25%;
  background:
    radial-gradient(700px 480px at 78% 18%, rgba(255, 107, 0, 0.13), transparent 62%),
    radial-gradient(620px 520px at 16% 82%, rgba(255, 163, 51, 0.09), transparent 58%),
    radial-gradient(420px 380px at 55% 55%, rgba(255, 138, 26, 0.05), transparent 65%);
}
.login-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0.55;
}
/* dark veil so the glass card always reads, video or not */
.login-bg::after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(1200px 800px at 50% 45%, transparent, rgba(11, 10, 9, 0.72));
}
@media (prefers-reduced-motion: no-preference) {
  .login-ember-drift { animation: login-drift 28s ease-in-out infinite alternate; }
}
@media (prefers-reduced-motion: reduce) {
  /* reduced motion: the static gradient only */
  .login-video { display: none; }
}
@keyframes login-drift {
  from { transform: translate3d(-3%, -2%, 0) scale(1); }
  50% { transform: translate3d(2%, 3%, 0) scale(1.06); }
  to { transform: translate3d(4%, -3%, 0) scale(1.02); }
}

.login-card {
  position: relative; z-index: 1;
  width: 100%; max-width: 400px;
  background: var(--glass);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 34px 32px 30px;
  backdrop-filter: blur(12px);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
}
.login-card .brand { padding: 0; margin-bottom: 22px; }
.login-title { font-family: var(--font-display); font-size: 22px; margin: 0 0 4px; }
.login-sub { color: var(--ink-dim); font-size: 13px; margin: 0 0 22px; }

/* ---- motion: orchestrated entrance, staggers, live glows ------------------
   One entrance per page load — main-inner children rise in sequence.
   Everything in this layer runs ONLY when the user allows motion.        */

@keyframes rise-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: no-preference) {
  .main-inner > * { animation: rise-in 340ms cubic-bezier(0.22, 1, 0.36, 1) both; }
  .main-inner > *:nth-child(2) { animation-delay: 70ms; }
  .main-inner > *:nth-child(3) { animation-delay: 140ms; }
  .main-inner > *:nth-child(4) { animation-delay: 210ms; }
  .main-inner > *:nth-child(5) { animation-delay: 280ms; }
  .main-inner > *:nth-child(n+6) { animation-delay: 340ms; }

  /* staggered grid cards inside an entering section */
  .grid-stagger > * { animation: rise-in 320ms cubic-bezier(0.22, 1, 0.36, 1) both; }
  .grid-stagger > *:nth-child(2) { animation-delay: 60ms; }
  .grid-stagger > *:nth-child(3) { animation-delay: 120ms; }
  .grid-stagger > *:nth-child(n+4) { animation-delay: 180ms; }

  /* staggered table rows (deals list, accounts) */
  .rows-stagger tbody tr { animation: rise-in 280ms ease-out both; }
  .rows-stagger tbody tr:nth-child(1) { animation-delay: 40ms; }
  .rows-stagger tbody tr:nth-child(2) { animation-delay: 80ms; }
  .rows-stagger tbody tr:nth-child(3) { animation-delay: 120ms; }
  .rows-stagger tbody tr:nth-child(4) { animation-delay: 160ms; }
  .rows-stagger tbody tr:nth-child(5) { animation-delay: 200ms; }
  .rows-stagger tbody tr:nth-child(6) { animation-delay: 240ms; }
  .rows-stagger tbody tr:nth-child(n+7) { animation-delay: 280ms; }

  /* healthy live derived figures breathe a quiet green */
  .pulse-ok { animation: breathe-ok 3.4s ease-in-out infinite; }
  /* value held live in escrow breathes ember */
  .pulse-ember { animation: breathe-ember 3.4s ease-in-out infinite; }

  /* htmx swap: outgoing content fades, incoming settles in */
  .htmx-swapping { opacity: 0; transform: translateY(4px); }
  .htmx-added { animation: rise-in 240ms ease-out both; }
}
.htmx-swapping { transition: opacity 120ms ease, transform 120ms ease; }

@keyframes breathe-ok {
  0%, 100% { text-shadow: 0 0 10px rgba(85, 201, 138, 0.12); }
  50% { text-shadow: 0 0 22px rgba(85, 201, 138, 0.4); }
}
@keyframes breathe-ember {
  0%, 100% { text-shadow: 0 0 10px rgba(255, 138, 26, 0.12); }
  50% { text-shadow: 0 0 22px rgba(255, 138, 26, 0.38); }
}

/* quiet settled seal (deal that has reached RELEASED / SETTLED) */
.seal-settled {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono); font-size: 10px; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ok);
  border: 1px solid rgba(85, 201, 138, 0.35);
  background: var(--ok-soft);
  border-radius: 999px; padding: 3px 10px;
}
.seal-settled::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--ok); box-shadow: 0 0 8px rgba(85, 201, 138, 0.6);
}

/* ---- responsive --------------------------------------------------------- */
/* Contract: 1280 is the reference composition; >=1600 the same composition
   scales up centered; ~1080 tightens gutters; <=860 the rail collapses to a
   top bar; <=780 single column; <=480 thumb-sized targets. Tables always
   scroll inside .table-wrap — the page itself never scrolls horizontally. */

@media (min-width: 1600px) {
  .main-inner { max-width: 1440px; }
}

@media (max-width: 1080px) {
  .main { padding: 28px 26px 54px; }
  :root { --gap: 16px; }
}

@media (max-width: 860px) {
  .shell { flex-direction: column; }
  .rail {
    width: auto; flex: none;
    flex-direction: row; align-items: center;
    border-right: none; border-bottom: 1px solid var(--hairline);
    padding: 12px 16px; gap: 14px;
  }
  .nav { flex-direction: row; flex-wrap: wrap; }
  .rail-foot { margin-top: 0; margin-left: auto; }
  .rail-user { display: none; }
  .main { padding: 22px 16px 48px; }
}

@media (max-width: 780px) {
  .brand-sub { display: none; }
  .nav a { padding: 7px 10px; font-size: 13px; }
  .grid-totals, .grid { grid-template-columns: 1fr; }
  h1.page-title { font-size: 24px; }
  .money-lg { font-size: 22px; }
  .card { padding: 16px 16px; }
  .toasts { left: 16px; right: 16px; max-width: none; }
}

@media (max-width: 480px) {
  .main { padding: 18px 12px 44px; }
  .rail { padding: 10px 12px; gap: 10px; }
  /* touch targets: at least 44px tall on every tappable control */
  .nav a { min-height: 44px; padding: 10px 12px; align-items: center; }
  .btn { padding: 11px 14px; }
  .btn-ghost { padding: 12px; }
  .btn-ember { padding: 13px 16px; }
  .field input { padding: 12px; }
  h1.page-title { font-size: 21px; }
  .money-lg { font-size: 20px; }
  .card { padding: 14px 14px; }
  table.ledger { font-size: 12.5px; }
  table.ledger th, table.ledger td { padding: 8px 8px; }
  .login-card { padding: 26px 20px 24px; }
}

/* ---- reduced motion: keep the shared layer perfectly still ---------------- */

@media (prefers-reduced-motion: reduce) {
  .card, a.card:hover, .card-lift:hover,
  .btn, .btn:active:not(:disabled), .btn-ember, .btn-ember:active {
    transition: none; transform: none;
  }
  .toast, .main-inner > *, .grid-stagger > *, .rows-stagger tbody tr,
  .pulse-ok, .pulse-ember, .login-ember-drift, .htmx-added {
    animation: none !important;
  }
  .htmx-swapping { opacity: 1; transform: none; transition: none; }
}

/* Real CIBL wordmark (static/obsidian/brand/cibl-logo.png) replaces the
   stand-in "C" tile: no box, no glow — the orange serif mark carries itself. */
.brand-mark.brand-logo {
  width: auto; height: 26px; border-radius: 0;
  background: none; box-shadow: none; display: block; object-fit: contain;
}
.login-card .brand-mark.brand-logo,
.otp-card .brand-mark.brand-logo { height: 34px; }

/* ==========================================================================
   APP PACK — notification bell, notification lists, channel preferences,
   the install prompt, the mobile tab bar and htmx skeletons.

   Shared layer on purpose: the bell and the notification list render
   identically in the client portal and the staff console, and the mobile
   shell tokens (safe-area insets, 44px targets) belong with the rest of
   the responsive system rather than in one app's stylesheet.
   ========================================================================== */

/* Alpine cloaks elements until it has initialised; without this the panel
   flashes open on every page load. */
[x-cloak] { display: none !important; }

/* The browser's own `[hidden] { display: none }` is a UA rule, so ANY
   author rule that sets `display` on the same element beats it — the
   install prompt (display:flex) and the iOS step list (display:grid)
   rendered in full while the DOM said hidden.  Restate it as an author
   rule so `el.hidden = true` means hidden, everywhere. */
[hidden] { display: none !important; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.inline-form { display: inline; margin: 0; }
.btn-tiny { font-size: 11.5px; padding: 5px 9px; }

/* ---- sticky rail ----------------------------------------------------------
   The rail's foot (bell, user, sign out) used to sit at the bottom of the
   DOCUMENT, so on a long console page the notification bell was a thousand
   pixels below the fold — useless.  On the two-column layout the rail now
   pins to the viewport, which is also what makes the app feel like an app
   rather than a scrolling web page.  Below 860px the rail is already a top
   bar and needs none of this. */

@media (min-width: 861px) {
  .rail {
    position: sticky; top: 0;
    height: 100vh; align-self: flex-start;
    /* `position: sticky` makes the rail its own stacking context, which
       traps the dropdown's z-index inside it — without a z-index here the
       panel paints UNDER every positioned card in the main column. */
    z-index: 50;
  }
  /* The NAV scrolls, not the rail: an `overflow` on the rail itself would
     clip the bell's dropdown to a 236px column. */
  .rail .nav { overflow-y: auto; min-height: 0; overscroll-behavior: contain; }
}

/* ---- bell ---------------------------------------------------------------- */

.bell-wrap { position: relative; }
.bell-slot { display: block; }
/* Room for the count badge, which pokes 5px above the button. */
.rail-foot .bell-wrap { margin-bottom: 12px; padding-top: 5px; }
.bell-btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; padding: 0;
  background: var(--glass); color: var(--ink-dim);
  border: 1px solid var(--hairline); border-radius: 11px;
  cursor: pointer;
  transition: color 150ms ease, border-color 150ms ease, background 150ms ease;
}
.bell-btn:hover, .bell-btn:focus-visible { color: var(--ink); border-color: var(--ember-ring); }
.bell-glyph { width: 20px; height: 20px; display: block; }
.bell-btn-unread { color: var(--ember-hi); border-color: rgba(255, 138, 26, 0.35); }
.bell-count {
  position: absolute; top: -5px; right: -5px;
  min-width: 18px; height: 18px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ember-grad); color: #14100A;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  border-radius: 9px; border: 2px solid var(--ground);
}
@media (prefers-reduced-motion: no-preference) {
  .bell-btn-unread .bell-glyph { animation: bell-nudge 5s ease-in-out infinite; }
  @keyframes bell-nudge {
    0%, 88%, 100% { transform: rotate(0); }
    91% { transform: rotate(-9deg); }
    94% { transform: rotate(7deg); }
    97% { transform: rotate(-3deg); }
  }
}

.bell-panel {
  position: absolute; z-index: 90;
  bottom: calc(100% + 10px); left: 0;
  width: 330px; max-height: 60vh; overflow-y: auto;
  background: var(--ground-raise);
  border: 1px solid var(--hairline-strong); border-radius: var(--radius);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.bell-panel:empty { display: none; }
.bell-panel-head, .bell-panel-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 14px;
}
.bell-panel-head { border-bottom: 1px solid var(--hairline); }
.bell-panel-foot { border-top: 1px solid var(--hairline); }
.notif-all-link { font-size: 12.5px; color: var(--ember-hi); }

/* ---- notification list --------------------------------------------------- */

.notif-list { list-style: none; margin: 0; padding: 0; }
.notif-item {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; border-bottom: 1px solid var(--hairline);
}
.notif-item:last-child { border-bottom: none; }
.notif-link { display: flex; gap: 10px; width: 100%; color: var(--ink); text-decoration: none; }
.notif-link:hover { text-decoration: none; }
.notif-dot {
  flex: 0 0 auto; width: 7px; height: 7px; margin-top: 6px;
  border-radius: 50%; background: var(--hairline-strong);
}
.notif-unread .notif-dot { background: var(--ember-grad); box-shadow: 0 0 8px rgba(255, 138, 26, 0.45); }
.notif-alert .notif-dot { background: var(--bad); box-shadow: none; }
.notif-action.notif-unread .notif-dot { background: var(--ember-grad); }
.notif-copy { display: grid; gap: 3px; min-width: 0; flex: 1 1 auto; }
.notif-title {
  font-size: 13.5px; font-weight: 500; color: var(--ink-dim);
  text-decoration: none; overflow-wrap: anywhere;
}
a.notif-title:hover { color: var(--ember-hi); text-decoration: none; }
.notif-unread .notif-title { color: var(--ink); font-weight: 600; }
.notif-body { font-size: 12.5px; color: var(--ink-dim); overflow-wrap: anywhere; }
.notif-meta {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline;
  font-family: var(--font-mono); font-size: 11px; color: var(--ink-faint);
}
.notif-actions { flex: 0 0 auto; }
.notif-list-wrap { display: block; }
.notif-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 12px;
}
.notif-filters { display: flex; gap: 8px; }
.notif-filters .chip { cursor: pointer; text-decoration: none; }
.notif-filters .chip:hover { text-decoration: none; border-color: var(--ember-ring); }
.notif-list-compact .notif-item { padding: 10px 14px; }

/* ---- channel preferences -------------------------------------------------- */

.prefs { display: grid; gap: 0; }
.pref-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 14px 0; border-bottom: 1px solid var(--hairline);
}
.pref-row:last-of-type { border-bottom: none; }
.pref-row-sub { padding-left: 16px; border-left: 2px solid var(--hairline); }
.pref-copy { display: grid; gap: 3px; min-width: 0; }
.pref-label { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.pref-note { font-size: 12.5px; color: var(--ink-dim); }
.pref-control { flex: 0 0 auto; text-align: right; }
.pref-ios { font-size: 12px; color: var(--ink-dim); margin: 8px 0 0; max-width: 34ch; }

/* ---- install prompt ------------------------------------------------------- */

.install-prompt {
  position: fixed; z-index: 95;
  left: 20px; right: auto; bottom: 20px; width: min(360px, calc(100vw - 40px));
  display: flex; flex-direction: column; gap: 12px;
  padding: 16px 16px 14px;
  padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  background: var(--ground-raise);
  border: 1px solid var(--hairline-strong);
  border-left: 3px solid var(--ember-hi);
  border-radius: var(--radius);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.6);
}
.install-title { font-family: var(--font-display); font-size: 15px; margin: 0 0 4px; color: var(--ink); }
.install-body { font-size: 12.5px; color: var(--ink-dim); margin: 0; }
.install-ios { font-size: 12.5px; color: var(--ink-dim); margin: 8px 0 0; padding-left: 18px; display: grid; gap: 4px; }
.install-actions { display: flex; gap: 8px; align-items: center; }
@media (prefers-reduced-motion: no-preference) {
  .install-prompt.install-in { animation: toast-in 260ms cubic-bezier(0.22, 1, 0.36, 1) both; }
}

/* ---- offline page --------------------------------------------------------- */

.offline-card { text-align: left; }
.offline-list {
  list-style: none; margin: 0 0 20px; padding: 0;
  display: grid; gap: 8px; font-size: 13px; color: var(--ink-dim);
}
.offline-list li { padding-left: 16px; position: relative; }
.offline-list li::before {
  content: ""; position: absolute; left: 0; top: 7px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--ember-hi);
}
.offline-retry { width: 100%; }

/* ---- htmx skeletons ------------------------------------------------------
   Any element htmx is currently loading into gets a shimmer instead of a
   dead gap.  Purely additive: no existing template had to be touched. */

.skeleton {
  background: linear-gradient(90deg, var(--glass) 25%, var(--glass-strong) 37%, var(--glass) 63%);
  background-size: 400% 100%;
  border-radius: var(--radius-sm);
}
.skeleton-line { height: 12px; margin: 8px 0; }
[hx-get].htmx-request, [hx-post].htmx-request,
.htmx-request > .skeleton { position: relative; }
@media (prefers-reduced-motion: no-preference) {
  .skeleton { animation: skeleton-sheen 1.4s ease-in-out infinite; }
  @keyframes skeleton-sheen {
    0% { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
  }
  .htmx-request .notif-item, .htmx-request.card { opacity: 0.55; transition: opacity 140ms ease; }
}

/* ---- mobile app shell: bottom tab bar ------------------------------------
   Below 780px the portal stops being a website with a sidebar and starts
   being an app: primary destinations move to a thumb-reachable bar pinned
   to the bottom, padded for the home indicator. */

.tabbar { display: none; }
.tabbar-sheet { display: none; }

@media (max-width: 860px) {
  /* Top-bar layout: the bell hangs on the right of the row, so its panel
     drops DOWN from the bar instead of rising off the top of the screen.
     This block MUST precede the 780px one below, which switches the panel
     to fixed positioning — a `top: calc(100% + 10px)` left in place after
     that switch would resolve against the viewport and land off-screen. */
  .bell-panel { top: calc(100% + 10px); bottom: auto; right: 0; left: auto; }
  .rail-foot { display: flex; align-items: center; gap: 10px; }
}

@media (max-width: 780px) {
  .tabbar {
    position: fixed; z-index: 80; left: 0; right: 0; bottom: 0;
    display: flex; align-items: stretch;
    background: rgba(11, 10, 9, 0.94);
    border-top: 1px solid var(--hairline);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    backdrop-filter: blur(14px);
  }
  .tabbar-item {
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; padding: 8px 4px 7px; min-height: 52px;
    color: var(--ink-faint); font-size: 10.5px; letter-spacing: 0.02em;
    background: none; border: 0; cursor: pointer; text-decoration: none;
  }
  .tabbar-item:hover { text-decoration: none; color: var(--ink-dim); }
  .tabbar-item.active { color: var(--ember-hi); }
  .tabbar-glyph { width: 21px; height: 21px; display: block; }
  .tabbar-label { display: block; }
  .tabbar-badge {
    position: absolute; transform: translate(12px, -6px);
    min-width: 8px; height: 8px; border-radius: 50%; background: var(--ember-grad);
  }
  .tabbar-sheet {
    position: fixed; z-index: 81; left: 0; right: 0;
    bottom: calc(52px + env(safe-area-inset-bottom, 0px));
    display: grid; gap: 2px; padding: 10px 12px 14px;
    background: var(--ground-raise);
    border-top: 1px solid var(--hairline-strong);
    box-shadow: 0 -18px 44px rgba(0, 0, 0, 0.6);
  }
  .tabbar-sheet a, .tabbar-sheet button {
    display: block; width: 100%; text-align: left;
    padding: 13px 12px; min-height: 44px;
    color: var(--ink-dim); font-size: 14px;
    background: none; border: 0; border-radius: var(--radius-sm); cursor: pointer;
  }
  .tabbar-sheet a:hover, .tabbar-sheet button:hover {
    background: var(--glass); color: var(--ink); text-decoration: none;
  }
  /* The bar floats over the page; give the content room to scroll clear. */
  body.has-tabbar .main { padding-bottom: calc(78px + env(safe-area-inset-bottom, 0px)); }
  body.has-tabbar .toasts { bottom: calc(70px + env(safe-area-inset-bottom, 0px)); }
  body.has-tabbar .install-prompt { bottom: calc(70px + env(safe-area-inset-bottom, 0px)); }
  /* The sidebar nav is redundant once the tab bar exists. */
  body.has-tabbar .rail .nav { display: none; }

  /* Sticky primary action: on a phone the confirm button must stay under
     the thumb instead of scrolling away up the page.  `.sticky-action` is
     the opt-in class; `.actions-card` (the portal's deal action panel) gets
     it for free because that is the one place where a mis-tap costs money
     and a scroll-to-find costs confidence.  Sticky, not fixed: the panel
     pins only while its own section is on screen. */
  .sticky-action, .actions-card {
    position: sticky; z-index: 40;
    bottom: calc(60px + env(safe-area-inset-bottom, 0px));
  }

  /* Phone: the dropdown becomes a full-width sheet under the top bar
     rather than a 330px card hanging off one corner. */
  .bell-panel {
    position: fixed; left: 12px; right: 12px; width: auto;
    top: 64px; bottom: auto; max-height: 70vh;
  }
  .install-prompt { left: 12px; right: 12px; width: auto; }
}

@media (max-width: 480px) {
  .bell-btn { width: 44px; height: 44px; }
  .notif-item { padding: 12px 12px; }
  .pref-row { flex-direction: column; align-items: flex-start; gap: 10px; }
  .pref-control { text-align: left; width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .bell-btn-unread .bell-glyph, .skeleton, .install-prompt.install-in {
    animation: none !important;
  }
  .htmx-request .notif-item, .htmx-request.card { opacity: 1; transition: none; }
}
