/* ============================================================
   Skelly shared stylesheet — tokens, base rules and shared
   components for every Skelly page: the dashboard templates link
   it (/static/skelly.css) and the Lister imports it from
   lister/web/app/globals.css into Tailwind's components layer.
   Page-specific rules stay in each page.
   ============================================================ */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/inter-latin.woff2") format("woff2");
}
/* JetBrains Mono (OFL, app/static/fonts/JetBrainsMono-OFL.txt): labels, chips, SKUs and IDs look the same on
   every OS instead of SF Mono / Consolas / DejaVu. Latin subset, ligatures removed so SKUs and API examples
   show the characters typed; dotted zero tells 0 from O. */
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/static/fonts/jetbrains-mono-latin.woff2") format("woff2");
}

:root {
  color-scheme: dark;
  --app-bg: #0c0f13;
  --surface: #161b22;
  --surface-raised: #1c2430;
  --input: #0a0d11;
  --border: #262d36;
  --border-strong: #333c47;
  --text: #e6edf3;
  --text-secondary: #9fb0bd;
  --text-tertiary: #8797a4;
  --accent: #4c9aff;
  --accent-hover: #79b8ff;
  --accent-soft: rgba(76, 154, 255, .12);
  --accent-border: rgba(76, 154, 255, .45);
  --focus: 0 0 0 3px rgba(76, 154, 255, .28);

  /* Status: the only status colours anywhere. Info = the accent family. */
  --success: #3fb950;
  --success-text: #56d364;
  --success-soft: rgba(86, 211, 100, .1);
  --success-border: rgba(86, 211, 100, .45);
  --warning: #f0b429;
  --warning-text: #f0b429;
  --warning-soft: rgba(240, 180, 41, .1);
  --warning-border: rgba(240, 180, 41, .45);
  --danger: #f85149;
  --danger-text: #ff7b72;
  --danger-soft: rgba(248, 81, 73, .1);
  --danger-border: rgba(248, 81, 73, .45);
  --drift-soft: rgba(248, 81, 73, .12);

  /* Type scale: nothing smaller than 11px. */
  --fs-xs: 11px;   /* labels, badges, table headers, chips */
  --fs-sm: 12px;   /* help/secondary text, dense table cells */
  --fs-base: 13px; /* body, inputs, buttons */
  --fs-md: 14px;   /* section + page titles */
  --fs-lg: 16px;   /* page headings */
  --fs-xl: 20px;   /* stat numbers */
  --font: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "Cascadia Code", Consolas, monospace;

  --radius: 4px;
  --radius-lg: 8px;
  --radius-pill: 999px;

  /* Chat keeps its own 120+. */
  --z-sticky: 20;
  --z-header: 40;
  --z-modal: 50;
  --z-overlay: 100;
}

/* ---------- base ---------- */
* { box-sizing: border-box; scrollbar-color: var(--border-strong) var(--app-bg); scrollbar-width: thin; }
html { background: var(--app-bg); }
body { margin: 0; min-height: 100vh; background: var(--app-bg); color: var(--text); font: var(--fs-base)/1.45 var(--font); -webkit-font-smoothing: antialiased; }
::selection { background: var(--accent); color: var(--app-bg); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
a { color: var(--accent); text-underline-offset: .16em; }
a:hover { color: var(--accent-hover); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--app-bg); }
::-webkit-scrollbar-thumb { border: 2px solid var(--app-bg); border-radius: var(--radius-pill); background: var(--border-strong); }
::-webkit-scrollbar-thumb:hover { background: var(--text-tertiary); }
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
[hidden] { display: none !important; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* ---------- header ---------- */
/* Logo, app tabs, optional chips row, right group. The chips drop to their own row when the
   header gets narrow; on phones the tabs get a row too. */
.topbar { position: relative; z-index: var(--z-header); display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto; align-items: center; gap: .5rem .6rem; padding: .5rem 1rem; border-bottom: 1px solid var(--border); background: rgba(12, 15, 19, .96); backdrop-filter: blur(10px); }
.topbar > .brand-block { grid-row: 1; grid-column: 1; }
.topbar > .app-tabs { grid-row: 1; grid-column: 2; justify-self: start; }
.topbar > .status-strip { grid-row: 1; grid-column: 3; }
.topbar > .topbar-end { grid-row: 1; grid-column: 4; justify-self: end; }
.topbar-end { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: .3rem .4rem; }
/* The current app's page links (Inventory, Settings, …); the active one carries aria-current="page". */
.topbar-pages { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: .25rem; }
.topbar-link { display: inline-flex; align-items: center; min-height: 2rem; padding: .5rem .65rem; border: 1px solid transparent; border-radius: var(--radius); color: var(--text-tertiary); font: 650 var(--fs-sm)/1 var(--font); text-decoration: none; white-space: nowrap; transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease; }
.topbar-link:hover { background: rgba(255, 255, 255, .04); color: var(--text); }
.topbar-link[aria-current="page"] { border-color: var(--border-strong); background: rgba(255, 255, 255, .04); color: var(--text); }
.topbar-divider { flex: 0 0 auto; width: 1px; height: 1.25rem; margin: 0 .2rem; background: var(--border-strong); }

.brand-block { display: inline-flex; flex: 0 0 44px; width: 44px; height: 44px; align-items: center; justify-content: center; border-radius: var(--radius); text-decoration: none; }
.brand-mark { display: block; width: 44px; height: 44px; object-fit: contain; transition: filter 160ms ease; }
.brand-block:hover .brand-mark, .brand-block:focus-visible .brand-mark { filter: brightness(1.08) drop-shadow(0 0 4px rgba(255, 255, 255, .9)) drop-shadow(0 0 12px rgba(255, 255, 255, .55)); }

/* App tabs (partials/app_tabs.html, components/app-tabs.tsx). */
.app-tabs { display: inline-flex; gap: .18rem; padding: .2rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--input); }
.app-tabs a { display: inline-flex; align-items: center; min-height: 1.85rem; padding: .3rem .66rem; border-radius: 3px; color: var(--text-tertiary); font: 650 var(--fs-sm)/1 var(--font); text-decoration: none; white-space: nowrap; }
.app-tabs a:hover { background: rgba(255, 255, 255, .04); color: var(--text); }
.app-tabs a[aria-current="page"], .app-tabs a[aria-current="true"] { background: var(--surface-raised); box-shadow: inset 0 0 0 1px var(--border-strong); color: var(--text); }
/* Rentals' pages live in a menu dropping down from its tab only; the strip never grows. */
.app-tabs .tab-group { position: relative; display: inline-flex; align-items: center; }
.app-tabs .tab-parent::after { content: ""; margin-left: .42rem; border-top: 4px solid currentColor; border-right: 3px solid transparent; border-left: 3px solid transparent; opacity: .7; }
.app-tabs .tab-menu { position: absolute; top: 100%; left: 0; z-index: var(--z-overlay); display: none; flex-direction: column; gap: .18rem; min-width: 9rem; margin-top: .32rem; padding: .2rem; border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--input); box-shadow: 0 10px 28px rgba(0, 0, 0, .5); }
/* Bridges the gap under the tab so the menu survives the pointer moving onto it. */
.app-tabs .tab-menu::before { content: ""; position: absolute; top: -.5rem; right: 0; left: 0; height: .5rem; }
.app-tabs .tab-group:hover .tab-menu, .app-tabs .tab-group:focus-within .tab-menu { display: flex; }

/* ---------- status chips ---------- */
/* An accordion: the toggle (worst-state dot, "Sources", issue count, chevron) shows/hides the chips.
   Collapsed by default; the choice persists per browser in localStorage "skelly.statusStrip.open",
   shared by the dashboard (partials/topbar.html) and the Lister (components/status-strip.tsx).
   Open, the chips sit on one line and scroll sideways when they don't fit (scrollbar hidden). */
.status-strip { display: flex; min-width: 0; align-items: center; gap: .5rem; }
/* flex: 1 so the fade-out mask lands on the strip's empty end, not on the last chip, unless the chips overflow. */
.status-chips { display: flex; flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; align-items: center; gap: .5rem; overflow-x: auto; scrollbar-width: none; mask-image: linear-gradient(to right, #000 calc(100% - 1.25rem), transparent); animation: status-chips-in 140ms ease-out; }
.status-chips::-webkit-scrollbar { display: none; }
@keyframes status-chips-in { from { opacity: 0; transform: translateX(-.4rem); } }
.status-toggle { flex: 0 0 auto; }
.status-chevron { width: .38rem; height: .38rem; margin: 0 .12rem 0 .05rem; border-right: 1.5px solid var(--text-tertiary); border-bottom: 1.5px solid var(--text-tertiary); transform: rotate(-45deg); transition: transform 140ms ease; }
.status-toggle[aria-expanded="true"] .status-chevron { transform: rotate(135deg); }
/* Open and sharing a row with the chips, the toggle shrinks to its chevron so the chips keep the room.
   (681–1100px the chips get their own row, so the toggle keeps its label.) */
@media (min-width: 1101px), (max-width: 680px) {
  .status-toggle[aria-expanded="true"] > :not(.status-chevron) { display: none; }
}
/* Proxy/Odoo (infrastructure) split from the store chips by a divider. */
.infrastructure-status { display: inline-flex; flex: 0 0 auto; flex-wrap: nowrap; align-items: center; gap: .25rem; padding-right: .375rem; border-right: 3px solid var(--border-strong); }
.account-status { display: inline-flex; flex: 0 0 auto; flex-wrap: nowrap; align-items: center; gap: .25rem; }
/* States: ok | warn | bad | disabled | pending.
   Chips are buttons when clicking tests the connection, spans when they only report a state. */
.source-status { display: inline-flex; min-width: 0; align-items: center; justify-content: flex-start; gap: .3rem; min-height: 1.72rem; padding: .3rem .35rem; border: 1px solid var(--border); border-radius: 3px; background: transparent; color: var(--text); font: 400 var(--fs-xs)/1.2 var(--mono); text-transform: none; white-space: nowrap; cursor: pointer; transition: background-color 120ms ease, border-color 120ms ease; }
button.source-status:hover:not(:disabled) { border-color: var(--border-strong); background: rgba(255, 255, 255, .04); color: var(--text); }
span.source-status { cursor: default; }
.source-status:disabled { cursor: wait; opacity: 1; }
.source-status .dot { width: .52rem; height: .52rem; flex: 0 0 auto; border-radius: 50%; background: var(--text-tertiary); }
.source-status.ok { border-color: var(--success-border); }
.source-status.ok .dot { background: var(--success); }
.source-status.warn { border-color: var(--warning-border); }
.source-status.warn .dot { background: var(--warning); }
.source-status.bad { border-color: var(--danger-border); }
.source-status.bad .dot { background: var(--danger); }
.source-status.disabled { border-color: var(--border); }
.source-status.disabled .dot { background: var(--text-tertiary); }
.source-status.pending { border-color: var(--accent); }
.source-status.pending .dot { background: var(--accent); animation: status-pulse .8s ease-in-out infinite alternate; }
@keyframes status-pulse { to { transform: scale(.62); opacity: .5; } }
.source-name { color: var(--text); font-size: var(--fs-xs); font-weight: 650; letter-spacing: .01em; }
.source-result { max-width: 15rem; overflow: hidden; color: var(--text-tertiary); font-size: var(--fs-xs); text-overflow: ellipsis; }
.source-result:empty { display: none; }
/* A healthy chip says so with its green dot; the word ("connected") stays for screen readers only. */
.source-status.ok .source-result { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---------- buttons ---------- */
.btn,
:where(body.dash) button,
:where(body.dash) .button { display: inline-flex; align-items: center; justify-content: center; gap: .3rem; min-height: 2rem; padding: .5rem .7rem; border: 1px solid var(--border-strong); border-radius: var(--radius); background: transparent; color: var(--text); font: 650 var(--fs-base)/1 var(--font); text-decoration: none; white-space: nowrap; cursor: pointer; transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease; }
.btn:hover:not(:disabled),
:where(body.dash) button:hover:not(:disabled),
:where(body.dash) .button:hover { border-color: var(--text-tertiary); background: rgba(255, 255, 255, .04); color: var(--text); }
.btn:active:not(:disabled),
:where(body.dash) button:active:not(:disabled),
:where(body.dash) .button:active { background: rgba(255, 255, 255, .07); }
.btn:disabled,
:where(body.dash) button:disabled { cursor: not-allowed; opacity: .5; }
.btn-primary { border-color: var(--accent); background: var(--accent); color: var(--app-bg); }
.btn-primary:hover:not(:disabled) { border-color: var(--accent-hover); background: var(--accent-hover); color: var(--app-bg); }
.btn-danger { border-color: var(--danger-border); color: var(--danger-text); }
.btn-danger:hover:not(:disabled) { border-color: var(--danger); background: var(--danger-soft); color: var(--danger-text); }
.btn-ghost { border-color: transparent; background: transparent; color: var(--text-tertiary); }
.btn-ghost:hover:not(:disabled) { border-color: transparent; background: rgba(255, 255, 255, .04); color: var(--text); }
.btn-sm { min-height: 1.75rem; padding: .3rem .5rem; font-size: var(--fs-sm); }
.icon-btn { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; width: 28px; min-width: 28px; height: 28px; min-height: 28px; padding: 0; border: 1px solid transparent; border-radius: var(--radius); background: transparent; color: var(--text-secondary); cursor: pointer; transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease; }
.icon-btn:hover:not(:disabled) { border-color: var(--border-strong); background: rgba(255, 255, 255, .04); color: var(--text); }
.icon-btn:disabled { cursor: not-allowed; opacity: .5; }
.icon-btn svg { display: block; width: 16px; height: 16px; flex: 0 0 auto; }

/* ---------- forms ---------- */
.input,
:where(body.dash) input:where(:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="hidden"], [type="button"], [type="submit"], [type="reset"], [type="image"])),
:where(body.dash) select,
:where(body.dash) textarea { min-height: 2.25rem; padding: .5rem .6rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--input); color: var(--text); font: var(--fs-base) var(--font); caret-color: var(--accent); }
.input:focus,
:where(body.dash) input:focus,
:where(body.dash) select:focus,
:where(body.dash) textarea:focus { border-color: var(--accent); outline: none; box-shadow: var(--focus); }
.input::placeholder,
:where(body.dash) input::placeholder,
:where(body.dash) textarea::placeholder { color: var(--text-tertiary); opacity: 1; }
/* A required field that is still empty: light red until it is filled. */
.missing-required:not(:disabled) { background: color-mix(in srgb, var(--danger) 16%, transparent); }
.missing-required:not(:disabled):not(:focus) { border-color: color-mix(in srgb, var(--danger) 60%, transparent); }
.control-label { display: block; margin-bottom: .3rem; color: var(--text-secondary); font: 650 var(--fs-xs)/1.2 var(--mono); letter-spacing: .04em; text-transform: uppercase; }
.field-help { margin: .25rem 0 0; color: var(--text-tertiary); font-size: var(--fs-sm); line-height: 1.4; }

/* ---------- badges ---------- */
.badge { display: inline-flex; align-items: center; gap: .28rem; padding: .18rem .38rem; border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--surface-raised); color: var(--text-secondary); font: 600 var(--fs-xs)/1.2 var(--font); letter-spacing: .02em; white-space: nowrap; }
.badge-neutral { border-color: var(--border-strong); background: var(--surface-raised); color: var(--text-secondary); }
.badge-success { border-color: var(--success-border); background: var(--success-soft); color: var(--success-text); }
.badge-warning { border-color: var(--warning-border); background: var(--warning-soft); color: var(--warning-text); }
.badge-danger { border-color: var(--danger-border); background: var(--danger-soft); color: var(--danger-text); }
.badge-accent { border-color: var(--accent-border); background: var(--accent-soft); color: var(--accent-hover); }

/* ---------- tables ---------- */
/* Locked header: the table scrolls INSIDE this bounded box, so the sticky thead stays pinned. */
.table-shell { width: 100%; max-height: calc(100dvh - 12rem); overflow: auto; border: 1px solid var(--border); border-radius: 3px; background: var(--surface); }
.table-shell table { width: 100%; min-width: 0; border-collapse: collapse; background: var(--surface); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.table-shell thead { position: sticky; top: 0; z-index: var(--z-sticky); background: var(--app-bg); box-shadow: 0 1px 0 var(--border-strong); }
.table-shell th, .table-shell td { padding: .48rem .55rem; border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); text-align: left; vertical-align: middle; }
.table-shell th:last-child, .table-shell td:last-child { border-right: 0; }
.table-shell tbody tr:last-child td { border-bottom: 0; }
.table-shell tbody tr:hover { background: rgba(255, 255, 255, .04); }
/* Opaque header cells so scrolled rows never show through the locked header. */
.table-shell th { position: relative; padding: .25rem .55rem; border-bottom-color: var(--border-strong); background: var(--app-bg); color: var(--text-secondary); font: 650 var(--fs-xs)/1.2 var(--mono); letter-spacing: .03em; text-transform: uppercase; user-select: none; }
.table-shell th.num, .table-shell td.num, .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Same-width digits for numbers in columns and fields. Buttons and inputs set `font`, which resets this. */
.table-shell :where(button, input, select), .input { font-variant-numeric: tabular-nums; }

/* ---------- banners ---------- */
.banner { margin: 0 0 .65rem; padding: .52rem .65rem; border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--surface); color: var(--text-secondary); font-size: var(--fs-sm); }
.banner.error { border-color: var(--danger-border); background: var(--danger-soft); color: var(--danger-text); }
.banner.warning { border-color: var(--warning-border); background: var(--warning-soft); color: var(--warning-text); }
.banner.success { border-color: var(--success-border); background: var(--success-soft); color: var(--success-text); }

/* ---------- cards ---------- */
.card { padding: 1rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.stat-tile { padding: .85rem 1rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.stat-label { color: var(--text-tertiary); font: 650 var(--fs-xs)/1.2 var(--mono); letter-spacing: .04em; text-transform: uppercase; }
.stat-value { margin-top: .2rem; color: var(--text); font-size: var(--fs-xl); font-weight: 650; font-variant-numeric: tabular-nums; }
.empty-state { padding: 2rem; color: var(--text-secondary); text-align: center; }
/* Placeholder shapes while data loads (tables, the editor). The global reduced-motion rule stops the pulse. */
.skeleton { display: block; border-radius: 3px; background: var(--border-strong); animation: skeleton-pulse 1.1s ease-in-out infinite alternate; }
@keyframes skeleton-pulse { from { opacity: .4; } to { opacity: .8; } }

/* ---------- responsive header ---------- */
@media (max-width: 1100px) {
  /* The toggle stays on row 1; the open chips take row 2. */
  .topbar > .status-strip { display: contents; }
  .topbar > .status-strip > .status-toggle { grid-row: 1; grid-column: 3; justify-self: start; }
  .topbar > .status-strip > .status-chips { grid-row: 2; grid-column: 1 / -1; }
}
/* Phones: logo + right group on row 1, tabs on row 2, Sources toggle and chips on row 3; everything a notch tighter
   so a 390px screen holds each row without sideways scrolling. */
@media (max-width: 680px) {
  .topbar { grid-template-columns: auto minmax(0, 1fr); gap: .5rem .6rem; padding: .5rem .75rem; }
  .topbar > .topbar-end { grid-row: 1; grid-column: 2; min-width: 0; }
  .topbar > .app-tabs { grid-row: 2; grid-column: 1 / -1; max-width: 100%; }
  .topbar > .status-strip { display: flex; grid-row: 3; grid-column: 1 / -1; }
  .topbar-end { gap: .3rem .25rem; }
  .topbar-link, .topbar-end .btn { padding-inline: .45rem; font-size: var(--fs-sm); }
  .topbar-divider { margin: 0 .1rem; }
  .app-tabs a { padding-inline: .5rem; }
}
