/* =============================================================
 * OxyLo Hub — design tokens + light component layer
 * =============================================================
 * No build step. Plain CSS, loaded after Bootstrap so we can
 * override Bootstrap variables via :root and target Bootstrap
 * classes directly when needed.
 *
 * Authoring rules:
 *   - No raw hex inside component blocks; always reference a var.
 *   - No raw px values for spacing; use --ox-space-*.
 *   - One type scale only: --ox-fs-*. Don't invent new sizes per
 *     component. Don't use Bootstrap's .fs-1..6 — they bypass the
 *     scale.
 *   - All motion goes through --ox-motion-*. Anything that animates
 *     must also respect prefers-reduced-motion via the block at
 *     the bottom of this file.
 * ============================================================= */


/* -------------------------------------------------------------
 * 1. Tokens
 * ------------------------------------------------------------- */
:root {
  /* Brand */
  /* Kept in step with theme-v2 (Tom 2026-08-26). v2 overrides all
   * of these, so what they really are is the fallback if v2 fails
   * to load — which is a reason to keep them current, not stale. */
  --ox-brand:          #2D2B2B;
  --ox-brand-hover:    #201E1D;
  --ox-brand-pressed:  #141312;
  --ox-brand-tint:     #F0EEED;       /* subtle background wash */
  --ox-brand-tint-2:   #D2CDCA;       /* nav active background */

  /* Ink (text) */
  --ox-ink:            #201E1D;       /* body — ~14.9:1 on surface */
  --ox-ink-muted:      #6A6563;       /* captions  — ~5.8:1 */
  --ox-ink-faint:      #918B89;       /* disabled / placeholder */
  --ox-ink-inverse:    #FFFFFF;       /* on brand / on dark surfaces */

  /* Surfaces */
  --ox-surface:        #F5F4F4;       /* app background */
  --ox-surface-raised: #FFFFFF;       /* cards, panels */
  --ox-surface-sunken: #EDEBEA;       /* nested / muted regions */
  --ox-surface-dark:   #1E1B1A;       /* sidebar header, etc. */

  /* Hairlines (replace borders) */
  --ox-hairline:       #E6E3E1;
  --ox-hairline-strong:#CFCAC7;

  /* Accent — used for focus rings, secondary highlights.
   * 4.72:1 against the surface so the focus outline clears WCAG 1.4.11
   * non-text contrast (3:1 min). Same hex as --ox-warning by design —
   * they serve different semantic roles but share the same color
   * because the system only needs one warm accent. */
  --ox-accent:         #B45309;
  --ox-accent-tint:    #FEF3C7;

  /* Status */
  --ox-success:        #0F766E;
  --ox-success-tint:   #D1FAE5;
  --ox-warning:        #B45309;
  --ox-warning-tint:   #FEF3C7;
  --ox-danger:         #B91C1C;
  --ox-danger-tint:    #FEE2E2;
  --ox-info:           #1D4ED8;
  --ox-info-tint:      #DBEAFE;

  /* Type — single scale */
  --ox-font-sans:      system-ui, -apple-system, "Segoe UI", Roboto,
                       "Helvetica Neue", Arial, sans-serif;
  --ox-font-mono:      ui-monospace, "SF Mono", Menlo, Consolas,
                       "Roboto Mono", monospace;
  --ox-fs-xs:          0.75rem;   /* 12px — labels, captions, mono IDs */
  --ox-fs-sm:          0.875rem;  /* 14px — BASE BODY */
  --ox-fs-md:          1rem;      /* 16px — lead paragraph, h5 */
  --ox-fs-lg:          1.25rem;   /* 20px — h4 */
  --ox-fs-xl:          1.5rem;    /* 24px — h3 */
  --ox-fs-xxl:         2rem;      /* 32px — h2 / page title */
  --ox-lh-tight:       1.25;
  --ox-lh-body:        1.5;

  /* Spacing — 4-pt scale */
  --ox-space-1:        0.25rem;   /*  4px */
  --ox-space-2:        0.5rem;    /*  8px */
  --ox-space-3:        0.75rem;   /* 12px */
  --ox-space-4:        1rem;      /* 16px */
  --ox-space-5:        1.5rem;    /* 24px */
  --ox-space-6:        2rem;      /* 32px */
  --ox-space-7:        3rem;      /* 48px */
  --ox-space-8:        4rem;      /* 64px */

  /* Radius */
  --ox-radius-sm:      4px;
  --ox-radius-md:      6px;
  --ox-radius-lg:      10px;
  --ox-radius-pill:    9999px;

  /* Shadow — used instead of borders for containment */
  --ox-shadow-xs:      0 1px 1px rgba(20, 24, 29, 0.04);
  --ox-shadow-sm:      0 1px 2px rgba(20, 24, 29, 0.06),
                       0 1px 1px rgba(20, 24, 29, 0.04);
  --ox-shadow-md:      0 4px 12px rgba(20, 24, 29, 0.08),
                       0 1px 2px rgba(20, 24, 29, 0.04);
  --ox-shadow-lg:      0 16px 32px rgba(20, 24, 29, 0.12),
                       0 2px 6px rgba(20, 24, 29, 0.06);

  /* Motion */
  --ox-motion-fast:    120ms;
  --ox-motion-base:    200ms;
  --ox-motion-slow:    320ms;
  --ox-ease:           cubic-bezier(0.2, 0.6, 0.2, 1);   /* ease-out-ish */

  /* Layout */
  --ox-sidebar-w:      220px;
  --ox-sidebar-w-collapsed: 56px;
  --ox-topbar-h:       48px;
}


/* -------------------------------------------------------------
 * 2. Bootstrap variable bridge
 * ------------------------------------------------------------- */
/* Map Bootstrap's --bs-* tokens onto our --ox-* tokens so default
 * Bootstrap components inherit the new palette without having to
 * rewrite every utility class. */
:root {
  --bs-primary:        var(--ox-brand);
  --bs-primary-rgb:    0, 68, 170;
  --bs-link-color:     var(--ox-brand);
  --bs-link-color-rgb: 0, 68, 170;
  --bs-link-hover-color: var(--ox-brand-hover);
  --bs-body-color:     var(--ox-ink);
  --bs-body-color-rgb: 26, 32, 36;
  --bs-body-bg:        var(--ox-surface);
  --bs-border-color:   var(--ox-hairline);
  --bs-secondary-color: var(--ox-ink-muted);
  --bs-tertiary-bg:    var(--ox-surface-sunken);
  --bs-success:        var(--ox-success);
  --bs-success-rgb:    15, 118, 110;
  --bs-warning:        var(--ox-warning);
  --bs-danger:         var(--ox-danger);
  --bs-info:           var(--ox-info);
  --bs-font-sans-serif: var(--ox-font-sans);
  --bs-font-monospace:  var(--ox-font-mono);
  --bs-body-font-size: var(--ox-fs-sm);
  --bs-body-line-height: var(--ox-lh-body);
}


/* -------------------------------------------------------------
 * 3. Typography
 * ------------------------------------------------------------- */
html {
  font-size: 100%;        /* keep 1rem = 16px so rem math stays stable */
}

body {
  font-family: var(--ox-font-sans);
  font-size: var(--ox-fs-sm);
  line-height: var(--ox-lh-body);
  color: var(--ox-ink);
  background-color: var(--ox-surface);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Heading scale — single source of truth */
h1, .h1 { font-size: var(--ox-fs-xxl); line-height: var(--ox-lh-tight); font-weight: 600; letter-spacing: -0.01em; }
h2, .h2 { font-size: var(--ox-fs-xl);  line-height: var(--ox-lh-tight); font-weight: 600; letter-spacing: -0.005em; }
h3, .h3 { font-size: var(--ox-fs-lg);  line-height: var(--ox-lh-tight); font-weight: 600; }
h4, .h4 { font-size: var(--ox-fs-md);  line-height: var(--ox-lh-tight); font-weight: 600; }
h5, .h5 { font-size: var(--ox-fs-sm);  line-height: var(--ox-lh-tight); font-weight: 600; }
h6, .h6 { font-size: var(--ox-fs-xs);  line-height: var(--ox-lh-tight); font-weight: 600;
          text-transform: uppercase; letter-spacing: 0.05em; color: var(--ox-ink-muted); }

/* Page title — used as a stable class for top-of-page H1s */
.ox-page-title {
  font-size: var(--ox-fs-xl);
  line-height: var(--ox-lh-tight);
  font-weight: 600;
  letter-spacing: -0.005em;
  margin: 0 0 var(--ox-space-4);
  color: var(--ox-ink);
}
.ox-page-subtitle {
  font-size: var(--ox-fs-sm);
  color: var(--ox-ink-muted);
  margin: calc(var(--ox-space-1) * -1) 0 var(--ox-space-5);
}

/* Section label — small uppercase eyebrow above grouped content */
.ox-eyebrow {
  font-size: var(--ox-fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ox-ink-muted);
}

/* Captions / metadata */
.text-muted,
.form-text,
small,
.small {
  font-size: var(--ox-fs-xs);
  color: var(--ox-ink-muted) !important;
}

/* Monospace IDs / codes / hashes */
code, kbd, samp, .ox-mono {
  font-family: var(--ox-font-mono);
  font-size: 0.85em;
}
.ox-id {                                       /* batch IDs, vial codes, doc IDs */
  font-family: var(--ox-font-mono);
  font-size: var(--ox-fs-md);                  /* 16px — large enough to scan in a list */
  letter-spacing: 0.01em;
  color: var(--ox-ink);
  font-weight: 600;
}
.ox-id-link {                                  /* same as .ox-id but for <a> wrappers */
  font-family: var(--ox-font-mono);
  font-size: var(--ox-fs-md);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ox-brand);
  text-decoration: none;
}
.ox-id-link:hover { color: var(--ox-brand-hover); text-decoration: underline; }

/* Links */
a {
  color: var(--ox-brand);
  text-decoration: none;
  transition: color var(--ox-motion-fast) var(--ox-ease);
}
a:hover {
  color: var(--ox-brand-hover);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Don't let Bootstrap's .fs-* utilities punch holes in the scale */
.fs-1 { font-size: var(--ox-fs-xxl) !important; }
.fs-2 { font-size: var(--ox-fs-xl) !important; }
.fs-3 { font-size: var(--ox-fs-lg) !important; }
.fs-4 { font-size: var(--ox-fs-md) !important; }
.fs-5 { font-size: var(--ox-fs-sm) !important; }
.fs-6 { font-size: var(--ox-fs-xs) !important; }

/* Tables — inherit the body scale, no per-table overrides */
.table {
  font-size: var(--ox-fs-sm);
  color: var(--ox-ink);
}
.table > :not(caption) > * > * {
  padding: var(--ox-space-3) var(--ox-space-4);
}
.table thead th {
  font-size: var(--ox-fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ox-ink-muted);
  background: transparent;
  border-bottom: 1px solid var(--ox-hairline-strong);
}

/* Forms — uniform sizing tied to the scale */
.form-label {
  font-size: var(--ox-fs-xs);
  font-weight: 600;
  color: var(--ox-ink);
  margin-bottom: var(--ox-space-1);
}
.form-control,
.form-select {
  font-size: var(--ox-fs-sm);
  line-height: var(--ox-lh-body);
}
.input-group-text {
  font-size: var(--ox-fs-sm);
}

/* Buttons — keep Bootstrap padding, just tame the type */
.btn {
  font-size: var(--ox-fs-sm);
  font-weight: 500;
}
.btn-sm {
  font-size: var(--ox-fs-xs);
}


/* -------------------------------------------------------------
 * 4. Layout shell — top bar + sidebar + main
 * ------------------------------------------------------------- */

/* App shell uses fixed-position chrome around a scrolling main. */
body { padding-top: var(--ox-topbar-h); }

/* ── Top bar ───────────────────────────────────────────────── */
.ox-topbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: var(--ox-topbar-h);
  z-index: 1030;
  display: flex;
  align-items: center;
  gap: var(--ox-space-3);
  padding: 0 var(--ox-space-4);
  background: var(--ox-surface-raised);
  border-bottom: 1px solid var(--ox-hairline);
  box-shadow: var(--ox-shadow-xs);
}
.ox-topbar-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  border-radius: var(--ox-radius-md);
  background: transparent;
  border: 0;
  color: var(--ox-ink-muted);
  font-size: var(--ox-fs-xl);
  cursor: pointer;
  transition: background-color var(--ox-motion-fast) var(--ox-ease),
              color var(--ox-motion-fast) var(--ox-ease);
}
.ox-topbar-toggle:hover {
  background: var(--ox-surface-sunken);
  color: var(--ox-ink);
}
.ox-topbar-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--ox-space-2);
  color: var(--ox-ink);
  font-weight: 600;
  font-size: var(--ox-fs-sm);
  letter-spacing: -0.005em;
  text-decoration: none;
}
.ox-topbar-brand:hover { color: var(--ox-brand); text-decoration: none; }
.ox-topbar-brand img { display: block; }
.ox-topbar-search {
  flex: 1;
  max-width: 420px;
  margin-left: var(--ox-space-4);
}
.ox-topbar-search .form-control {
  background: var(--ox-surface-sunken);
  border-color: transparent;
  font-size: var(--ox-fs-sm);
}
.ox-topbar-search .form-control:focus {
  background: var(--ox-surface-raised);
  border-color: var(--ox-brand);
}
.ox-topbar-spacer { flex: 1; }
.ox-topbar-user .btn {
  display: inline-flex;
  align-items: center;
  gap: var(--ox-space-2);
  background: transparent;
  color: var(--ox-ink);
  border: 0;
  padding: var(--ox-space-1) var(--ox-space-3);
  border-radius: var(--ox-radius-md);
  font-weight: 500;
}
.ox-topbar-user .btn:hover,
.ox-topbar-user .btn[aria-expanded="true"] {
  background: var(--ox-surface-sunken);
}
.ox-topbar-user-avatar {
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--ox-brand);
  color: var(--ox-ink-inverse);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--ox-fs-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* ── Sidebar (desktop / tablet ≥992px) ─────────────────────── */
.ox-sidebar {
  position: fixed;
  top: var(--ox-topbar-h);
  left: 0;
  bottom: 0;
  width: var(--ox-sidebar-w);
  background: var(--ox-surface-sunken);   /* grey tone for contrast */
  border-right: 1px solid var(--ox-hairline);
  z-index: 1020;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: width var(--ox-motion-base) var(--ox-ease);
}
body.ox-sidebar-collapsed .ox-sidebar {
  width: var(--ox-sidebar-w-collapsed);
}

/* In-sidebar collapse / expand toggle (chevron) — sits in the
 * upper-right corner of the sidebar so it's anchored to the surface
 * it controls. Replaces the old top-bar hamburger on desktop. */
.ox-sidebar-toggle {
  position: absolute;
  top: var(--ox-space-2);
  right: var(--ox-space-2);
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Bulletproof reset — defeats UA defaults that draw a 2px outset border. */
  border: 0 !important;
  outline: 0;
  box-shadow: none;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  color: var(--ox-ink-muted);
  border-radius: var(--ox-radius-sm);
  cursor: pointer;
  z-index: 1;
  transition: background-color var(--ox-motion-fast) var(--ox-ease),
              color var(--ox-motion-fast) var(--ox-ease),
              right var(--ox-motion-base) var(--ox-ease),
              left var(--ox-motion-base) var(--ox-ease);
}
.ox-sidebar-toggle:hover {
  background: var(--ox-surface-raised);
  color: var(--ox-ink);
}
/* When collapsed: center the toggle in the icon rail. */
body.ox-sidebar-collapsed .ox-sidebar-toggle {
  right: auto;
  left: 50%;
  transform: translateX(-50%);
}
/* Chevron icon flips direction based on state (set via inline icon
 * swap in the JS so screen readers also get the updated label). */
.ox-sidebar-toggle i { font-size: var(--ox-fs-md); line-height: 1; }

.ox-sidebar-nav {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: calc(var(--ox-space-2) + 36px) var(--ox-space-2) var(--ox-space-3);
  display: flex;
  flex-direction: column;
  gap: 2px;             /* slightly tighter — sleeker, less padded */
  /* Hide scrollbar visually while preserving wheel scroll. Firefox first,
   * then WebKit/Blink. */
  scrollbar-width: none;
}
.ox-sidebar-nav::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* Nothing in the rail shrinks. The rail scrolls instead.
 *
 * .ox-sidebar-nav is a flex column, so every child defaults to
 * flex-shrink:1 and gets compressed when the content is taller than the
 * rail — the overflow-y:auto above never gets a chance to engage,
 * because flex absorbs the excess by squashing rows first.
 *
 * It disfigures the group headings specifically, and only them. A flex
 * item's automatic minimum size is normally its content, which is what
 * stops .ox-nav-link being squashed — but for an item whose overflow is
 * not `visible` that floor drops to zero, and .ox-nav-section is
 * overflow:hidden so it can be compressed to nothing and clip its own
 * text. Measured at a 700px viewport: "RECORDS" wanted 33px and was
 * rendered at 21.7, with the bottom of the letters cut off and the row
 * beneath crowding into it (Tom 2026-08-27).
 *
 * Not a zoom bug, though zooming is how you notice it: it happens at any
 * size where the rail overflows, and the outreach sub-nav's nine extra
 * rows are usually what tips it over. */
.ox-sidebar-nav > * { flex: none; }
.ox-nav-section {
  font-size: var(--ox-fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ox-ink-faint);
  font-weight: 600;
  padding: var(--ox-space-4) var(--ox-space-3) var(--ox-space-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: opacity var(--ox-motion-fast) var(--ox-ease);
}
body.ox-sidebar-collapsed .ox-sidebar .ox-nav-section {
  opacity: 0;
  padding-left: 0;
  padding-right: 0;
}
.ox-nav-link {
  display: flex;
  align-items: center;
  gap: var(--ox-space-3);
  padding: 6px var(--ox-space-3);
  color: var(--ox-ink);
  border-radius: var(--ox-radius-md);
  text-decoration: none;
  font-size: var(--ox-fs-sm);
  font-weight: 500;
  white-space: nowrap;
  transition: background-color var(--ox-motion-fast) var(--ox-ease),
              color var(--ox-motion-fast) var(--ox-ease);
  position: relative;
}
.ox-nav-link i {
  flex: 0 0 24px;
  text-align: center;
  font-size: 1.05rem;
  color: var(--ox-ink-muted);
  transition: color var(--ox-motion-fast) var(--ox-ease);
}
/* Hover: lift onto a white surface — high contrast with the sunken
 * grey sidebar background. */
.ox-nav-link:hover {
  background: var(--ox-surface-raised);
  color: var(--ox-ink);
  text-decoration: none;
}
.ox-nav-link:hover i { color: var(--ox-ink); }
.ox-nav-link.active {
  background: var(--ox-brand-tint);
  color: var(--ox-brand);
}
.ox-nav-link.active i { color: var(--ox-brand); }
.ox-nav-link.active::before {
  content: '';
  position: absolute;
  left: -2px;          /* hug the sidebar's left edge */
  top: 6px;
  bottom: 6px;
  width: 3px;
  border-radius: 0 var(--ox-radius-sm) var(--ox-radius-sm) 0;
  background: var(--ox-brand);
}
.ox-nav-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: opacity var(--ox-motion-fast) var(--ox-ease);
}
body.ox-sidebar-collapsed .ox-sidebar .ox-nav-label {
  opacity: 0;
  pointer-events: none;
}
.ox-sidebar-footer {
  padding: var(--ox-space-2);
  border-top: 1px solid var(--ox-hairline);
  font-size: var(--ox-fs-xs);
  color: var(--ox-ink-muted);
  white-space: nowrap;
  overflow: hidden;
  transition: opacity var(--ox-motion-fast) var(--ox-ease);
}
body.ox-sidebar-collapsed .ox-sidebar .ox-sidebar-footer {
  opacity: 0;
  pointer-events: none;
}

/* Admin eyebrow row: section label on the left + "..." trigger on the
 * right. Behaves like .ox-nav-section but also hosts a dropdown button. */
.ox-nav-section-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--ox-space-4) var(--ox-space-3) var(--ox-space-2);
  min-height: 28px;
  transition: opacity var(--ox-motion-fast) var(--ox-ease),
              padding var(--ox-motion-base) var(--ox-ease);
}
.ox-nav-section-row .ox-nav-section { padding: 0; }
.ox-nav-section-row .ox-section-menu-trigger {
  width: 24px; height: 24px;
  border: 0 !important;
  outline: 0;
  box-shadow: none;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  color: var(--ox-ink-muted);
  border-radius: var(--ox-radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: 0 0 auto;
  transition: background-color var(--ox-motion-fast) var(--ox-ease),
              color var(--ox-motion-fast) var(--ox-ease);
}
.ox-nav-section-row .ox-section-menu-trigger:hover,
.ox-nav-section-row .ox-section-menu-trigger[aria-expanded="true"] {
  background: var(--ox-surface-raised);
  color: var(--ox-ink);
}
/* When the sidebar is collapsed, the eyebrow label hides; center the
 * trigger button in the icon-rail slot so it remains tappable. */
body.ox-sidebar-collapsed .ox-sidebar .ox-nav-section-row {
  padding: var(--ox-space-3) 0 var(--ox-space-2);
  justify-content: center;
}
body.ox-sidebar-collapsed .ox-sidebar .ox-nav-section-row .ox-nav-section {
  display: none;
}

/* Mobile-only hamburger in the top bar — visible <992px to open the
 * off-canvas. Replaces the previous desktop-and-mobile shared button. */
@media (min-width: 992px) {
  .ox-topbar-toggle { display: none; }
}

/* Overflow "More" row — rendered hidden by default. JS reveals it and
 * hides nav items into it when the sidebar can't fit everything. The
 * button uses the same .ox-nav-link styling so it sits flush with the
 * rest of the rail. */
.ox-nav-more-row {
  position: relative;        /* dropdown anchor */
}
.ox-nav-more-row .ox-nav-link {
  width: 100%;
  border: 0 !important;
  outline: 0;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  text-align: left;
  cursor: pointer;
}
.ox-nav-more-row .dropdown-menu {
  /* Anchor the menu to the right of the rail rather than below the
   * trigger, so it works in both expanded and collapsed widths. */
  min-width: 200px;
  /* The menu holds every module that did not fit, so the shorter the
   * viewport the taller it gets — which is backwards, and at a 400px
   * viewport it was 472px tall and ran off the screen. Cap it against
   * the viewport and let it scroll: a menu you can scroll is a menu, a
   * menu that ends below the fold is not (Tom 2026-08-27). */
  max-height: min(70vh, 30rem);
  overflow-y: auto;
}
.ox-nav-more-item i { margin-right: var(--ox-space-2); color: var(--ox-ink-muted); }

/* The Admin tools menu is in the same clipped box as More and gets the
 * same cap. Both are positioned against the viewport from script; this is
 * the half that keeps them from being taller than it. */
.ox-nav-section-row .dropdown-menu {
  max-height: min(70vh, 30rem);
  overflow-y: auto;
}

/* ── Page gutter ───────────────────────────────────────────────
 *
 * Every page's content is rendered inside this one <main>, so the
 * indent belongs here rather than on each page's own container --
 * twenty templates would be twenty chances to miss one, and the next
 * page added would start without it.
 *
 * Bootstrap's `.container-fluid` keeps its own small padding inside
 * this, which is what stops content touching the gutter exactly --
 * and on a narrow screen it adds to a gutter that is already modest,
 * since 5% of 375px is about 19px.
 *
 * No breakpoint: a percentage scales by construction, which is the
 * argument for it over the fixed gutter this replaced.
 */
main {
  padding-inline: var(--ox-page-gutter);
}

/* ── Main content ──────────────────────────────────────────── */
.ox-main {
  margin-left: var(--ox-sidebar-w);
  padding: var(--ox-space-5) var(--ox-space-6);
  min-height: calc(100vh - var(--ox-topbar-h));
  transition: margin-left var(--ox-motion-base) var(--ox-ease);
}
body.ox-sidebar-collapsed .ox-main {
  margin-left: var(--ox-sidebar-w-collapsed);
}

/* ── Mobile / narrow tablet (<992px) ───────────────────────── */
@media (max-width: 991.98px) {
  .ox-sidebar { display: none; }
  .ox-main { margin-left: 0; padding: var(--ox-space-4); }
  .ox-topbar-search { display: none; }   /* search moves into off-canvas */
}

/* Off-canvas mirror of the sidebar */
.ox-offcanvas {
  width: 280px;
  background: var(--ox-surface-raised);
}
.ox-offcanvas .offcanvas-header {
  border-bottom: 1px solid var(--ox-hairline);
  padding: var(--ox-space-3) var(--ox-space-4);
}
.ox-offcanvas .offcanvas-body {
  padding: var(--ox-space-3) var(--ox-space-2);
}

/* Flash container — float top-right under the top bar */
.flash-container {
  position: fixed;
  top: calc(var(--ox-topbar-h) + var(--ox-space-3));
  right: var(--ox-space-4);
  z-index: 1050;
  max-width: 400px;
}
.flash-container .alert {
  box-shadow: var(--ox-shadow-md);
  border: 0;
  border-radius: var(--ox-radius-md);
  background: var(--ox-surface-raised);
}
.flash-container .alert-success { border-left: 3px solid var(--ox-success); }
.flash-container .alert-warning { border-left: 3px solid var(--ox-warning); }
.flash-container .alert-danger  { border-left: 3px solid var(--ox-danger);  }
.flash-container .alert-info    { border-left: 3px solid var(--ox-info);    }


/* -------------------------------------------------------------
 * 5. Components
 * ------------------------------------------------------------- */

/* ── Cards ─────────────────────────────────────────────────── */
/* Replace heavy borders with subtle elevation + tinted background.
 * Reach for .card only when content needs visual containment;
 * prefer raw whitespace + headings when it doesn't. */
.card {
  background: var(--ox-surface-raised);
  border: 0;
  border-radius: var(--ox-radius-lg);
  box-shadow: var(--ox-shadow-sm);
  overflow: hidden;
}
.card-header {
  background: var(--ox-surface-sunken);    /* subtle grey so headers separate from card body */
  border-bottom: 1px solid var(--ox-hairline);
  padding: var(--ox-space-3) var(--ox-space-4);
  font-weight: 600;
  color: var(--ox-ink);
}
.card-header > .h5,
.card-header > h5 { margin: 0; }
.card-body { padding: var(--ox-space-4); }
.card-footer {
  background: transparent;
  border-top: 1px solid var(--ox-hairline);
  padding: var(--ox-space-3) var(--ox-space-4);
}

/* Sectioned page — use this in place of nested cards-in-cards */
.ox-section {
  margin-bottom: var(--ox-space-6);
}
.ox-section-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ox-space-3);
  margin-bottom: var(--ox-space-3);
  padding-bottom: var(--ox-space-2);
  border-bottom: 1px solid var(--ox-hairline);
}
.ox-section-title {
  font-size: var(--ox-fs-md);
  font-weight: 600;
  margin: 0;
  color: var(--ox-ink);
}

/* Stat tiles (dashboard summary) — replace old .stat-card */
.ox-stat {
  display: flex;
  flex-direction: column;
  gap: var(--ox-space-1);
  padding: var(--ox-space-4);
  background: var(--ox-surface-raised);
  border-radius: var(--ox-radius-lg);
  box-shadow: var(--ox-shadow-sm);
  border-left: 3px solid var(--ox-hairline-strong);
  transition: box-shadow var(--ox-motion-fast) var(--ox-ease),
              transform var(--ox-motion-fast) var(--ox-ease);
}
.ox-stat:hover {
  box-shadow: var(--ox-shadow-md);
  transform: translateY(-1px);
}
.ox-stat-label {
  font-size: var(--ox-fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ox-ink-muted);
}
.ox-stat-value {
  font-size: var(--ox-fs-xl);
  font-weight: 600;
  color: var(--ox-ink);
  letter-spacing: -0.01em;
}
.ox-stat-meta {
  font-size: var(--ox-fs-xs);
  color: var(--ox-ink-muted);
}
.ox-stat.is-brand   { border-left-color: var(--ox-brand); }
.ox-stat.is-success { border-left-color: var(--ox-success); }
.ox-stat.is-warning { border-left-color: var(--ox-warning); }
.ox-stat.is-danger  { border-left-color: var(--ox-danger); }
.ox-stat.is-muted   { border-left-color: var(--ox-hairline-strong); }

/* Back-compat aliases for the old .stat-card class — keeps existing
 * dashboard markup looking right until templates are migrated. */
.stat-card {
  display: flex;
  flex-direction: column;
  gap: var(--ox-space-1);
  padding: var(--ox-space-4);
  background: var(--ox-surface-raised);
  border-radius: var(--ox-radius-lg);
  box-shadow: var(--ox-shadow-sm);
  border-left: 3px solid var(--ox-hairline-strong) !important;
}
.stat-card.draft    { border-left-color: var(--ox-hairline-strong) !important; }
.stat-card.review   { border-left-color: var(--ox-warning) !important; }
.stat-card.approved { border-left-color: var(--ox-success) !important; }
.stat-card.total    { border-left-color: var(--ox-brand) !important; }

/* ── Tables ────────────────────────────────────────────────── */
.table {
  --bs-table-bg: transparent;
  --bs-table-color: var(--ox-ink);
  margin-bottom: 0;
}
.table > :not(caption) > * > * {
  background: transparent;
  border-bottom: 1px solid var(--ox-hairline);
  vertical-align: middle;
}
.table tbody tr {
  transition: background-color var(--ox-motion-fast) var(--ox-ease);
}
.table-hover > tbody > tr:hover > * {
  background-color: var(--ox-surface-sunken);
}
.table > :not(:first-child) { border-top: 0; }
.table-bordered > :not(caption) > * > * {
  border: 0;
  border-bottom: 1px solid var(--ox-hairline);
}
/* Sticky table headers when used inside a scrollable .table-responsive */
.table-responsive { border-radius: var(--ox-radius-lg); }
.table-responsive .table thead th {
  position: sticky;
  top: 0;
  background: var(--ox-surface-raised);
  z-index: 1;
}

/* Column-width helpers for list tables. Use sparingly — only when a
 * column genuinely needs more room than its content claims by default. */
.ox-col-date { min-width: 130px; white-space: nowrap; }
.ox-col-id   { min-width: 120px; white-space: nowrap; }

/* ── Attachment thumbnail grid ─────────────────────────────────
 * Used in notebook entry view for both main attachments and addendum
 * attachments. Three columns on wide screens, two on tablet, one on
 * mobile. Click a tile to open in the .ox-att-modal full-size view. */
.ox-att-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--ox-space-3);
}
@media (max-width: 991.98px) {
  .ox-att-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 575.98px) {
  .ox-att-grid { grid-template-columns: 1fr; }
}
.ox-att-tile {
  display: flex;
  flex-direction: column;
  background: var(--ox-surface-raised);
  border-radius: var(--ox-radius-md);
  overflow: hidden;
  box-shadow: var(--ox-shadow-xs);
  transition: box-shadow var(--ox-motion-fast) var(--ox-ease),
              transform var(--ox-motion-fast) var(--ox-ease);
}
.ox-att-tile:hover { box-shadow: var(--ox-shadow-sm); transform: translateY(-1px); }
.ox-att-thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--ox-surface-sunken);
  border: 0 !important;
  outline: 0;
  padding: 0;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  overflow: hidden;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ox-att-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.ox-att-icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ox-space-1);
  color: var(--ox-ink-muted);
}
.ox-att-icon .bi { font-size: var(--ox-fs-xxl); color: var(--ox-ink-faint); }
.ox-att-ext {
  font-family: var(--ox-font-mono);
  font-size: var(--ox-fs-xs);
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--ox-ink-muted);
}
.ox-att-meta {
  padding: var(--ox-space-2) var(--ox-space-3);
  border-top: 1px solid var(--ox-hairline);
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.ox-att-name {
  font-size: var(--ox-fs-xs);
  font-weight: 600;
  color: var(--ox-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ox-att-info {
  font-size: var(--ox-fs-xs);
  color: var(--ox-ink-muted);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ox-space-2);
}
.ox-att-dl {
  color: var(--ox-ink-muted);
  text-decoration: none;
  padding: 2px 4px;
  border-radius: var(--ox-radius-sm);
}
.ox-att-dl:hover { color: var(--ox-brand); background: var(--ox-brand-tint); }

/* ── Breadcrumb ──────────────────────────────────────────────
 * Small navigation row used at the top of nested views (notebook
 * library → notebook → entry). Sits ABOVE the page header. */
.ox-breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--ox-space-2);
  margin-bottom: var(--ox-space-2);
  font-size: var(--ox-fs-xs);
  color: var(--ox-ink-muted);
}
.ox-breadcrumb a { color: var(--ox-ink-muted); text-decoration: none; }
.ox-breadcrumb a:hover { color: var(--ox-brand); text-decoration: underline; }
.ox-breadcrumb [aria-current="page"] { color: var(--ox-ink); font-weight: 500; }

/* Per-notebook page-number prefix in the entry title — small mono chip
 * that doesn't overpower the title text. */
.ox-nb-prefix-lg {
  display: inline-block;
  font-family: var(--ox-font-mono);
  font-size: 0.65em;
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--ox-brand);
  background: var(--ox-brand-tint);
  padding: 2px 8px;
  border-radius: var(--ox-radius-sm);
  vertical-align: middle;
  margin-right: var(--ox-space-1);
}
.ox-page-num {
  font-family: var(--ox-font-mono);
  font-weight: 600;
  color: var(--ox-brand);
}
.ox-page-title-divider { color: var(--ox-ink-faint); margin: 0 var(--ox-space-1); }
.ox-page-title-text { color: var(--ox-ink); }
.ox-id-faint {
  font-family: var(--ox-font-mono);
  font-size: var(--ox-fs-xs);
  color: var(--ox-ink-faint);
  letter-spacing: 0.02em;
}


/* ── Note / addendum tile grid ───────────────────────────────
 * Used in notebook entry view for the Data/Notes addenda list. Two
 * columns at desktop, one at mobile. Each tile is its own surface
 * (header + rich-text body + optional attachment thumbnails). The
 * "add new" form sits below the grid at full width so writing has
 * room to breathe. */
.ox-note-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--ox-space-3);
  align-items: start;          /* don't stretch shorter notes */
}
@media (max-width: 767.98px) {
  .ox-note-grid { grid-template-columns: 1fr; }
}
.ox-note-tile {
  background: var(--ox-surface-raised);
  border-radius: var(--ox-radius-md);
  box-shadow: var(--ox-shadow-xs);
  padding: var(--ox-space-3) var(--ox-space-4);
  display: flex;
  flex-direction: column;
  gap: var(--ox-space-2);
  transition: box-shadow var(--ox-motion-fast) var(--ox-ease);
}
.ox-note-tile:hover { box-shadow: var(--ox-shadow-sm); }
.ox-note-tile-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--ox-space-2);
  padding-bottom: var(--ox-space-2);
  border-bottom: 1px solid var(--ox-hairline);
}
.ox-note-author { font-weight: 600; font-size: var(--ox-fs-sm); color: var(--ox-ink); }
.ox-note-time   { font-size: var(--ox-fs-xs); color: var(--ox-ink-muted); white-space: nowrap; }
.ox-note-body { font-size: var(--ox-fs-sm); }
.ox-note-body p:last-child { margin-bottom: 0; }
.ox-note-atts { margin-top: var(--ox-space-1); }

/* Tighter attachment grid for use inside note tiles. */
.ox-att-grid.is-tight {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--ox-space-2);
}
@media (max-width: 575.98px) {
  .ox-att-grid.is-tight { grid-template-columns: 1fr; }
}

/* Form below the grid: visually separated so it reads as "compose"
 * rather than another note. */
.ox-note-form-wrap {
  margin-top: var(--ox-space-5);
  padding-top: var(--ox-space-4);
  border-top: 1px solid var(--ox-hairline);
}
.ox-note-form-wrap .ox-eyebrow { margin-bottom: var(--ox-space-2); }

/* Modal: stretchy wrapper for an image / iframe loaded on demand. */
#oxAttModal .modal-dialog { max-width: 90vw; }
#oxAttModal .modal-body {
  padding: 0;
  background: var(--ox-surface-sunken);
  /* Allow image / iframe content to overflow when zoomed; both axes
   * get scrollbars as needed. */
  overflow: auto;
  max-height: 85vh;
  min-height: 200px;
  /* Flex centering keeps un-zoomed media in the middle of the dialog. */
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Image: render at native size; only scale DOWN when it exceeds the modal,
 * never stretch up. The zoom button applies an inline transform on top of
 * these constraints — transform-origin:center so zooming feels natural. */
#oxAttModal .ox-att-modal-img {
  display: block;
  max-width: 100%;
  max-height: 80vh;
  width: auto;
  height: auto;
  object-fit: contain;
  margin: 0 auto;
  transform-origin: center center;
  transition: transform var(--ox-motion-fast) var(--ox-ease);
}
/* Iframe (PDF / doc previews) needs an explicit size — there's no native
 * intrinsic size to fall back on. */
#oxAttModal .ox-att-modal-iframe {
  display: block;
  width: 100%;
  height: 80vh;
  border: 0;
  background: var(--ox-surface-raised);
}

/* Surface a table inside a card cleanly (no nested borders) */
.card > .table-responsive,
.card > .table {
  margin: 0;
}
.card > .table > :not(caption) > * > * {
  padding-left: var(--ox-space-4);
  padding-right: var(--ox-space-4);
}

/* ── Buttons ───────────────────────────────────────────────── */
.btn {
  border-radius: var(--ox-radius-md);
  padding: var(--ox-space-2) var(--ox-space-3);
  transition: background-color var(--ox-motion-fast) var(--ox-ease),
              border-color var(--ox-motion-fast) var(--ox-ease),
              color var(--ox-motion-fast) var(--ox-ease),
              box-shadow var(--ox-motion-fast) var(--ox-ease);
}
.btn-primary {
  background: var(--ox-brand);
  border-color: var(--ox-brand);
  color: var(--ox-ink-inverse);
}
.btn-primary:hover,
.btn-primary:focus {
  background: var(--ox-brand-hover);
  border-color: var(--ox-brand-hover);
  color: var(--ox-ink-inverse);
}
.btn-primary:active,
.btn-primary:disabled,
.btn-primary.disabled {
  background: var(--ox-brand-pressed);
  border-color: var(--ox-brand-pressed);
  color: var(--ox-ink-inverse);
}
.btn-outline-primary {
  border-color: var(--ox-brand);
  color: var(--ox-brand);
}
.btn-outline-primary:hover {
  background: var(--ox-brand);
  border-color: var(--ox-brand);
  color: var(--ox-ink-inverse);
}
.btn-secondary,
.btn-light {
  background: var(--ox-surface-raised);
  border-color: var(--ox-hairline-strong);
  color: var(--ox-ink);
}
.btn-secondary:hover,
.btn-light:hover {
  background: var(--ox-surface-sunken);
  border-color: var(--ox-hairline-strong);
  color: var(--ox-ink);
}
.btn-outline-secondary {
  border-color: var(--ox-hairline-strong);
  color: var(--ox-ink-muted);
  background: transparent;
}
.btn-outline-secondary:hover {
  background: var(--ox-surface-sunken);
  border-color: var(--ox-hairline-strong);
  color: var(--ox-ink);
}
.btn-success {
  background: var(--ox-success); border-color: var(--ox-success); color: var(--ox-ink-inverse);
}
.btn-success:hover { background: #0B5B55; border-color: #0B5B55; color: var(--ox-ink-inverse); }
.btn-danger {
  background: var(--ox-danger); border-color: var(--ox-danger); color: var(--ox-ink-inverse);
}
.btn-danger:hover { background: #9A1818; border-color: #9A1818; color: var(--ox-ink-inverse); }
.btn-warning {
  background: var(--ox-warning); border-color: var(--ox-warning); color: var(--ox-ink-inverse);
}
.btn-warning:hover { background: var(--ox-warning-ink); border-color: var(--ox-warning-ink); color: var(--ox-ink-inverse); }
.btn-outline-light {                         /* used in old dark sidebar */
  color: var(--ox-ink-muted);
  border-color: var(--ox-hairline-strong);
}
.btn-outline-light:hover {
  background: var(--ox-surface-sunken);
  color: var(--ox-ink);
  border-color: var(--ox-hairline-strong);
}
.btn-outline-danger {
  color: var(--ox-danger);
  border-color: var(--ox-danger);
}
.btn-outline-danger:hover {
  background: var(--ox-danger);
  color: var(--ox-ink-inverse);
  border-color: var(--ox-danger);
}

/* Ghost / icon-only button used in toolbars */
.ox-btn-ghost {
  background: transparent;
  border: 0;
  color: var(--ox-ink-muted);
  padding: var(--ox-space-2) var(--ox-space-3);
  border-radius: var(--ox-radius-md);
}
.ox-btn-ghost:hover {
  background: var(--ox-surface-sunken);
  color: var(--ox-ink);
}

/* ── Page header ───────────────────────────────────────────── */
.ox-page-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--ox-space-4);
  flex-wrap: wrap;
  margin-bottom: var(--ox-space-5);
  padding-bottom: var(--ox-space-3);
  border-bottom: 1px solid var(--ox-hairline);
}
.ox-page-header-meta {
  display: flex;
  flex-direction: column;
  gap: var(--ox-space-1);
}
.ox-page-header-actions {
  display: flex;
  gap: var(--ox-space-2);
  align-items: center;
}

/* ── Toolbar (above tables/lists) ──────────────────────────── */
.ox-toolbar {
  display: flex;
  align-items: center;
  gap: var(--ox-space-2);
  flex-wrap: wrap;
  margin-bottom: var(--ox-space-3);
}
.ox-toolbar .form-control,
.ox-toolbar .form-select {
  max-width: 240px;
}

/* ── Forms ─────────────────────────────────────────────────── */
.form-control,
.form-select {
  background: var(--ox-surface-raised);
  border: 1px solid var(--ox-hairline-strong);
  border-radius: var(--ox-radius-md);
  color: var(--ox-ink);
  padding: var(--ox-space-2) var(--ox-space-3);
  transition: border-color var(--ox-motion-fast) var(--ox-ease),
              box-shadow var(--ox-motion-fast) var(--ox-ease);
}
.form-control:focus,
.form-select:focus {
  border-color: var(--ox-brand);
  box-shadow: 0 0 0 3px var(--ox-brand-tint);
}
.form-control::placeholder { color: var(--ox-ink-faint); }
.form-check-input:checked {
  background-color: var(--ox-brand);
  border-color: var(--ox-brand);
}
.form-check-input:focus {
  border-color: var(--ox-brand);
  box-shadow: 0 0 0 3px var(--ox-brand-tint);
}
.input-group-text {
  background: var(--ox-surface-sunken);
  border-color: var(--ox-hairline-strong);
  color: var(--ox-ink-muted);
}

/* ── Badges / status pills ─────────────────────────────────── */
.badge {
  font-size: var(--ox-fs-xs);
  font-weight: 600;
  padding: 0.25em 0.6em;
  border-radius: var(--ox-radius-pill);
  letter-spacing: 0.02em;
}
.badge.bg-primary    { background: var(--ox-brand) !important;   color: var(--ox-ink-inverse); }
.badge.bg-secondary  { background: var(--ox-surface-sunken) !important; color: var(--ox-ink-muted); }
.badge.bg-success    { background: var(--ox-success-tint) !important; color: var(--ox-success); }
.badge.bg-warning    { background: var(--ox-warning-tint) !important; color: var(--ox-warning); }
.badge.bg-danger     { background: var(--ox-danger-tint) !important;  color: var(--ox-danger); }
.badge.bg-info       { background: var(--ox-info-tint) !important;    color: var(--ox-info); }
.badge.bg-dark       { background: var(--ox-ink) !important;          color: var(--ox-ink-inverse); }
.badge.bg-light      { background: var(--ox-surface-sunken) !important; color: var(--ox-ink); }

/* Status pill — explicit variant for review/approved/draft etc. */
.ox-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--ox-space-1);
  padding: 0.2em 0.6em;
  border-radius: var(--ox-radius-pill);
  font-size: var(--ox-fs-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.ox-pill.is-draft   { background: var(--ox-surface-sunken); color: var(--ox-ink-muted); }
.ox-pill.is-review  { background: var(--ox-warning-tint);   color: var(--ox-warning); }
.ox-pill.is-approved{ background: var(--ox-success-tint);   color: var(--ox-success); }
.ox-pill.is-locked  { background: var(--ox-brand-tint);     color: var(--ox-brand); }
.ox-pill.is-danger  { background: var(--ox-danger-tint);    color: var(--ox-danger); }

/* ── Alerts (in-page) ──────────────────────────────────────── */
.alert {
  border: 0;
  border-left: 3px solid;
  border-radius: var(--ox-radius-md);
  padding: var(--ox-space-3) var(--ox-space-4);
  background: var(--ox-surface-raised);
  color: var(--ox-ink);
  box-shadow: var(--ox-shadow-xs);
}
/* Dismissible alerts need extra right-padding so the absolutely-
   positioned .btn-close doesn't overlap the message text. Our
   custom .alert padding above (identical specificity to Bootstrap's
   .alert-dismissible) wiped out the padding-right: 3rem Bootstrap
   ships, so the close X was sitting on top of the last words of
   the flash message. Fix by re-declaring it here (Tom 2026-07-25). */
.alert.alert-dismissible {
  padding-right: 3rem;
}
.alert-success { border-left-color: var(--ox-success); background: var(--ox-success-tint); }
.alert-warning { border-left-color: var(--ox-warning); background: var(--ox-warning-tint); }
.alert-danger  { border-left-color: var(--ox-danger);  background: var(--ox-danger-tint); }
.alert-info    { border-left-color: var(--ox-info);    background: var(--ox-info-tint); }
.alert-primary { border-left-color: var(--ox-brand);   background: var(--ox-brand-tint); }

/* ── Dropdowns ─────────────────────────────────────────────── */
.dropdown-menu {
  border: 0;
  border-radius: var(--ox-radius-md);
  box-shadow: var(--ox-shadow-md);
  padding: var(--ox-space-1);
  font-size: var(--ox-fs-sm);
  min-width: 200px;
}
.dropdown-item {
  padding: var(--ox-space-2) var(--ox-space-3);
  border-radius: var(--ox-radius-sm);
  color: var(--ox-ink);
  display: flex;
  align-items: center;
  gap: var(--ox-space-2);
}
.dropdown-item:hover,
.dropdown-item:focus {
  background: var(--ox-surface-sunken);
  color: var(--ox-ink);
}
.dropdown-item.active,
.dropdown-item:active {
  background: var(--ox-brand-tint);
  color: var(--ox-brand);
}
.dropdown-item.text-danger { color: var(--ox-danger); }
.dropdown-item.text-danger:hover { background: var(--ox-danger-tint); color: var(--ox-danger); }
.dropdown-divider {
  border-color: var(--ox-hairline);
  margin: var(--ox-space-1) 0;
}
.dropdown-header,
.dropdown-item-text {
  padding: var(--ox-space-2) var(--ox-space-3);
  font-size: var(--ox-fs-xs);
  color: var(--ox-ink-muted);
}

/* ── List groups ───────────────────────────────────────────── */
.list-group {
  border-radius: var(--ox-radius-lg);
  overflow: hidden;
}
.list-group-item {
  border: 0;
  border-bottom: 1px solid var(--ox-hairline);
  padding: var(--ox-space-3) var(--ox-space-4);
  background: var(--ox-surface-raised);
  color: var(--ox-ink);
}
.list-group-item:last-child { border-bottom: 0; }
.list-group-item-action:hover { background: var(--ox-surface-sunken); }

/* ── Nav tabs / pills ──────────────────────────────────────── */
.nav-tabs {
  border-bottom: 1px solid var(--ox-hairline);
}
.nav-tabs .nav-link {
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--ox-ink-muted);
  font-weight: 500;
  padding: var(--ox-space-2) var(--ox-space-3);
}
.nav-tabs .nav-link:hover {
  color: var(--ox-ink);
  border-color: transparent;
}
.nav-tabs .nav-link.active {
  color: var(--ox-brand);
  background: transparent;
  border-bottom-color: var(--ox-brand);
}

/* ── Pagination ────────────────────────────────────────────── */
.page-link {
  border-color: var(--ox-hairline);
  color: var(--ox-ink);
}
.page-link:hover {
  background: var(--ox-surface-sunken);
  border-color: var(--ox-hairline);
  color: var(--ox-ink);
}
.page-item.active .page-link {
  background: var(--ox-brand);
  border-color: var(--ox-brand);
  color: var(--ox-ink-inverse);
}

/* ── Empty / loading states ────────────────────────────────── */
.ox-empty {
  text-align: center;
  padding: var(--ox-space-7) var(--ox-space-4);
  color: var(--ox-ink-muted);
}
.ox-empty i {
  font-size: 2.5rem;
  color: var(--ox-ink-faint);
  margin-bottom: var(--ox-space-3);
}
.ox-empty-title {
  font-size: var(--ox-fs-md);
  font-weight: 600;
  color: var(--ox-ink);
  margin-bottom: var(--ox-space-1);
}
.ox-skeleton {
  background: linear-gradient(90deg,
              var(--ox-surface-sunken) 0%,
              var(--ox-hairline) 50%,
              var(--ox-surface-sunken) 100%);
  background-size: 200% 100%;
  animation: ox-skeleton-shimmer 1.5s ease-in-out infinite;
  border-radius: var(--ox-radius-sm);
}
@keyframes ox-skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}


/* -------------------------------------------------------------
 * 6. Motion — animated entries for menus, drawers, popovers
 * ------------------------------------------------------------- */

/* Bootstrap toggles .show on dropdown-menu when open. Drive an enter
 * animation off that class so menus feel like they're being surfaced.
 *
 * Animated with `translate` and `scale`, NOT with `transform`, and this
 * is not a style preference (Tom 2026-08-27).
 *
 * Bootstrap positions every Popper-placed dropdown by writing
 * `transform: translate(x, y)` inline. Animating the same property here
 * put the two in a fight the menu lost: the rail's More menu was placed
 * correctly at y=60 and rendered at y=-4 — displaced by exactly the
 * translateY(-4px) below, and scaled to 0.98 with it — which on a short
 * viewport tucked its first rows under the fixed topbar, where they
 * could not be clicked.
 *
 * `translate` and `scale` are separate properties from `transform` and
 * compose with it rather than replacing it, so the animation and the
 * positioning stop overwriting one another. Every dropdown in the app is
 * fixed by this, not only the rail's. */
.dropdown-menu {
  display: block;             /* always rendered to allow transition */
  visibility: hidden;
  opacity: 0;
  translate: 0 -4px;
  scale: 0.98;
  transform-origin: top right;
  pointer-events: none;
  transition: opacity var(--ox-motion-base) var(--ox-ease),
              translate var(--ox-motion-base) var(--ox-ease),
              scale var(--ox-motion-base) var(--ox-ease),
              visibility 0s var(--ox-motion-base);
}
.dropdown-menu.show {
  visibility: visible;
  opacity: 1;
  translate: 0 0;
  scale: 1;
  pointer-events: auto;
  transition: opacity var(--ox-motion-base) var(--ox-ease),
              translate var(--ox-motion-base) var(--ox-ease),
              scale var(--ox-motion-base) var(--ox-ease),
              visibility 0s;
}
/* Menus that drop from a left-aligned trigger get a different origin. */
.dropdown-menu:not(.dropdown-menu-end) { transform-origin: top left; }
.dropdown-menu.dropdown-menu-end       { transform-origin: top right; }

/* Off-canvas: Bootstrap already animates the transform; we just retune
 * the timing/easing to match the rest of the system. */
.offcanvas {
  transition: transform var(--ox-motion-slow) var(--ox-ease) !important;
}

/* Modal backdrop and fade: align easing with the system. */
.modal.fade .modal-dialog {
  transition: transform var(--ox-motion-base) var(--ox-ease);
}


/* -------------------------------------------------------------
 * 7. Focus + accessibility
 * ------------------------------------------------------------- */
/* Single source of truth for focus rings — visible to keyboard, hidden
 * for mouse users. Amber accent so the brand-blue UI doesn't swallow
 * the ring. */
:focus { outline: none; }   /* Bootstrap default — replaced below */
:focus-visible {
  outline: 2px solid var(--ox-accent);
  outline-offset: 2px;
  border-radius: var(--ox-radius-sm);
}
/* Form controls get an inset glow instead of an outer outline so they
 * don't break their own border-radius. */
.form-control:focus-visible,
.form-select:focus-visible {
  outline: none;
  border-color: var(--ox-brand);
  box-shadow: 0 0 0 3px var(--ox-brand-tint);
}
/* Buttons keep an outer outline because they don't have a glow state. */
.btn:focus-visible,
.dropdown-toggle:focus-visible {
  outline: 2px solid var(--ox-accent);
  outline-offset: 2px;
}
/* Skip link — present in DOM, only visible on focus. */
.ox-skip-link {
  position: absolute;
  top: var(--ox-space-2);
  left: var(--ox-space-2);
  padding: var(--ox-space-2) var(--ox-space-3);
  background: var(--ox-brand);
  color: var(--ox-ink-inverse);
  border-radius: var(--ox-radius-md);
  font-weight: 600;
  font-size: var(--ox-fs-sm);
  z-index: 2000;
  transform: translateY(-200%);
  transition: transform var(--ox-motion-fast) var(--ox-ease);
}
.ox-skip-link:focus,
.ox-skip-link:focus-visible {
  transform: translateY(0);
  color: var(--ox-ink-inverse);
}


/* -------------------------------------------------------------
 * 8. Motion — respect reduced-motion preference
 * ------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* ════════════════════════════════════════════════════════════════════
   Notebook sidebar (P1-P4)
   Used by templates/notebook/_sidebar.html on the library + notebook/
   folder/entry detail pages. Two-pane layout: left tree, right content.
   Collapse-toggle shrinks the sidebar to a 32px gutter with just the
   re-open button.
   ════════════════════════════════════════════════════════════════════ */
.nb-lib {
  display: flex;
  height: calc(100vh - var(--ox-header-h, 0px));
  min-height: 600px;
  gap: 0;
}

.nb-lib-sidebar {
  position: relative;  /* containing block for the absolute toggle */
  width: 320px; min-width: 240px; max-width: 480px;
  border-right: 1px solid #dee2e6; background: #fafbfc;
  display: flex; flex-direction: column; overflow: hidden;
  resize: horizontal;
  transition: width 0.18s ease, min-width 0.18s ease;
}
.nb-lib-sidebar.is-collapsed {
  width: 32px !important; min-width: 32px; max-width: 32px;
  resize: none;
}
.nb-lib-sidebar.is-collapsed > *:not(.nb-collapse-toggle) { display: none; }

/* Toggle button — sits in the top-right of the header when expanded,
   and becomes a vertical pill in the center of the sidebar when
   collapsed (the only thing visible there). Always absolute-positioned
   so collapse/expand doesn't reflow the header. */
.nb-collapse-toggle {
  position: absolute; top: 10px; right: 6px;
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: transparent; color: #6c757d;
  border-radius: 4px; cursor: pointer; z-index: 5;
  transition: background 0.15s, color 0.15s;
}
.nb-collapse-toggle:hover { background: var(--ox-brand-tint); color: var(--ox-brand); }
.nb-lib-sidebar.is-collapsed .nb-collapse-toggle {
  top: 50%; right: 2px; transform: translateY(-50%);
  width: 28px; height: 60px;
  background: var(--ox-ink-inverse); border: 1px solid #dee2e6;
  border-radius: 4px;
}

.nb-lib-sidebar-head {
  padding: 12px 40px 12px 14px;  /* right pad clears the absolute toggle */
  border-bottom: 1px solid #e9ecef; background: var(--ox-ink-inverse);
  display: flex; align-items: center; gap: 8px;
}
.nb-lib-sidebar-head h2 {
  font-size: 14px; font-weight: 600; margin: 0; flex: 1;
  text-transform: uppercase; letter-spacing: 0.04em; color: #495057;
}
/* P4a: bigger header action buttons (was btn-sm icon-only at ~24px;
   now ~36px tap targets, more comfortable on touch + easier to hit). */
.nb-lib-sidebar-head .btn {
  padding: 7px 11px; font-size: 14px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.nb-lib-sidebar-head .btn i { font-size: 18px; }
.nb-lib-sidebar-head .btn-group .btn { border-radius: 5px; }

.nb-lib-tree {
  flex: 1; overflow-y: auto; padding: 8px 4px 16px; font-size: 13.5px;
}
.nb-lib-tree-loading, .nb-lib-tree-empty {
  color: #6c757d; padding: 32px 16px; text-align: center; font-style: italic;
}
.nb-lib-tree-empty .btn { font-style: normal; margin-top: 8px; }

.nb-tree-node { user-select: none; transition: background 0.15s; outline: none; }
.nb-tree-node:focus-visible > .nb-tree-row {
  box-shadow: 0 0 0 2px var(--ox-accent), inset 0 0 0 1px var(--ox-accent);
  border-radius: 4px;
}
.nb-tree-row {
  display: flex; align-items: center; gap: 4px;
  padding: 3px 6px 3px 4px; border-radius: 4px; cursor: pointer;
  color: #212529; text-decoration: none; white-space: nowrap;
}
.nb-tree-row:hover { background: var(--ox-brand-tint); color: var(--ox-brand); }
.nb-tree-row.is-active { background: var(--ox-brand-tint-2); color: var(--ox-brand-hover); font-weight: 600; }
.nb-tree-row .chevron {
  display: inline-block; width: 14px; text-align: center; font-size: var(--ox-radius-lg);
  color: #adb5bd; flex-shrink: 0;
}
.nb-tree-row .chevron.is-leaf { visibility: hidden; }
.nb-tree-row .icon { width: 16px; flex-shrink: 0; text-align: center; color: #6c757d; }
.nb-tree-row .icon.bi-folder-fill { color: #ffc107; }
.nb-tree-row .icon.bi-journal-bookmark-fill { color: var(--ox-brand); }
.nb-tree-row .icon.bi-journal-text { color: #6c757d; }
.nb-tree-row .label {
  flex: 1; overflow: hidden; text-overflow: ellipsis;
}
.nb-tree-row .badge-page {
  font-size: var(--ox-radius-lg); color: #6c757d; background: #f1f3f5; padding: 1px 5px;
  border-radius: 3px; flex-shrink: 0;
}
.nb-tree-row .meta { font-size: 11px; color: #adb5bd; flex-shrink: 0; }

.nb-tree-children { padding-left: 18px; min-height: 4px; }
.nb-tree-node.is-collapsed > .nb-tree-children { display: none; }
.nb-tree-node.is-archived > .nb-tree-row { opacity: 0.55; }
.nb-tree-node.is-archived > .nb-tree-row .label::after {
  content: " (archived)"; color: #adb5bd; font-size: 11px; font-weight: 400;
}

/* SortableJS drag states (P2-P3) */
.nb-tree-chosen > .nb-tree-row {
  background: #fff3cd !important; cursor: grabbing;
}
.nb-tree-ghost > .nb-tree-row {
  opacity: 0.4; background: #d1e7dd !important;
}
.nb-tree-drag {
  background: var(--ox-ink-inverse) !important; box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  border-radius: 4px;
}
.nb-tree-node.is-pending-move { opacity: 0.6; pointer-events: none; }

/* While dragging, reveal every collapsed children container so the user
   can drop into ANY folder/notebook without expanding first. */
body.nb-is-dragging .nb-tree-node.is-collapsed > .nb-tree-children {
  display: block !important;
  min-height: 20px;
  margin: 2px 0 2px 18px;
  border-left: 2px dashed rgba(13, 110, 253, 0.35);
  background: rgba(13, 110, 253, 0.04);
  border-radius: 0 4px 4px 0;
}
body.nb-is-dragging .nb-tree-node.is-collapsed > .nb-tree-children:empty::before {
  content: "drop here";
  display: block; padding: 2px 8px; font-size: 11px;
  color: #6c757d; font-style: italic;
}
body.nb-is-dragging .nb-tree-children.nb-drag-over {
  background: rgba(13, 110, 253, 0.15);
  border-color: rgba(13, 110, 253, 0.7);
}

.nb-lib-sidebar-foot {
  border-top: 1px solid #e9ecef; padding: 8px 12px; background: var(--ox-ink-inverse);
  font-size: 12px; color: #6c757d; display: flex; gap: 8px; align-items: center;
}
.nb-lib-sidebar-foot .form-check-input { margin-top: 0; }

/* Right pane: holds whatever the consuming page renders. */
.nb-lib-pane { flex: 1; overflow-y: auto; padding: 32px 40px; min-width: 0; }
.nb-lib-pane h1 { font-size: 24px; font-weight: 600; margin-bottom: 4px; }
.nb-lib-pane .lead { color: #6c757d; font-size: 14px; }

/* Library landing right-pane (stats grid + recent list) */
.nb-stat-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px; margin: 24px 0;
}
.nb-stat-card {
  background: var(--ox-ink-inverse); border: 1px solid #e9ecef; border-radius: 8px;
  padding: 16px 20px;
}
.nb-stat-card .label {
  font-size: 11px; color: #6c757d; text-transform: uppercase;
  letter-spacing: 0.04em; margin-bottom: 6px;
}
.nb-stat-card .value { font-size: 28px; font-weight: 600; color: #212529; }
.nb-recent-list { list-style: none; padding: 0; margin: 0; }
.nb-recent-list li {
  padding: 10px 0; border-bottom: 1px solid #f1f3f5; display: flex; gap: 12px;
  align-items: baseline;
}
.nb-recent-list li:last-child { border-bottom: none; }
.nb-recent-list .page-tag {
  font-size: 11px; color: #6c757d; background: #f1f3f5;
  padding: 2px 6px; border-radius: 3px; flex-shrink: 0;
}
.nb-recent-list .title { flex: 1; color: #212529; text-decoration: none; }
.nb-recent-list .title:hover { color: var(--ox-brand); }
.nb-recent-list .date { font-size: 12px; color: #adb5bd; flex-shrink: 0; }

@media (max-width: 768px) {
  .nb-lib { flex-direction: column; height: auto; }
  .nb-lib-sidebar { width: 100%; max-width: 100%; resize: none; max-height: 50vh; }
  .nb-lib-sidebar.is-collapsed { max-height: 32px; }
  .nb-lib-pane { padding: 16px; }
}

/* Context menu (P5) — most styles inlined by JS for portability;
   these rules cover hover/focus polish. */
.nb-ctx-menu .nb-ctx-item:focus {
  outline: 2px solid var(--ox-accent); outline-offset: -2px;
}
.nb-ctx-menu .nb-ctx-item:hover {
  background: var(--ox-brand-tint) !important; color: var(--ox-brand) !important;
}
.nb-inline-rename:focus {
  outline: none; box-shadow: 0 0 0 2px rgba(13,110,253,0.25);
}
