/* Design tokens (palette + semantic + type + shape) live in tokens.css, which
   Propshaft loads before this file. This stylesheet consumes the semantic
   variables only — no raw colors, so the palette can be re-skinned in one place.

   Aesthetic: Vercel Geist. Off-white canvas, gray-1000 ink, hairline gray-400
   borders, soft raised-card shadows, 6px corners, Geist Sans headings at weight
   600 with tracking that tightens as size grows, Geist Mono for codes (opening
   IDs, coordinates, timestamps). Neutral (gray-1000) primary action; blue-700 as
   the link/focus accent. Dark theme is the same tokens, driven by data-theme on
   <html> (system follows the OS; light/dark force it) — see tokens.css. */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 14px;            /* Geist copy-14 baseline */
  font-weight: var(--weight-reg);
  line-height: 20px;
  letter-spacing: -0.01em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---- Headings: Geist weight 600, tracking tightens with size ---- */
h1,
h2,
h3 {
  margin-block-start: 0;
  font-weight: var(--weight-semi);
  text-wrap: balance;
}

p {
  margin-block-start: 0;
  text-wrap: pretty;
}

/* Mobile-first heading scale. Phone sizes are a step down so a long page title
   (e.g. a full floor-plan name) doesn't wrap into a five-line wall above the
   content; the desktop breakpoint restores the larger Geist heading-32/24/20. */
h1 {
  font-size: 26px;
  line-height: 32px;
  letter-spacing: -0.8px;
}

h2 {
  font-size: 20px;
  line-height: 28px;
  letter-spacing: -0.6px;
}

h3 {
  font-size: 18px;
  line-height: 24px;
  letter-spacing: -0.3px;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  color: var(--accent-strong);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

/* ---- Geist focus ring: surface gap + accent halo ---- */
a:focus-visible,
button:focus-visible,
.button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* ---- Geist Mono for codes: opening IDs, coordinates, timestamps ---- */
.mono,
.opening-id,
time,
.log-entry header time {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

.topbar {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--gap-control);
  width: min(var(--container), calc(100vw - 2 * var(--gutter)));
  margin: 0 auto;
  padding: var(--space-3) 0;
  background: var(--bg);
}

/* Hairline rule spans the full viewport; content stays in the column */
.topbar::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline: 50%;
  width: 100vw;
  transform: translateX(-50%);
  border-bottom: 1px solid var(--border);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-inline);
  color: var(--text);
  font-weight: var(--weight-semi);
  text-decoration: none;
}

.brand:hover {
  text-decoration: none;
  color: var(--text);
}

/* Logo mark + REVEAL wordmark */
.brand-logo {
  display: block;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-sm);
}

.brand-word {
  font-size: 14px;
  font-weight: var(--weight-semi);
  letter-spacing: 0.12em;
  line-height: 1;
}

.topbar nav {
  display: flex;
  gap: var(--gap-stack);
}

.topbar nav a {
  color: var(--muted);
  font-size: 14px;
  font-weight: var(--weight-med);
  text-decoration: none;
  transition: color var(--dur-state) var(--ease);
}

.topbar nav a:hover {
  color: var(--text);
  text-decoration: none;
}

.session {
  display: flex;
  align-items: center;
  gap: var(--gap-control);
  margin-left: auto;
  color: var(--muted);
  font-size: 13px;
}

/* Phone: the whole session block (name + sign-out) is hidden — sign-out lives in
   the bottom-bar "More" sheet, so a second button in the header is redundant. The
   header is just the brand on phone. The desktop breakpoint brings the block back. */
.session {
  display: none;
}

.page {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gap-section);
  align-content: start;
  width: min(var(--container), calc(100vw - 2 * var(--gutter)));
  margin: 0 auto;
  /* Phone: clear the fixed bottom nav (+ device safe area) so the last element
     isn't trapped under it. Desktop drops the nav and restores the roomy footer. */
  padding: var(--gap-section) 0 calc(var(--bottom-nav-h) + var(--safe-bottom) + var(--gap-section));
}

/* Inside the desktop shell, the page is the right-hand column — no own width/margin. */
.shell .page {
  width: auto;
  min-width: 0;
  margin: 0;
}

/* Mobile-first: heading block stacks above its actions so the buttons get the
   full width and don't fight the title for room. Desktop puts them side by side. */
.page-header {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--gap-control);
}

.page-header h1 {
  margin-bottom: var(--space-2);
}

.eyebrow,
.muted {
  color: var(--muted);
}

.eyebrow {
  margin-bottom: var(--space-1);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0;
  text-transform: uppercase;
}

/* Mobile-first: actions wrap, each button growing to fill its row but never
   shrinking below a readable width — so labels like "Export doors CSV" stay on one
   line and a button that won't fit drops to the next row full-width instead of
   being clipped. Desktop lets them size to content. */
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-inline);
}

.actions > .button,
.actions > form,
.actions > .is-disabled {
  flex: 1 1 8rem;
  white-space: nowrap;
}

.actions > form > .button,
.actions > form > input[type="submit"] {
  width: 100%;
}

/* ---- Buttons: neutral (gray-1000) primary, hairline secondary ---- */
.button,
button,
input[type="submit"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;            /* Geist default button height */
  padding: 0 14px;
  border: 1px solid var(--action);
  border-radius: var(--radius-sm);
  background: var(--action);
  color: var(--on-action);
  font: inherit;
  font-size: 14px;
  font-weight: var(--weight-med);
  letter-spacing: -0.28px;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur-state) var(--ease), border-color var(--dur-state) var(--ease), box-shadow var(--dur-state) var(--ease);
}

.button:hover,
button:hover,
input[type="submit"]:hover {
  background: var(--action-hover);
  border-color: var(--action-hover);
  text-decoration: none;
  color: var(--on-action);
}

.button.secondary,
.session button {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
}

.button.secondary:hover,
.session button:hover {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text);
}

/* Destructive action (delete an opening): danger fill, hairline danger border */
.button.danger,
button.danger,
.button.danger:hover,
button.danger:hover {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

/* Delete blocked because the opening has immutable log entries */
.button.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

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

/* ---- Panels: raised cards, hairline border, 6px corners, soft shadow ---- */
.card,
.panel,
.empty,
.auth-panel,
.log-entry {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: var(--pad-card);
}

.empty {
  color: var(--muted);
  box-shadow: none;
  background: var(--surface-alt);
  border-style: dashed;
}

.table-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}

table {
  width: 100%;
  border-collapse: collapse;
}

/* ---- Mobile-first table: stacked rows ----
   On phone a multi-column table overflows the viewport. Instead each row becomes a
   block "card" and each cell a "Label: value" line, the label pulled from the
   column header by responsive_table_controller.js. The header row is hidden (its
   text now lives in the labels) and the leading cell reads as the row title. The
   desktop breakpoint reverts every one of these to normal table layout. */
.table-wrap {
  overflow: hidden;             /* clip the stacked rows' square corners to the radius */
}

.table-wrap thead {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.table-wrap tr {
  display: block;
  padding: var(--gap-control);
  border-bottom: 1px solid var(--border);
}

.table-wrap tbody tr:last-child {
  border-bottom: 0;
}

.table-wrap td {
  display: flex;
  gap: var(--gap-stack);
  justify-content: space-between;
  align-items: baseline;
  padding: var(--space-1) 0;
  border: 0;
  text-align: right;
}

/* The header text, stamped onto each cell, becomes the left-hand label. */
.table-wrap td::before {
  content: attr(data-label);
  color: var(--muted);
  font-size: 12px;
  font-weight: var(--weight-med);
  text-transform: uppercase;
  letter-spacing: 0;
  text-align: left;
}

/* First cell is the row title: full width, no label, larger. */
.table-wrap td:first-child {
  justify-content: flex-start;
  margin-bottom: var(--space-1);
  font-size: 15px;
  font-weight: var(--weight-med);
  text-align: left;
}

.table-wrap td:first-child::before {
  display: none;
}

th,
td {
  padding: var(--gap-control);
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
  font-size: 14px;
}

tr:last-child td {
  border-bottom: 0;
}

th {
  background: var(--surface-alt);
  color: var(--muted);
  font-size: 12px;
  font-weight: var(--weight-med);
  letter-spacing: 0;
  text-transform: uppercase;
}

.stack {
  display: grid;
  gap: var(--gap-stack);
  max-width: 640px;
}

label,
fieldset {
  display: grid;
  gap: var(--gap-inline);
  color: var(--text);
  font-size: 14px;
  font-weight: var(--weight-med);
}

fieldset {
  padding: var(--pad-compact);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

input,
select,
textarea {
  width: 100%;
  min-height: 40px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: var(--weight-reg);
  transition: border-color var(--dur-state) var(--ease), box-shadow var(--dur-state) var(--ease);
}

textarea {
  padding: 10px 12px;
}

/* Geist input: border steps to strong gray on hover, accent ring on focus */
input:hover,
select:hover,
textarea:hover {
  border-color: var(--border-strong);
}

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}

input::placeholder,
textarea::placeholder {
  color: var(--muted-soft);
}

textarea {
  resize: vertical;
}

.field-grid,
.detail-grid,
.mapper {
  display: grid;
  gap: var(--gap-stack);
}

.field-grid {
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}

/* Mobile-first: one column (plan/content first, panel below). The desktop
   breakpoint splits them into content + a 340px side panel. */
.detail-grid,
.mapper {
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

.auth-panel {
  max-width: 420px;
  margin: 12vh auto 0;
}

/* Login brand lockup: logo mark + REVEAL wordmark, matching the topbar brand. */
.auth-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-inline);
  margin-bottom: var(--gap-control);
}

.auth-logo {
  display: block;
  width: 32px;
  height: 32px;
}

.auth-word {
  font-size: 16px;
  font-weight: var(--weight-semi);
  letter-spacing: 0.12em;
  line-height: 1;
}

/* One-line pitch above the sign-in heading. */
.auth-intro {
  margin-bottom: var(--gap-section);
  color: var(--muted);
  font-size: 15px;
  line-height: 1.5;
  text-wrap: pretty;
}

.flash {
  padding: 10px 14px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font-size: 14px;
}

.flash.notice {
  background: var(--success-bg);
  color: var(--success-text);
  border-color: var(--success-text);
}

.flash.alert,
.error-list {
  background: var(--danger-bg);
  color: var(--danger-text);
}

.error-list {
  border: 1px solid var(--danger-border);
  border-radius: var(--radius-md);
  padding: var(--gap-control) var(--gap-stack);
}

/* ---- Mapper "Save door" action ----
   On phone the form is long (room, category, type, name, rating, hardware, note),
   so the submit sticks to the bottom of the viewport above the tab bar — always
   thumb-reachable without scrolling to the end of the panel. The action sits in a
   surface bar with a top hairline so it reads as pinned chrome, not loose in the
   scroll. Desktop returns it to normal flow under the last field. */
.form-actions {
  position: sticky;
  bottom: calc(var(--bottom-nav-h) + var(--safe-bottom));
  z-index: var(--z-sticky);
  margin: var(--gap-stack) calc(-1 * var(--gutter)) 0;
  padding: var(--gap-control) var(--gutter);
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.form-actions input[type="submit"],
.form-actions .button {
  width: 100%;
}

/* Phone base: the plan goes full-bleed (negative gutter) with square corners so
   the pinnable surface gets every pixel of a narrow screen. Desktop re-insets it
   into its framed column. */
.plan-frame {
  overflow: auto;
  margin-inline: calc(-1 * var(--gutter));
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: var(--gap-control);
}

.plan-surface {
  position: relative;
  min-height: 560px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: crosshair;
  /* Let the browser handle native pinch-zoom; a single primary tap still places
     a pin (pin-picker ignores non-primary pointers). */
  touch-action: pinch-zoom;
}

.plan-media {
  display: block;
  width: 100%;
  min-height: 560px;
  border: 0;
  object-fit: contain;
}

iframe.plan-media {
  height: 720px;
}

.plan-placeholder {
  display: grid;
  min-height: 560px;
  place-items: center;
  color: var(--muted);
}

/* Warning shown when a re-render failed but the previous page is still usable. */
.plan-notice {
  margin: var(--space-2) 0 0;
  padding: var(--space-2) var(--space-3);
  font-size: 14px;
  color: var(--danger);
  background: var(--danger-soft, transparent);
  border: 1px solid var(--danger);
  border-radius: var(--radius-1, 0);
}

/* ---- Pin: round marker. Danger = placed, accent = preview. ---- */
.pin {
  position: absolute;
  z-index: 2;
  width: 34px;
  height: 34px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--background-100);
  border-radius: 50%;
  background: var(--danger);
  color: var(--background-100);
  box-shadow: var(--shadow-card);
  font-size: 0;
}

.pin::after {
  content: "";
  position: absolute;
  inset: 10px;
  border-radius: 50%;
  background: var(--background-100);
}

.pin.preview {
  background: var(--accent);
  pointer-events: none;
}

.timeline {
  display: grid;
  gap: var(--gap-control);
}

.log-entry header,
.log-entry footer {
  display: flex;
  justify-content: space-between;
  gap: var(--gap-control);
  color: var(--muted);
  font-size: 13px;
}

/* Mobile-first definition list: label stacked above value so long values (opening
   IDs, room names) never get clipped by a fixed label column. Desktop restores the
   two-column "label | value" grid. */
dl {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gap-tight) var(--gap-stack);
  font-size: 14px;
}

dl > dd + dt {
  margin-top: var(--gap-control);
}

dt {
  color: var(--muted);
}

dd {
  margin: 0;
}

/* ---- Phone base: field-tech ergonomics ----
   Phone is the default target, so these are base rules, not overrides. Thumb-sized
   tap targets (iOS HIG floor is 44px), a full-bleed plan, a roomier pin that's easy
   to hit on glass, and full-width action buttons. The desktop block below tightens
   them back to the denser pointer-driven sizes. */
.button,
button,
input[type="submit"],
input,
select,
textarea {
  min-height: 44px;
}

.pin {
  width: 40px;
  height: 40px;
}

.actions .button,
.actions button,
.actions form {
  flex: 1;
}


/* ---- Dashboard shell ----
   Mobile-first: one column, the sidebar hidden (the fixed bottom tab bar carries
   navigation on phone). At the desktop breakpoint the shell becomes a two-column
   row and the sidebar returns. Defining "hidden" as the base state — rather than
   overriding a desktop `display:flex` inside a max-width query — is what keeps the
   sidebar reliably gone on phone; a trailing base rule can't win the cascade and
   re-show it. */
.shell {
  width: min(var(--container), calc(100vw - 2 * var(--gutter)));
  margin: 0 auto;
}

.sidebar {
  display: none;
}

/* ---- Bottom tab bar (phone navigation) ----
   Fixed to the viewport bottom, above the device safe area. Four equal tabs with
   an icon over a short label. Hidden at the desktop breakpoint, where the sidebar
   takes over. */
.bottom-nav {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--z-bottom-nav);
  display: flex;
  height: calc(var(--bottom-nav-h) + var(--safe-bottom));
  padding-bottom: var(--safe-bottom);
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.bottom-nav-link {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 0;
  color: var(--muted);
  font-size: 11px;
  font-weight: var(--weight-med);
  text-decoration: none;
}

.bottom-nav-link .icon {
  width: 22px;
  height: 22px;
  vertical-align: 0;
}

.bottom-nav-link:hover {
  color: var(--text);
  text-decoration: none;
}

.bottom-nav-link.is-active {
  color: var(--accent);
}

/* The "More" tab is a <button>; strip the button chrome so it matches the links. */
button.bottom-nav-link,
button.bottom-nav-link:hover {
  height: auto;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  letter-spacing: 0;
}

button.bottom-nav-link:hover {
  color: var(--text);
}

/* ---- "More" sheet ----
   A native <dialog> that slides up from the bottom, holding the secondary and
   admin destinations that don't fit the three-tab bar. Native <dialog> puts it in
   the top layer, so it clears the fixed bottom-nav's stacking context and gets a
   backdrop + Escape-to-close for free. */
.more-sheet {
  position: fixed;
  inset: auto 0 0 0;         /* pin to the bottom edge, full width */
  width: 100%;
  max-width: 100%;
  max-height: 85vh;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
}

.more-sheet[open] {
  animation: sheet-rise var(--dur-popover) var(--ease);
}

@keyframes sheet-rise {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .more-sheet[open] {
    animation: none;
  }
}

.more-sheet::backdrop {
  background: var(--gray-alpha-700);
}

.more-sheet-panel {
  display: flex;
  flex-direction: column;
  gap: var(--gap-control);
  padding: var(--gap-stack) var(--gutter) calc(var(--gap-stack) + var(--safe-bottom));
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.more-sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.more-sheet-head h2 {
  margin: 0;
}

.more-sheet-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  min-height: 40px;
  padding: 0;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
}

.more-sheet-close:hover {
  border-color: var(--border-strong);
  background: var(--surface);
  color: var(--text);
}

.more-sheet-links {
  display: flex;
  flex-direction: column;
}

.more-sheet-link {
  display: flex;
  align-items: center;
  gap: var(--gap-control);
  width: 100%;
  min-height: 52px;
  padding: var(--gap-control);
  border: 0;
  border-radius: 0;
  background: var(--surface);
  color: var(--text);
  font-size: 16px;
  font-weight: var(--weight-med);
  text-align: left;
  text-decoration: none;
  letter-spacing: 0;
  justify-content: flex-start;
}

.more-sheet-link + .more-sheet-link {
  border-top: 1px solid var(--border);
}

.more-sheet-link:hover {
  background: var(--surface-alt);
  color: var(--text);
  text-decoration: none;
}

.more-sheet-link .icon {
  width: 20px;
  height: 20px;
  color: var(--muted);
}

/* Sign out reads as a destructive-ish action, set apart from the nav links. */
.more-sheet-signout {
  border: 1px solid var(--border);
  color: var(--danger);
  justify-content: center;
}

.more-sheet-signout:hover {
  border-color: var(--danger-border);
  background: var(--danger-bg);
  color: var(--danger);
}

.more-sheet-signout .icon {
  color: var(--danger);
}

/* One nav, full height: contextual links flow from the top, the persistent
   foot block is pushed to the bottom with margin-top:auto. */
.sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: var(--gap-tight);
  flex: 1;
}

.sidebar-foot {
  display: flex;
  flex-direction: column;
  gap: var(--gap-tight);
  margin-top: auto;
  padding-top: var(--gap-tight);
  border-top: 1px solid var(--border);
}

/* KB article links sit under their section label — indent to read as a group. */
.sidebar-link.is-sub {
  padding-left: calc(var(--gap-control) + var(--space-3));
}

.sidebar-link {
  display: flex;
  align-items: center;
  gap: var(--gap-inline);
  padding: var(--space-2) var(--gap-control);
  color: var(--text);
  font-weight: var(--weight-med);
  border-left: 2px solid transparent;
}

/* Icons read as quiet secondary marks; they lift with the link on hover/active */
.sidebar-link .icon {
  width: 16px;
  height: 16px;
  color: var(--muted);
  transition: color var(--dur-state) var(--ease);
}

.sidebar-link:hover {
  background: var(--surface-alt);
  text-decoration: none;
  color: var(--text);
}

.sidebar-link:hover .icon {
  color: var(--text);
}

.sidebar-link.is-active {
  background: var(--surface-alt);
  border-left-color: var(--accent);
  color: var(--accent);
}

.sidebar-link.is-active .icon {
  color: var(--accent);
}

.sidebar-back {
  display: flex;
  align-items: center;
  gap: var(--gap-tight);
  padding: var(--space-1) var(--gap-control);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 12px;
}

.sidebar-back .icon {
  width: 14px;
  height: 14px;
}

.sidebar-back:hover {
  color: var(--text);
  text-decoration: none;
}

.sidebar-context {
  margin: var(--space-2) 0;
  padding: 0 var(--gap-control);
  color: var(--muted);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ---- Contextual nav strip (phone) ----
   The per-facility links that live in the sidebar on desktop, shown as a
   horizontal scrollable strip at the top of the page on phone. Hidden at the
   desktop breakpoint. */
.context-nav {
  display: flex;
  gap: var(--gap-inline);
  margin: calc(-1 * var(--gap-section)) calc(-1 * var(--gutter)) 0;
  padding: var(--gap-inline) var(--gutter);
  overflow-x: auto;
  border-bottom: 1px solid var(--border);
  scrollbar-width: none;            /* the strip scrolls; the scrollbar is noise */
  -webkit-overflow-scrolling: touch;
}

.context-nav::-webkit-scrollbar {
  display: none;
}

.context-link {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-tight);
  flex: none;
  padding: var(--space-2) var(--gap-control);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text);
  font-size: 14px;
  font-weight: var(--weight-med);
  white-space: nowrap;
}

.context-link .icon {
  width: 16px;
  height: 16px;
  color: var(--muted);
}

.context-link:hover {
  text-decoration: none;
  border-color: var(--border-strong);
  color: var(--text);
}

.context-link.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-action);
}

.context-link.is-active .icon {
  color: var(--on-action);
}

/* ---- Openings category tabs ---- */
.tabs {
  display: flex;
  gap: var(--gap-tight);
  border-bottom: 1px solid var(--border);
}

.tab {
  padding: var(--space-2) var(--gap-control);
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font: inherit;
  font-weight: var(--weight-med);
  cursor: pointer;
}

.tab.is-active {
  color: var(--text);
  border-bottom-color: var(--accent);
}

.tab-panel { display: none; }
.tab-panel.is-active { display: block; }

/* ---- Lucide icons (rails_icons): inherit color, align to text ---- */
.icon {
  flex: none;
  width: 20px;               /* default size; override per-call with width:/height: */
  height: 20px;
  vertical-align: -0.18em;   /* sit on the text baseline when inline */
}

/* ---- Floor plan icon (Levels table) ---- */
.plan-icon {
  display: inline-flex;
  color: var(--accent);
}

.plan-icon .icon {
  width: 16px;
  height: 16px;
}

.plan-icon:hover {
  color: var(--accent-strong);
}

/* ============================================================
   Knowledge base
   ============================================================ */

/* ---- Table of contents (landing page) ---- */
.kb-toc {
  display: flex;
  flex-direction: column;
  gap: var(--gap-section);
}

.kb-section {
  border: 1px solid var(--border);
  background: var(--surface);
  padding: var(--pad-card);
}

.kb-section h2 {
  margin: 0 0 var(--space-2);
}

.kb-section-desc {
  color: var(--muted);
  margin: 0 0 var(--space-4);
}

.kb-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.kb-list li {
  display: flex;
  align-items: center;
  gap: var(--gap-inline);
  padding: var(--space-2) 0;
  border-top: 1px solid var(--border);
}

.kb-list li:first-child {
  border-top: 0;
}

/* ---- Draft badge (admin-only) ---- */
.kb-draft {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: var(--weight-med);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 1px var(--space-2);
  vertical-align: middle;
}

/* ---- Article prose ---- */
.kb-prose {
  max-width: 70ch;
}

.kb-prose > * + * {
  margin-top: var(--gap-stack);
}

.kb-prose h2 {
  margin-top: var(--gap-section);
}

.kb-prose h3 {
  margin-top: var(--gap-stack);
}

.kb-prose ul,
.kb-prose ol {
  padding-left: var(--space-6);
}

.kb-prose li + li {
  margin-top: var(--space-1);
}

.kb-prose a {
  color: var(--accent);
}

.kb-prose code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  padding: 1px var(--space-1);
}

.kb-prose pre {
  font-family: var(--font-mono);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  padding: var(--pad-compact);
  overflow-x: auto;
}

.kb-prose pre code {
  border: 0;
  background: none;
  padding: 0;
}

.kb-prose blockquote {
  border-left: 2px solid var(--border);
  padding-left: var(--gap-control);
  color: var(--muted);
  margin-left: 0;
}

.kb-prose table {
  border-collapse: collapse;
  width: 100%;
}

.kb-prose th,
.kb-prose td {
  border: 1px solid var(--border);
  padding: var(--space-2) var(--gap-control);
  text-align: left;
}

.kb-prose th {
  background: var(--surface-alt);
  font-weight: var(--weight-med);
}

.kb-back {
  margin-top: var(--gap-section);
}

/* ---- Admin authoring helpers ---- */
.label-hint {
  color: var(--muted-soft);
  font-weight: var(--weight-reg);
}

.mono-input {
  font-family: var(--font-mono);
}

label.check {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--gap-inline);
}

label.check input {
  width: auto;
  min-height: 0;
}

.row-actions {
  display: flex;
  gap: var(--gap-control);
  align-items: center;
}

.link-button {
  background: none;
  border: 0;
  padding: 0;
  color: var(--accent);
  font: inherit;
  cursor: pointer;
  min-height: 0;
}

.link-button:hover {
  color: var(--accent-strong);
  text-decoration: underline;
}

/* ---- Settings: stacked preference cards ---- */
.settings {
  display: flex;
  flex-direction: column;
  gap: var(--gap-layout);
  max-width: 560px;
}

.settings-group {
  display: flex;
  flex-direction: column;
  gap: var(--gap-control);
  padding: var(--gap-layout);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.settings-head h2 {
  margin: 0;
}

.settings-head .muted {
  margin: var(--space-1) 0 0;
  font-size: 14px;
}

/* Theme picker: radios laid out as a row of options */
.theme-options {
  display: flex;
  gap: var(--gap-tight);
}

.theme-option {
  display: flex;
  align-items: center;
  gap: var(--gap-inline);
  padding: var(--space-2) var(--gap-control);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
}

.theme-option:has(:checked) {
  border-color: var(--accent);
  color: var(--accent);
}

/* Pagination — Newer/Older links under long tables (facility logs). */
.pagination {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* ============================================================
   Desktop enhancement (≥ 768px)
   Phone is the base above; here the layout grows up: the sidebar returns, the
   bottom tab bar and phone-only contextual strip drop away, grids regain their
   second column, tables become real tables again, and the sticky save unsticks.
   ============================================================ */
@media (min-width: 768px) {
  .bottom-nav,
  .context-nav {
    display: none;
  }

  /* Shell becomes the two-column sidebar + content row. */
  .shell {
    display: flex;
    align-items: flex-start;
    gap: var(--gap-layout);
  }

  .sidebar {
    position: sticky;
    top: var(--space-4);
    display: flex;
    flex-direction: column;
    flex: 0 0 220px;
    min-height: calc(100vh - var(--space-16));
    padding: var(--gap-layout) 0;
  }

  .shell .page {
    flex: 1;
  }

  /* Roomy desktop footer instead of bottom-nav clearance. */
  .page {
    padding: var(--pad-hero) 0 var(--space-16);
  }

  /* The session block (name + sign-out) returns to the header. */
  .session {
    display: flex;
  }

  /* Page header back to title-left / actions-right; actions size to content. */
  .page-header {
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--gap-section);
  }

  .actions {
    flex-wrap: wrap;
  }

  .actions > .button,
  .actions > form,
  .actions > .is-disabled {
    flex: 0 0 auto;
  }

  /* Denser pointer-driven control height (Geist default). */
  .button,
  button,
  input[type="submit"],
  input,
  select,
  textarea {
    min-height: 40px;
  }

  .pin {
    width: 34px;
    height: 34px;
  }

  /* Plan returns inside its framed column. */
  .plan-frame {
    margin-inline: 0;
    border-radius: var(--radius-md);
  }

  /* Larger Geist heading scale. */
  h1 {
    font-size: 32px;
    line-height: 40px;
    letter-spacing: -1.28px;
  }

  h2 {
    font-size: 24px;
    line-height: 32px;
    letter-spacing: -0.96px;
  }

  h3 {
    font-size: 20px;
    line-height: 26px;
    letter-spacing: -0.4px;
  }

  /* Two-column content + side panel. */
  .detail-grid,
  .mapper {
    grid-template-columns: minmax(0, 1fr) 340px;
  }

  /* Definition list back to "label | value". */
  dl {
    grid-template-columns: 140px 1fr;
    gap: var(--gap-inline) var(--gap-stack);
  }

  dl > dd + dt {
    margin-top: 0;
  }

  /* Save action unsticks: normal flow under the last field. */
  .form-actions {
    position: static;
    margin: 0;
    padding: 0;
    background: none;
    border-top: 0;
  }

  .form-actions input[type="submit"],
  .form-actions .button {
    width: auto;
  }

  /* ---- Tables become real tables again ---- */
  .table-wrap {
    overflow-x: auto;
  }

  .table-wrap thead {
    position: static;
    width: auto;
    height: auto;
    clip: auto;
  }

  .table-wrap tr,
  .table-wrap td {
    display: table-row;
  }

  .table-wrap td {
    display: table-cell;
    padding: var(--gap-control);
    border-bottom: 1px solid var(--border);
    text-align: left;
    font-size: 14px;
    font-weight: var(--weight-reg);
  }

  .table-wrap td:first-child {
    margin-bottom: 0;
    font-size: 14px;
    font-weight: var(--weight-reg);
  }

  .table-wrap td::before {
    display: none;
  }

  .table-wrap tr {
    padding: 0;
    border-bottom: 0;
  }

  .table-wrap tbody tr:last-child td {
    border-bottom: 0;
  }
}
