/*
 * Zeug — inventory for BUNTER BLOCK BUXTEHUDE.
 * Bootstrap 5.3 does the heavy lifting; this file carries the brand and the
 * handful of components Bootstrap has no answer for (colour swatches, the
 * size x colour matrix, the audit timeline).
 */

:root {
  --bb-cyan: #25b3d1;
  --bb-purple: #9b7ab8;
  --bb-pink: #e84c8a;
  --bb-orange: #f5a623;
  --bb-green: #8bc53f;
  --bb-red: #e53935;
  --bb-ink: #1d2233;

  --bb-gradient: linear-gradient(90deg, var(--bb-cyan), var(--bb-purple), var(--bb-pink), var(--bb-orange), var(--bb-green), var(--bb-red));

  --bs-body-font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --bs-primary: var(--bb-ink);
  --bs-link-color-rgb: 29, 34, 51;
  --bs-border-radius: 0.75rem;
  --bs-border-radius-lg: 1rem;
}

body {
  background-color: #f6f7fb;
  -webkit-font-smoothing: antialiased;
}

[data-bs-theme="dark"] body {
  background-color: #14161d;
}

/* ---------- Brand ---------- */

.text-bunter {
  background: var(--bb-gradient);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

.bunter-rule {
  height: 4px;
  border: 0;
  opacity: 1;
  background: var(--bb-gradient);
}

.brand-wordmark {
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
}

/* ---------- Actions ---------- */

/* `btn-dark` is the primary action everywhere. In dark mode Bootstrap paints it
   rgb(33,37,41) — the exact colour of the card behind it, 1.00:1. The button
   disappears and only its label remains, so the primary action reads as plain
   text. Inverting it keeps the idea intact (near-black on near-white becomes
   near-white on near-black) and puts the ink back where it can be read. */
[data-bs-theme="dark"] .btn-dark {
  --bs-btn-bg: #f6f7fb;
  --bs-btn-border-color: #f6f7fb;
  --bs-btn-color: var(--bb-ink);
  --bs-btn-hover-bg: #e2e4ee;
  --bs-btn-hover-border-color: #e2e4ee;
  --bs-btn-hover-color: var(--bb-ink);
  --bs-btn-active-bg: #cfd2e0;
  --bs-btn-active-border-color: #cfd2e0;
  --bs-btn-active-color: var(--bb-ink);
  --bs-btn-disabled-bg: #f6f7fb;
  --bs-btn-disabled-border-color: #f6f7fb;
  --bs-btn-disabled-color: var(--bb-ink);
  /* The ring has to be inverted with the rest. Bootstrap's rgb(66,70,73) is
     drawn at half opacity, which over the dark background lands on #2b2e33 and
     1.33:1 — the outer half of the ring is invisible, so a focus indicator only
     someone who already knew where it was could find. The inverted fill gives
     5.00:1 against the background behind it and 3.37:1 against the button it
     surrounds, so both edges of the ring stay readable. */
  --bs-btn-focus-shadow-rgb: 246, 247, 251;
}

/* ---------- Layout ---------- */

.app-shell {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.app-main {
  flex: 1 0 auto;
  padding-bottom: 5.5rem; /* room for the mobile tab bar */
}

@media (min-width: 992px) {
  .app-main { padding-bottom: 2rem; }
}

/* Bootstrap ships no min-w-* utilities, and a flex item defaults to
   `min-width: auto` — its automatic minimum is the width of its content, so a
   `text-truncate` child never gets to truncate and the row pushes the page
   wider than the phone instead. Every `flex-grow-1` column that wants to
   truncate needs this, so it is a utility rather than five one-off rules. */
.min-w-0 { min-width: 0; }

.page-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.page-header h1 {
  font-weight: 800;
  letter-spacing: -0.02em;
  margin: 0;
  font-size: clamp(1.5rem, 4vw, 2rem);
}

/* ---------- Cards ---------- */

.card {
  border: 1px solid rgba(29, 34, 51, 0.08);
  box-shadow: 0 1px 2px rgba(29, 34, 51, 0.04), 0 8px 24px -16px rgba(29, 34, 51, 0.25);
}

[data-bs-theme="dark"] .card {
  border-color: rgba(255, 255, 255, 0.08);
}

.card-link-plain {
  text-decoration: none;
  color: inherit;
  display: block;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.card-link-plain:hover,
.card-link-plain:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 2px 4px rgba(29, 34, 51, 0.06), 0 16px 32px -20px rgba(29, 34, 51, 0.4);
}

.stat-card .stat-value {
  font-size: clamp(1.6rem, 5vw, 2.25rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.1;
}

.stat-card .stat-label {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--bs-secondary-color);
}

/* ---------- Article images ---------- */

.article-thumb {
  aspect-ratio: 4 / 3;
  object-fit: cover;
  width: 100%;
  background: rgba(29, 34, 51, 0.04);
}

.article-thumb-placeholder {
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  background: var(--bb-gradient);
  opacity: 0.15;
}

.article-gallery img {
  aspect-ratio: 1;
  object-fit: cover;
  width: 100%;
  border-radius: var(--bs-border-radius);
}

/* ---------- Colour swatch ---------- */

.swatch {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  border: 1px solid rgba(29, 34, 51, 0.25);
  vertical-align: -0.15em;
  flex: none;
}

.swatch-lg { width: 1.5rem; height: 1.5rem; }

/* The swatch as a button: no chrome of its own, so it still reads as the dot it
   was, but with a real hit area and a focus ring. */
.swatch-button {
  padding: 0;
  appearance: none;
  cursor: pointer;
  position: relative;
}

/* The dot is 16 px, which is not a thumb. This grows the hit area to about
   36 px without growing the dot — making the dot itself bigger would change
   what the matrix looks like, and the dot is the thing being read. */
.swatch-button::before {
  content: "";
  position: absolute;
  inset: -0.6rem;
}

.swatch-button:hover { transform: scale(1.15); }

@media (prefers-reduced-motion: no-preference) {
  .swatch-button { transition: transform 0.12s ease; }
}

/* Big enough to judge a colour by, and the same shape whatever the screen. */
.color-preview-panel {
  aspect-ratio: 3 / 2;
  width: 100%;
  border-radius: var(--bs-border-radius-lg);
  border: 1px solid rgba(29, 34, 51, 0.15);
}

[data-bs-theme="dark"] .color-preview-panel {
  border-color: rgba(255, 255, 255, 0.15);
}

.color-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 600;
  border: 1px solid rgba(29, 34, 51, 0.15);
}

/* ---------- Size and colour picker ---------- */

/* Bootstrap hides .btn-check with `position: absolute`, which takes the input
   out of the flex flow and drops it at the card's start corner — all of them on
   the same spot. Clicking a pill focuses it there, and the browser scrolls that
   into view, so picking a colour near the bottom threw you back to the top of
   the card. Giving each pill its own positioned box anchors the input to the
   label it belongs to, which is already where you are looking. */
.pick-option { position: relative; }

/* ---------- Size x colour matrix ---------- */

.matrix-wrapper { overflow-x: auto; }

.matrix {
  border-collapse: separate;
  border-spacing: 0.35rem;
  min-width: max-content;
}

.matrix th {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bs-secondary-color);
  font-weight: 700;
  text-align: center;
  padding: 0.25rem 0.5rem;
  white-space: nowrap;
}

/* Pinned, because the matrix scrolls sideways on a phone and the colour is the
   one thing you cannot infer from the cell you are looking at. Scrolling to the
   XL column used to take the whole colour column off screen with it — which
   also meant the row half of the hover highlight lit up where nobody could see
   it. Needs its own opaque background, or the cells scroll through it. */
.matrix th.matrix-row-head {
  text-align: left;
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.875rem;
  color: var(--bs-body-color);
  position: sticky;
  left: 0;
  z-index: 2;
  background-color: var(--bs-card-bg, var(--bs-body-bg));
}

/* The colour and size a hovered cell belongs to. Deliberately quiet: the cells
   carry the colour that means something, so this must not compete with it. */
.matrix th.is-pointed {
  color: var(--bs-body-color);
  background-image: linear-gradient(rgba(29, 34, 51, 0.07), rgba(29, 34, 51, 0.07));
  border-radius: 0.4rem;
}

[data-bs-theme="dark"] .matrix th.is-pointed {
  background-image: linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12));
}

@media (prefers-reduced-motion: no-preference) {
  .matrix th { transition: background-color 0.12s ease, color 0.12s ease; }
}

.matrix-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 3.75rem;
  min-height: 3.25rem;
  padding: 0.4rem 0.5rem;
  border-radius: 0.6rem;
  border: 1px solid rgba(29, 34, 51, 0.1);
  text-decoration: none;
  color: inherit;
  line-height: 1.15;
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.matrix-cell:hover { transform: scale(1.04); border-color: var(--bb-ink); }

.matrix-cell .count { font-weight: 800; font-size: 1.15rem; }
.matrix-cell .reserved { font-size: 0.7rem; color: var(--bs-secondary-color); }
.matrix-cell .backordered { font-size: 0.7rem; color: var(--bs-warning-text-emphasis); }

.matrix-cell.is-empty { opacity: 0.45; }
.matrix-cell.is-out { background: rgba(229, 57, 53, 0.1); border-color: rgba(229, 57, 53, 0.35); }
.matrix-cell.is-low { background: rgba(245, 166, 35, 0.14); border-color: rgba(245, 166, 35, 0.45); }
.matrix-cell.is-ok  { background: rgba(139, 197, 63, 0.14); border-color: rgba(139, 197, 63, 0.4); }
.matrix-cell.is-missing { border-style: dashed; background: transparent; }

/* ---------- Movement kinds ---------- */

.kind-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-weight: 600;
  font-size: 0.8125rem;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
}

.kind-in  { background: rgba(139, 197, 63, 0.18); color: #4a7314; }
.kind-out { background: rgba(229, 57, 53, 0.14); color: #a52a27; }
.kind-neutral { background: rgba(37, 179, 209, 0.16); color: #12657a; }

[data-bs-theme="dark"] .kind-in  { color: #b6e06f; }
[data-bs-theme="dark"] .kind-out { color: #ff8a86; }
[data-bs-theme="dark"] .kind-neutral { color: #7ad9f0; }

.quantity-delta { font-variant-numeric: tabular-nums; font-weight: 700; }

/* ---------- Audit timeline ---------- */

.trail { list-style: none; margin: 0; padding: 0; }

.trail-item {
  position: relative;
  padding: 0 0 1.25rem 1.75rem;
  border-left: 2px solid rgba(29, 34, 51, 0.12);
}

.trail-item:last-child { border-left-color: transparent; padding-bottom: 0; }

.trail-item::before {
  content: "";
  position: absolute;
  left: -0.4rem;
  top: 0.3rem;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--bb-cyan);
  border: 2px solid var(--bs-body-bg);
}

.trail-item.is-destructive::before { background: var(--bb-red); }
.trail-item.is-additive::before { background: var(--bb-green); }
.trail-item.is-session::before { background: var(--bb-purple); }

.trail-diff {
  font-size: 0.8125rem;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  background: rgba(29, 34, 51, 0.04);
  border-radius: 0.5rem;
  padding: 0.5rem 0.75rem;
  margin-top: 0.4rem;
}

[data-bs-theme="dark"] .trail-diff { background: rgba(255, 255, 255, 0.05); }

.trail-diff del { color: #a52a27; text-decoration: line-through; }
.trail-diff ins { color: #4a7314; text-decoration: none; }

/* ---------- Mobile tab bar ---------- */

.tabbar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 1030;
  display: flex;
  background: var(--bs-body-bg);
  border-top: 1px solid rgba(29, 34, 51, 0.1);
  padding-bottom: env(safe-area-inset-bottom);
}

/* `flex: 1` alone is not enough: the automatic minimum size keeps each tab as
   wide as its label, so a long one ("Reservierungen") pushed the fixed bar past
   the viewport and took the page's horizontal scroll with it. The tabs share
   the width evenly and the label ellipsises instead. */
.tabbar a {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  padding: 0.55rem 0.25rem;
  font-size: 0.6875rem;
  font-weight: 600;
  text-decoration: none;
  color: var(--bs-secondary-color);
}

.tabbar a span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tabbar a i { font-size: 1.25rem; }
.tabbar a.active { color: var(--bb-ink); }
.tabbar a.active i { color: var(--bb-pink); }

[data-bs-theme="dark"] .tabbar a.active { color: #fff; }

@media (min-width: 992px) {
  .tabbar { display: none; }
}

/* ---------- Login ---------- */

.login-shell {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: radial-gradient(120% 100% at 50% 0%, rgba(37, 179, 209, 0.14), transparent 60%), #f6f7fb;
}

[data-bs-theme="dark"] .login-shell {
  background: radial-gradient(120% 100% at 50% 0%, rgba(37, 179, 209, 0.16), transparent 60%), #14161d;
}

.login-card { width: 100%; max-width: 26rem; }

/* ---------- Misc ---------- */

.empty-state {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--bs-secondary-color);
}

.empty-state i { font-size: 2.5rem; opacity: 0.4; display: block; margin-bottom: 0.75rem; }

.table > :not(caption) > * > * { padding: 0.75rem 0.6rem; }

/* A wide table is meant to scroll inside .table-responsive, but the wrapper is
   `position: static`, so it is not the containing block for the absolutely
   positioned `.visually-hidden` labels in the cells. Those escape the clip and
   drag the whole page's horizontal scroll out to the table's full width, which
   is how the help matrix broke every page it appeared on. */
.table-responsive { position: relative; }

.tabular { font-variant-numeric: tabular-nums; }

.sticky-actions {
  position: sticky;
  bottom: 4.5rem;
  z-index: 5;
}

@media (min-width: 992px) {
  .sticky-actions { position: static; }
}

@media (prefers-reduced-motion: reduce) {
  .card-link-plain, .matrix-cell { transition: none; }
  .card-link-plain:hover, .matrix-cell:hover { transform: none; }
}
