/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 */

/* Koral — Sirius' own brand (light/dark via the theme toggle). Coral (#ff5d4a) is the one
   brand color: primary actions, the active module in the dock, focus rings, links. Neutrals
   are a cool, slightly blue-leaning gray so the coral reads warm against them; semantic
   colors (--status-*) stay their own green/amber/red/blue, never recolored to the brand.
   Shapes are soft (10px controls, 16px cards, fully round chips); headings use Bricolage
   Grotesque, everything else Figtree (both loaded in layouts/application.html.erb).
   --menu-bar-* is the "ink" palette for the few deliberately dark surfaces (kasa header,
   dispatch board, driver views) — not the app's top bar any more, which is light. */
:root {
  --font-body: "Figtree", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Bricolage Grotesque", "Figtree", -apple-system, "Segoe UI", sans-serif;

  --app-bg: #f4f5f7;
  --surface: #ffffff;
  --surface-muted: #eef0f3;
  --surface-raised: #ffffff;
  --border: #e1e4ea;
  --border-muted: #eaecf0;
  --border-strong: #c9ced8;
  --text: #1c1f26;
  --text-muted: #687080;
  --accent: #ff5d4a;
  --accent-strong: #e8412e;
  --accent-ink: #ffffff;
  --accent-muted: #ffe9e5;
  --success: #17875d;
  --danger: #d4323f;
  --warning: #b45309;
  --focus-ring: rgba(255, 93, 74, 0.3);
  /* Mandatory fields — a warm amber edge, quieter than the coral that means focus. */
  --required-border: #f2bf8c;
  --shadow-subtle: 0 1px 2px rgba(28, 31, 38, 0.04);
  --shadow-hover: 0 10px 28px -12px rgba(28, 31, 38, 0.25);
  --shadow-float: 0 16px 40px -18px rgba(28, 31, 38, 0.45);
  --radius: 10px;
  --radius-lg: 16px;
  --radius-pill: 999px;
  --content-padding: 1.5rem;

  --status-success-bg: #dff4ea;
  --status-success-fg: #17875d;
  --status-danger-bg: #fde6e7;
  --status-danger-fg: #c02a36;
  --status-warning-bg: #fef1dc;
  --status-warning-fg: #a14a07;
  --status-info-bg: #e6ecfb;
  --status-info-fg: #3b63d6;
  --status-neutral-bg: #eceef2;
  --status-neutral-fg: #555c69;

  --table-border: #e1e4ea;
  --table-border-light: #eceef2;
  --table-header-from: #f7f8fa;
  --table-header-to: #f7f8fa;
  --table-stripe: #fafbfc;
  --table-stripe-to: #fafbfc;
  --table-hover: #fff4f2;
  --table-hover-pseudo: #ffece8;
  --table-selected: #ff5d4a;
  --table-selected-arrow: #ffffff;
  --hairline: #eceef2;

  /* The light top bar and the sticky row of module chips under it. */
  --topbar-bg: rgba(255, 255, 255, 0.92);
  --topbar-height: 56px;
  --subnav-height: 48px;
  /* Everything sticky above the content — the flash toasts, the unsaved-form bar and the
     full-height pages (warehouse map) sit right under it. */
  --menu-bar-height: calc(var(--topbar-height) + var(--subnav-height));
  --dock-bg: rgba(255, 255, 255, 0.9);
  --dock-space: 92px;

  /* Ink — the deliberately dark surfaces (see the note above). */
  --menu-bar-bg: #1c1f26;
  --menu-bar-text: #f3f4f7;
  --menu-bar-text-muted: #a3a9b6;
  --menu-bar-border: rgba(255, 255, 255, 0.12);
  --menu-bar-hover-bg: rgba(255, 255, 255, 0.08);
  --menu-bar-active-bg: #ff5d4a;
}

/* Explicit choice (theme_toggle_controller.js, stored in localStorage) always wins; absent
   that, the inline <script> in shared/_theme_script resolves the system preference to a
   concrete data-theme before first paint, so only the attribute is keyed here. Neutrals are a
   near-neutral charcoal with the same faint blue lean as the light theme; coral is lifted a
   step so it keeps its contrast on the dark ground. */
:root[data-theme="dark"] {
  --app-bg: #111318;
  --surface: #1a1d24;
  --surface-muted: #22262f;
  --surface-raised: #1f232b;
  --border: #2a2e37;
  --border-muted: #23272f;
  --border-strong: #3a3f4a;
  --text: #eceef3;
  --text-muted: #98a0ae;
  --accent: #ff7a69;
  --accent-strong: #ff9384;
  --accent-ink: #1c0d0a;
  --accent-muted: #3a1d19;
  --success: #5fd3a0;
  --danger: #ff7d86;
  --warning: #f5a65b;
  --focus-ring: rgba(255, 122, 105, 0.35);
  --required-border: #8a5a2b;
  --shadow-subtle: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-hover: 0 10px 28px -12px rgba(0, 0, 0, 0.6);
  --shadow-float: 0 16px 40px -18px rgba(0, 0, 0, 0.8);

  --status-success-bg: #133023;
  --status-success-fg: #7fe0b2;
  --status-danger-bg: #3a171b;
  --status-danger-fg: #ff9aa2;
  --status-warning-bg: #3a2711;
  --status-warning-fg: #f8bf85;
  --status-info-bg: #1b2442;
  --status-info-fg: #a3b8ff;
  --status-neutral-bg: #252932;
  --status-neutral-fg: #c3c8d2;

  --table-border: #2a2e37;
  --table-border-light: #23272f;
  --table-header-from: #1f232b;
  --table-header-to: #1f232b;
  --table-stripe: #1c1f26;
  --table-stripe-to: #1c1f26;
  --table-hover: #2a211f;
  --table-hover-pseudo: #33241f;
  --table-selected: #ff7a69;
  --table-selected-arrow: #1c0d0a;
  --hairline: #23272f;

  --topbar-bg: rgba(26, 29, 36, 0.92);
  --dock-bg: rgba(30, 33, 41, 0.92);

  --menu-bar-bg: #0c0e12;
  --menu-bar-active-bg: #ff7a69;
}

* {
  box-sizing: border-box;
}

/* Every font-size in this file is now rem (or a keyword like "smaller") rather than a fixed
   px — see the pass that converted every literal font-size: Npx to its /16 rem equivalent —
   specifically so this one responsive root size cascades through the whole app instead of
   only affecting body copy while every button/table cell (previously hardcoded px) stayed the
   same physical size regardless of viewport. Anchored at two real measurements rather than a
   guessed curve: unchanged (16px, 100%) up to a 1440px-wide laptop viewport, growing linearly
   to 19px (~119%) at a 3440px-wide 34" ultrawide — below 1440px it's clamped flat at 16px (a
   laptop should never look smaller than it already did), above 3440px flat at 19px (a title
   card, not a keep-growing font). Everything below (body's own 0.875rem, every component's
   rem) scales proportionally off of this, so a 34" ultrawide reads close to how the same page
   already looks zoomed in ~19%, not just "more empty margin around the same tiny text". */
html {
  background: var(--app-bg);
  /*font-size: clamp(16px, 0.15vw + 13.85px, 19px);*/
  font-size: 0.9rem;
}

body {
  margin: 0;
  background: var(--app-bg);
  color: var(--text);
  /* Native checkboxes/radios/range/progress otherwise render in the browser's default blue
     (Chrome) regardless of this app's own green theme — accent-color is inherited, so setting
     it once here covers every one of those controls app-wide. */
  accent-color: var(--accent);
  font-family: var(--font-body);
  /* Base reading size — most components override this downward to one of two smaller,
     app-wide tiers rather than picking their own one-off value: 0.75rem for regular UI text
     (buttons, labels, links, form fields, table cells) and 0.6875rem for small/meta text
     (badges, pills, hints, captions). Headers/titles and the two menu bars (.menu-bar,
     .module-nav) are the deliberate exceptions and keep their own larger/purpose-built sizes;
     the mobile scanner UI (phone.css companion) and the auth page are each their own
     self-contained design system and sit outside this scale entirely. */
  font-size: 1rem;
  line-height: 1.35;
}

/* Accent-colored scrollbar — small but immediately visible on every scrollable page/panel,
   the same Jungle Green used for links/buttons/focus rings rather than the browser default
   gray. `thin` (Firefox) / 10px (WebKit) keeps it unobtrusive rather than a heavy SaaS-style
   overlay bar. */
* {
  scrollbar-color: var(--border-strong) transparent;
  scrollbar-width: thin;
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: var(--app-bg);
}

::-webkit-scrollbar-thumb:hover {
  background-color: var(--accent);
}

::-webkit-scrollbar-thumb {
  background-color: var(--border-strong);
  border: 2px solid var(--app-bg);
  border-radius: var(--radius);
  background-clip: padding-box;
}

/* Card treatment (radius + subtle shadow + explicit surface bg) on top of the plain 1px
   border every fieldset already had — suse.com itself keeps --radius fairly tight (see the
   theme comment at the top of this file) rather than the fully pill-shaped SaaS look, so this
   reuses that same --radius instead of introducing a separate, bigger one just for "cards". */
fieldset {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  margin-top: 1.5rem;
  margin-bottom: 1.5rem;
  padding: 1.25rem 1.35rem;
  background: var(--surface);
  box-shadow: var(--shadow-subtle);
}

legend {
  padding: 0.2rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
}

/* .stack-flush — history of two reverted approaches before this one, kept here so the next
   pass doesn't re-try them:
   1) A blanket fieldset:has(> table) rule zeroing padding on every fieldset wrapping a table,
      including a plain index page's own listing fieldset (no sibling to align with) — its
      table ended up jammed flush against the card's rounded edges/shadow (a user screenshot of
      the TMS shipments index caught this).
   2) Scoped to just .stack-flush's own padding, still zeroed — same cramped, edge-to-edge look,
      just on show pages instead of index pages.
   3) Fieldset padding left alone; instead the *table* bled out past it via a negative margin
      exactly matching that padding, so the table's own edges lined up with the flush .list-table
      above it. That fixed the index-page/show-page cramped look, but then put the header/cell
      text right back against the card border with nothing but its own ~10px cell padding —
      a follow-up user screenshot flagged that as the same problem one layer in, so a first/
      last-child text-padding patch was added on top of the bleed.
   Current approach: no bleed, no text patch — the table simply sits inside the fieldset's own
   normal 1.25rem padding, the same as any other table in a fieldset anywhere else on the site.
   This gives up exact edge alignment with the .list-table panel above (the table is inset by
   1.25rem more than that panel is), in exchange for one consistent padding rule instead of a
   bleed-plus-patch pair — simpler, and it's what "restore the padding" asked for after seeing
   the bleed version in practice. If edge alignment turns out to matter more than consistent
   padding, the git history has the bleed version to revive. */

/* The bare <div> wrapping a .stack-flush fieldset (purchase_orders/show.html.erb and ~21 other
   show pages: render @record's bare .list-table, then this div/fieldset for "Stavke" or
   similar) has no margin of its own, so the visible gap above it was entirely fieldset's
   global 1.5rem margin-top collapsing straight through — a flat, texture-less 24px band that
   read as much bigger than it measured, sitting right under a key/value panel with no gap of
   its own below it. Halved, and scoped to specifically the fieldset directly following a
   .list-table (not fieldset's global margin-top, which every other fieldset on the site,
   including ones with no such sibling, still needs in full — e.g. the second "Prijemnice"
   fieldset right after this one keeps its normal spacing from "Stavke" above it). */
.list-table + div > fieldset {
  margin-top: 0.75rem;
}

/* SAP Fiori-style "object page" (object_page_controller.js) — a compact header (title + status
   + a handful of key facts, not the full key/value panel) with a row of tabs below switching
   which section shows, instead of a show page stacking the full .list-table and every
   related-records table in one long scroll. suse.com's own color/type tokens throughout (the
   active tab uses --accent, same green as everything else that marks "the current one" — the
   top nav's active pill, a selected table row — not a new color introduced just for this), so
   this is a layout change, not a re-skin. First rollout is purchase_orders/show.html.erb only;
   if it earns its keep the same markup shape can be reused on the other ~21 show pages that
   currently follow the plain list-table + fieldset pattern instead. */
.object-page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--border-muted);
}

/* The page h2's own accent underline (h1, h2 { border-bottom: 3px solid var(--accent) }, global
   below) would otherwise sit right on top of this header's own border-bottom a few lines down —
   two borders stacked close together read as a rendering glitch, not two intentional lines. */
.object-page-header h2 {
  margin-bottom: 0;
}

.object-page-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 1.75rem;
  margin-top: 0.6rem;
}

.object-page-fact {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.object-page-fact-label {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.object-page-fact-value {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
}

.object-page-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin: 0 0 1.25rem;
  border-bottom: 1px solid var(--border-muted);
}

.object-page-tab {
  padding: 0.65rem 1.1rem;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
}

.object-page-tab:hover {
  color: var(--text);
}

.object-page-tab.object-page-tab-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.object-page-tab:focus-visible {
  outline: 2px solid transparent;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.object-page-section[hidden] {
  display: none;
}

/* Content column. Bottom padding leaves room for the floating module dock (.module-dock) so
   the last row of a page never sits under it. */
.app-content {
  max-width: 1920px;
  margin: 0 auto;
  padding: 1.75rem var(--content-padding) var(--dock-space);
}

/* Credits line at the very end of the page (it used to be pinned to the bottom of the
   viewport; that spot now belongs to the module dock). */
.app-footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 1rem;
  max-width: 1920px;
  margin: -3.5rem auto 0;
  padding: 0 var(--content-padding) calc(var(--dock-space) - 1rem);
}

.app-footer-credits {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  opacity: 0.75;
}

.app-footer-label {
  font-size: 0.8rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.app-footer-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

/* Real logo artwork carries its own brand color — credits to other projects, never recolored. */
.app-footer-logo {
  height: 18px;
  width: auto;
  flex-shrink: 0;
}

.app-footer-logo-postgres {
  height: 22px;
}

.app-footer-logo-osm {
  height: 16px;
}

.app-footer-osm-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
}

h1,
h2,
h3 {
  color: var(--text);
  font-family: var(--font-display);
  line-height: 1.15;
  text-wrap: balance;
}

h1,
h2 {
  margin: 0 0 1rem;
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: -0.025em;
}

h3 {
  margin: 1rem 0 0.5rem;
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

p {
  margin: 0.5rem 0;
}

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

/* :where(a) — zero specificity, so this hover underline never outranks a component's own
   styling (.nav-pill, .menu-logo, cards) that happens to be an <a>. */
:where(a):hover {
  text-decoration: underline;
}

/* Top bar: brand, company, search (Ctrl+K), column-width reset, theme and the user. Light,
   translucent, sticky; the module chips (.submenu-bar) stick right under it and the modules
   themselves live in the floating dock at the bottom. */
.menu-bar {
  position: sticky;
  top: 0;
  /* Above the module dock (150): the Ctrl+K palette's overlay lives inside this bar, so the
     bar's own stacking level is the overlay's too. */
  z-index: 200;
  display: flex;
  align-items: center;
  gap: 14px;
  height: var(--topbar-height);
  padding: 0 var(--content-padding);
  /* Opaque, and no backdrop-filter: a filter would make this bar the containing block for
     every position: fixed descendant, and the Ctrl+K palette's overlay (rendered in here)
     would be squeezed into the bar instead of covering the viewport. */
  background: var(--surface);
  border-bottom: 1px solid var(--border-muted);
}

.menu-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.3rem;
  letter-spacing: -0.02em;
  text-decoration: none;
  white-space: nowrap;
}

.menu-logo:hover {
  text-decoration: none;
}

.menu-logo .logo-mark {
  display: block;
  width: auto;
  height: 32px;
}

.menu-context {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  color: var(--text-muted);
  font-size: 0.95rem;
  white-space: nowrap;
}

.menu-context::before {
  content: "";
  width: 1px;
  height: 22px;
  background: var(--border);
}

.menu-context strong {
  color: var(--text);
  font-weight: 600;
}

.menu-tools {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  min-width: 0;
}

.menu-tools .global-search {
  flex: 0 1 300px;
  width: 300px;
  margin: 0 4px 0 0;
}

/* The modules — a floating dock centered at the bottom of the viewport, icon over label, the
   open module filled coral. Scrolls sideways on a narrow window rather than wrapping. */
.module-dock {
  position: fixed;
  left: 50%;
  bottom: 14px;
  z-index: 150;
  transform: translateX(-50%);
  display: flex;
  gap: 4px;
  max-width: calc(100vw - 24px);
  padding: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  background: var(--dock-bg);
  border: 1px solid var(--border);
  border-radius: 20px;
  box-shadow: var(--shadow-float);
  backdrop-filter: saturate(1.4) blur(12px);
}

.module-dock::-webkit-scrollbar {
  display: none;
}

.nav-pill {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-width: 68px;
  min-height: 0;
  margin: 0;
  padding: 7px 10px 6px;
  border: none;
  border-radius: 14px;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease, transform 0.12s ease;
}

.nav-pill-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
}

.nav-pill-icon svg {
  width: 100%;
  height: 100%;
}

.nav-pill:hover {
  background: var(--surface-muted);
  color: var(--text);
  text-decoration: none;
}

.nav-pill:focus-visible {
  outline: 2px solid transparent;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.nav-pill.main-menu-active,
.nav-pill.main-menu-active:hover {
  background: var(--accent);
  color: var(--accent-ink);
}

@media (prefers-reduced-motion: no-preference) {
  .nav-pill:hover {
    transform: translateY(-2px);
  }
}

.user-info {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
  margin-left: 6px;
  padding-left: 12px;
  border-left: 1px solid var(--border);
}

.user-avatar {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--accent-muted);
  color: var(--accent-strong);
  font-weight: 700;
  font-size: 0.85rem;
  text-transform: uppercase;
}

.user-email {
  display: flex;
  flex-direction: column;
  font-size: 0.8rem;
  line-height: 1.2;
  color: var(--text-muted);
  white-space: nowrap;
}

.user-email strong {
  color: var(--text);
  font-weight: 600;
  font-size: 0.9rem;
}

/* Plain text, not a button — logging out isn't a destination. */
.logout-link {
  min-height: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  padding: 0 2px;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.logout-link:hover {
  color: var(--accent-strong);
  background-color: transparent;
}

.logout-link:focus-visible {
  outline: 2px solid var(--focus-ring);
}

.theme-toggle-btn {
  flex: 0 0 auto;
  width: 2.25rem;
  min-height: 2.25rem;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-size: 1.05rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.theme-toggle-btn:hover {
  background: var(--surface-muted);
}

.theme-toggle-btn:focus-visible {
  outline: 2px solid transparent;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

label {
  display: inline-block;
  margin: 0.5rem 0 0.2rem;
  color: var(--text-muted);
  font-size: 1rem;
  font-weight: 600;
}

input:not([type="checkbox"]):not([type="radio"]),
textarea,
select {
  max-width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

input:not([type="checkbox"]):not([type="radio"]),
select {
  min-height: 2.25rem;
  padding: 0.4rem 0.65rem;
}

input[type="text"] {
  width: 30em;
}

textarea {
  padding: 0.55rem 0.65rem;
  vertical-align: top;
  width: 30em;
}

/* A colored focus ring (border + soft halo) instead of a plain black outline reads as the
   modern-SaaS-dashboard convention this app's whole look is built around now — outline is kept
   too (not just box-shadow) so focus is still visible for a user with high-contrast/forced
   colors overriding box-shadow. */
input:focus,
textarea:focus,
select:focus {
  outline: 2px solid transparent;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* Required-field styling: RequiredAwareFormBuilder (app/form_builders) sets the HTML
   `required` attribute on every field whose underlying model attribute has a presence
   validator, so this is derived from real validations rather than hand-marked per form.
   Amber (--warning) reads as "pay attention, mandatory" without doubling up on --accent
   (already means focus/brand) or --danger (an actual validation error) — the dedicated
   :focus:required rule below outranks the plain :focus block above on specificity, so an
   active required field still shows the ordinary accent focus color, not amber. The
   :not([type="checkbox"]):not([type="radio"]) repeated here isn't needed for correctness
   (required is never set on those) but matches the base input rule's own specificity
   ((0,2,1) from its two :not()s) — without it, a plain "input:required" ((0,1,1)) silently
   loses to that base rule regardless of source order and never shows on a real <input>,
   only on <select> (which the base rule matches at plain element specificity). */
input:required:not([type="checkbox"]):not([type="radio"]),
textarea:required,
select:required {
  border-color: var(--required-border);
}

input:focus:required:not([type="checkbox"]):not([type="radio"]),
textarea:focus:required,
select:focus:required {
  border-color: var(--accent);
}

/* :has(:required) (no ">") on purpose — a handful of rows wrap their real field a level
   deeper than the label's own direct sibling (e.g. partner_search_field's searchable-select
   div), so the required input is a descendant of the row div, not necessarily its direct
   child. */
#resource-form div:has(> label):has(:required) > label::after {
  content: " *";
  color: var(--warning);
}

/* SaaS-style form layout — label *above* field (the same convention layouts/auth.html.erb's
   .auth-field already used — small uppercase muted label, comfortable input below it), not the
   old label-left "property sheet" row. Applied generically via #resource-form (every
   _form.html.erb across every resource in the app shares that id) rather than per-file, since
   every one of those partials already follows the identical
   <div><%= form.label %><%= field %></div> shape per field — a CSS-only change here upgrades
   every form in the app at once. :has(> label) reaches a field row even where a form wraps a
   few of them one level deeper (e.g. sales_orders' partner-scoped-select group) without needing
   a matching markup change there; nested line-item rows ([data-nested-fields-target="item"])
   are excluded since those lay out several label/field pairs across one row already, as their
   own .stavke-grid. */
#resource-form {
  display: block;
  /* Full page width, like the data-tables — no centered 1100px column. */
  margin: 0 0 2rem;
  padding: 1.5rem 1.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-subtle);
}

/* Line-item rows (data_table_column_resize.js's cousin — these aren't a <table>, but read as
   one): the default input[type="text"] { width: 30em } below is sized for a single full-width
   field, not three fields sharing one compact row, so it's narrowed specifically inside an
   item row — same idea .searchable-select-menu already applies at the dropdown level. */
[data-nested-fields-target="item"] input[type="text"] {
  width: 14em;
}

[data-nested-fields-target="item"] input[type="number"] {
  width: 6em;
}

[data-nested-fields-target="item"] label {
  margin-right: 0.15rem;
}

#resource-form div:has(> label):not([data-nested-fields-target]) {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  max-width: 420px;
  margin-bottom: 1rem;
}

/* Same story as the .btn-*[hidden] rule further down: the flex display above would otherwise
   beat `[hidden]`, so a field row a Stimulus controller hides (the promotion form's product /
   category / partner / tier target rows) stayed visible. */
#resource-form div:has(> label):not([data-nested-fields-target])[hidden] {
  display: none;
}

#resource-form div:has(> label):not([data-nested-fields-target]) label {
  margin: 0;
  text-align: left;
  white-space: nowrap;
  font-size: 0.95rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-muted);
}

/* A row that also carries a hint (e.g. products/_form.html.erb's <small> under
   "Veleprodajna cena") is just a 3rd flex child now — falls in below the field on its own
   line automatically, no column-placement override needed the way the old grid layout did. */
#resource-form div:has(> label):not([data-nested-fields-target]) > small,
#resource-form div:has(> label):not([data-nested-fields-target]) > .field-hint {
  margin: 0;
}

#resource-form div:has(> label):not([data-nested-fields-target])
  input:not([type="checkbox"]):not([type="radio"]),
#resource-form div:has(> label):not([data-nested-fields-target]) select,
#resource-form div:has(> label):not([data-nested-fields-target]) textarea {
  width: 100%;
}

/* A checkbox row reads oddly stacked (tiny box on its own line, floating left with nothing
   next to it) — keep those inline with their label instead, same as auth.html.erb has no
   equivalent for but every other SaaS settings-style checkbox row does. */
#resource-form div:has(> label):not([data-nested-fields-target]):has(> input[type="checkbox"]) {
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
}

#resource-form div:has(> label):not([data-nested-fields-target]):has(> input[type="checkbox"]) label {
  order: 2;
  text-transform: none;
  font-size: 1rem;
  font-weight: normal;
  letter-spacing: normal;
  color: var(--text);
}

/* A fieldset inside a form (Stavke and the like) sits on a slightly deeper gray than the form
   card around it, so the section reads as its own block without any extra color. */
#resource-form fieldset {
  max-width: none;
  margin-left: 0;
  margin-right: 0;
  background: var(--app-bg);
  border-color: var(--table-border);
}

/* Screen-reader-only — a "Stavke" row's per-field <label>s (Artikal/Količina/Cena po JM...)
   stay in the DOM for accessibility but are no longer shown on every single row now that
   .stavke-header shows those same column titles once, at the top. */
.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;
}

/* "Stavke" line-item grid — a real table-like grid (steel-bordered header row in the same
   gray-gradient language as .data-table th, striped data rows) instead of the per-row repeated
   label+field groups this used to be. Each document's own column count/widths differ (an
   invoice has a PDV-stopa column a delivery doesn't), so --stavke-cols is set inline per
   fieldset in the view rather than living in a single shared template here; .stavke-header and
   .stavke-row both read the same custom property so their columns always line up. */
.stavke-grid {
  margin-bottom: 0.5rem;
}

.stavke-header,
.stavke-row {
  display: grid;
  grid-template-columns: var(--stavke-cols);
  align-items: center;
  gap: 0 0.6rem;
  padding: 0.35rem 0.5rem;
}

.stavke-header {
  background: var(--table-header-from);
  border: 1px solid var(--table-border);
  border-radius: var(--radius) var(--radius) 0 0;
  color: var(--text-muted);
  font-size: 0.9rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
}

.stavke-header span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.stavke-row {
  background: var(--surface);
  border: 1px solid var(--table-border);
  border-top: none;
}

.stavke-row:nth-child(even) {
  background: var(--table-stripe);
}

.stavke-row [data-controller="searchable-select"] {
  display: block;
  width: 100%;
}

.stavke-row input:not([type="hidden"]) {
  width: 100%;
}

/* "Stavke" as an editable .data-table (StavkeHelper#stavke_table) — same header, row-number
   gutter and column resize as every list table, with a field in each cell. Doubled
   .data-table.stavke-table selectors: these must outrank the plain .data-table td rules further
   down. Cells don't clip (the artikal search's dropdown hangs below its cell; computed amounts
   show in full) and fields fill their column. */
.stavke-table-wrap {
  margin-bottom: 0.5rem;
}

/* One font size through the whole table — the header's 0.9rem for the rows, their fields and
   the Ukupno footer too, instead of the rows' fields at the full 1rem. */
.data-table.stavke-table td,
.data-table.stavke-table td input:not([type="hidden"]),
.data-table.stavke-table td select,
.data-table.stavke-table td textarea,
.data-table.stavke-table td button,
.data-table.stavke-table td .searchable-select-menu {
  font-size: 0.9rem;
}

.data-table.stavke-table td {
  padding: 3px 6px;
  overflow: visible;
  text-overflow: clip;
  vertical-align: middle;
}

.data-table.stavke-table td.stavke-product {
  min-width: 240px;
}

.data-table.stavke-table td [data-controller="searchable-select"] {
  display: block;
  width: 100%;
  min-width: 220px;
}

.data-table.stavke-table td input:not([type="hidden"]),
.data-table.stavke-table td select,
.data-table.stavke-table td textarea {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

.data-table.stavke-table td textarea {
  min-height: 2.25rem;
  resize: vertical;
}

.data-table.stavke-table td.stavke-num {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* A clicked row keeps the gutter's ▶ marker, but not the coral fill list tables use — here it
   would sit behind every field of the row being edited. */
.data-table.stavke-table tbody tr.selected td {
  background-color: transparent !important;
  color: inherit !important;
}

.data-table.stavke-table tfoot.stavke-totals td {
  padding: 6px;
  font-size: 0.9rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
  border-top: 1px solid var(--table-border);
}

.data-table.stavke-table tfoot.stavke-totals td.stavke-totals-label {
  text-align: left;
}

/* Repeatable nested-fields row (bank accounts, addresses, barcodes, trip stops, ...) that
   hasn't been upgraded to a full .stavke-grid — same idea as .stavke-row's box, just without
   column layout, and using theme tokens instead of the hardcoded #ccc gray literal every one
   of these used to carry (which never adapted to dark mode). */
.nested-item-row {
  margin-bottom: 0.5rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--table-border);
  background: var(--surface);
}

.stavke-remove-btn {
  min-height: 1.5rem;
  padding: 0.1rem 0.4rem;
}

/* Količina / Cena po JM — numbers read better right-aligned (decimal points line up down the
   column) than left-aligned like the Artikal search box next to them. */
[data-number-format-target="field"] {
  text-align: right;
}

#resource-form > .load-source-row {
  max-width: 700px;
}

/* A handful of pages (e.g. employees/show.html.erb, which doubles as an edit form plus
   related-records sections — "Korisnički nalog"/"Evidencija radnog vremena"/"Odsustva") render
   extra fieldsets (often holding a data-table) and standalone links *after* the main
   #resource-form, as plain page content rather than inside the form panel itself. Those had no
   width constraint of their own and stretched to .app-content's full 1920px cap while the form
   above stayed centered at 1100px — visibly wider than, and drifting past the right edge of,
   the form above them ("off-grid"). ~ (any later sibling, not just the immediate next one)
   since there can be more than one such section, each interleaved with a plain link. */
#resource-form ~ fieldset {
  margin: 0 0 1rem;
}

#resource-form ~ a {
  display: block;
  margin: 0 0 1rem;
}

button {
  min-height: 2.25rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  padding: 0.4rem 0.9rem;
  font-weight: 500;
  box-shadow: none;
  font: inherit;
  font-size: 1rem;
  cursor: pointer;
  transition: border-color 0.1s ease, box-shadow 0.1s ease, filter 0.1s ease;
}

/* var(--surface-muted), not the old "swap --table-header-from/-to" gradient-reversal trick —
   that trick only ever worked back when those two tokens held different shades (a subtle
   bevel); now that the flat-surface design keeps them deliberately equal, swapping them
   produces the exact same flat color and the hover became invisible everywhere a button used
   it (every plain button, .btn-edit/.btn-destroy/etc, .nav-pill, .module-nav button, the
   Izveštaji dropdown's summary — five separate rules across this file, all with the same
   dead swap, only caught once every one of them stopped visibly reacting to hover). */
button:hover {
  background: var(--surface-muted);
}

button:active {
  background: var(--surface-muted);
  transform: translateY(1px);
}

button:focus-visible {
  outline: 2px solid transparent;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.btn-new,
.btn-edit,
.btn-destroy,
.btn-save,
.btn-back,
.btn-cancel {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.25rem;
  padding: 0.4rem 0.95rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  box-shadow: none;
  font: inherit;
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color 0.1s ease, box-shadow 0.1s ease, filter 0.1s ease;
}

/* .btn-new is the one "create a new record" action — a solid accent fill (so it reads as the
   primary action wherever it appears, a plain page-actions bar or inside .module-nav), same
   size as every other .page-actions button (Odustani/Sačuvaj included) — just the fill/weight
   distinguish it, not a bigger footprint. */
.btn-new {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  box-shadow: 0 6px 16px -8px var(--accent);
}

.btn-destroy {
  color: var(--danger);
}

.btn-destroy:hover {
  border-color: var(--danger);
}

.btn-new:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
}

/* .btn-save gets the same solid-accent treatment as .btn-new — it's the one action that
   actually finishes a form, and reads as such (not just another gray Odustani-shaped button)
   wherever the Odustani/Sačuvaj bar appears. */
.btn-save {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  box-shadow: 0 6px 16px -8px var(--accent);
}

.btn-save:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
}

.btn-save:active {
  background: var(--accent);
  filter: brightness(0.9);
}

/* The unconditional `display: inline-flex` above otherwise beats the browser's own
   `[hidden] { display: none }` rule (comparable specificity, this one declared later in the
   cascade) — without this, toggling `.hidden` on one of these from JS (e.g. the bulk-actions
   bar's "Izmeni" link, shown only for a single selection) stops actually hiding it. */
.btn-new[hidden],
.btn-edit[hidden],
.btn-destroy[hidden],
.btn-save[hidden],
.btn-back[hidden],
.btn-cancel[hidden] {
  display: none;
}

.btn-edit:hover,
.btn-destroy:hover,
.btn-back:hover,
.btn-cancel:hover {
  background: var(--surface-muted);
}

.btn-new:focus-visible,
.btn-edit:focus-visible,
.btn-destroy:focus-visible,
.btn-save:focus-visible,
.btn-back:focus-visible,
.btn-cancel:focus-visible {
  outline: 2px solid transparent;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.btn-new:active {
  background: var(--accent);
  filter: brightness(0.9);
}

.btn-edit:active,
.btn-destroy:active,
.btn-back:active,
.btn-cancel:active {
  background: var(--surface-muted);
}

/* The page's own action bar (Nazad/Izmeni/Obriši on a show page, Odustani/Sačuvaj on a new/edit
   form, Novi X on an index page, and whatever else a page adds next to them) — lives in
   <main>, right at the top of the page's own content (see application.html.erb: content_for
   (:page_actions) is yielded there, before the rest of the page), not in the sticky header —
   unlike .module-nav (actual navigation: switching to a different module/tab/record list),
   which stays in .submenu-bar since losing that while scrolling would be disorienting; losing
   sight of "the Sačuvaj button" while scrolling a long form is just as recoverable as scrolling
   back up on any other page. No max-width/margin/padding of its own — it's a plain child of
   .app-content now, so that already provides the width/centering/side padding; margin-bottom +
   border-bottom just separate it from the title/content below, same as any other in-page
   element. Nazad (or Novi X, on an index page — see :has(.btn-new) below) is always moved to
   be the first element in the markup, so with the row pulled right, reading order stays Nazad,
   Izmeni, Obriši. */
.page-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1rem;
}

/* Nazad pinned to the far left, every other action (Izmeni/Obriši/PDF/...) still grouped on the
   right as before — auto margin eats the rest of the flex line's free space, same trick as
   .module-nav's own "primary action stays left, everything else right" comment above uses. The
   actual flex item here is button_to's own wrapping <form> (a <button class="btn-back"> alone
   isn't one — .page-actions' direct children are the forms, so the margin has to go on the form,
   not the button, or it has no effect on the row's layout at all), and :has(.btn-back) rather
   than :first-child so this doesn't depend on Nazad's form literally being first in the markup,
   just present. Same trick for .btn-new (an index page's "Novi X", no Nazad there to compete
   with) — plain .btn-new styling otherwise applies (same size as every other .page-actions
   button, just accent-filled; no special flush/bar treatment here now that this row isn't bar
   chrome). */
.page-actions form:has(.btn-back),
.page-actions form:has(.btn-new) {
  margin-right: auto;
}

/* Title + actions on one row (ApplicationHelper#with_page_actions wraps the page's first <h2>
   and its .page-actions in this): title left, every button pushed to the right end. The row
   carries the h2's accent underline itself so it runs full width under the buttons too; the
   h2 and .page-actions drop their own borders/margins inside it. Nazad/Novi X's margin-right:
   auto above has no effect here (.page-actions doesn't grow), so they sit right with the rest. */
.page-title-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.75rem 1rem;
  margin: 0 0 1.25rem;
}

.page-title-row > h2 {
  margin: 0;
}

.page-title-row > .page-actions {
  margin: 0 0 0 auto;
  padding: 0;
  border-bottom: none;
}

/* Inside .object-page-header the title block (h2 + facts) is the header's first child; let it
   grow so the actions reach the right edge (just left of the status badge), and drop the
   accent underline there — that header already has its own border-bottom. */
.object-page-header > div:first-child {
  flex: 1 1 auto;
}

.object-page-header .page-title-row {
  margin: 0;
  padding: 0;
  border-bottom: none;
}

/* The open module's pages as a row of chips, sticky right under the top bar — navigation only;
   what you can DO on a page (.page-actions) lives in the page itself. Always rendered, even on
   pages with no module navigation, so the layout doesn't jump between pages. */
.submenu-bar {
  position: sticky;
  top: var(--topbar-height);
  z-index: 99;
  background: var(--topbar-bg);
  border-bottom: 1px solid var(--border-muted);
  backdrop-filter: saturate(1.4) blur(10px);
}

/* Pages with no module chips (Početna, plain admin pages) don't keep an empty bar. */
.submenu-bar:has(> .module-nav:empty) {
  display: none;
}

.module-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-height: var(--subnav-height);
  max-width: 1920px;
  margin: 0 auto;
  padding: 8px var(--content-padding);
}

.module-nav form.button_to {
  display: inline-flex;
}

.module-nav > a,
.module-nav > .module-nav-active {
  display: inline-flex;
  align-items: center;
  padding: 0.3rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  font-size: 0.92rem;
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
}

.module-nav > a:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--surface);
  text-decoration: none;
}

.module-nav > a:focus-visible {
  outline: 2px solid transparent;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* Where you are — a plain <span>, nothing to click. */
.module-nav > .module-nav-active {
  background: var(--text);
  border-color: var(--text);
  color: var(--surface);
  font-weight: 600;
  cursor: default;
}

/* button_to always renders its own <form class="button_to">, which is display:block
   by default and forces the button onto its own line. Make every such form inline so
   button_to buttons sit next to each other and to link_to/button elements around them. */
form.button_to {
  display: inline;
}

/* Otherwise this unconditional `display: inline` beats the browser's own
   `[hidden] { display: none }` rule (comparable specificity, this one declared later in the
   cascade) — needed since data_table_row_navigation.js hides the "Prikaži" button_to form in
   favor of double-click-to-open, and `form.hidden = true` would otherwise do nothing. */
form.button_to[hidden] {
  display: none;
}

/* Filter toolbar directly above a .data-table — same gray-gradient/steel-border language
   as the .NET grid (see .data-table below) so the two read as one attached control, like a
   WinForms toolstrip docked to its grid, instead of a plain form floating above it. */
.filter-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0;
  padding: 0.45rem 0.55rem;
  background: var(--surface-muted);
  border: 1px solid var(--border-muted);
}

/* Sits directly under .filter-form with zero gap — same border color continues from the
   toolbar into the grid so the pair reads as one boxed unit. */
.filter-form + .data-table {
  margin-top: 0;
  border: 1px solid var(--border);
}

.filter-form input[type="text"],
.filter-form input[type="date"],
.filter-form input[type="number"],
.filter-form select {
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: none;
  padding: 3px 6px;
  font-size: 1rem;
}

.filter-form input:focus,
.filter-form select:focus {
  outline: 1px solid var(--text);
}

.filter-form a {
  font-size: 1rem;
  color: var(--text-muted);
}

/* Sits directly under .filter-form with zero gap — both already share the same border-muted
   token, so this only needs to remove the gap for the pair to read as one boxed unit. */
/* Custom autocomplete dropdown (searchable_select_controller.js) — deliberately not the
   native <datalist> popup, which has real keyboard/value bugs in Chromium. */
[data-controller="searchable-select"] {
  position: relative;
  display: inline-block;
}

.searchable-select-menu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 50;
  margin: 2px 0 0;
  padding: 0;
  list-style: none;
  min-width: 220px;
  max-height: 260px;
  overflow-y: auto;
  /* overflow-y: auto above already clips the x-axis too (a box can't scroll one axis and
     let the other bleed past its own border-radius) — same fix as .module-nav-dropdown-menu
     got, for the same reason: an item's :hover background is a plain edge-to-edge rectangle
     that would otherwise square off past this menu's own rounded corners. */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 1rem;
  text-align: left;
}

.searchable-select-item {
  padding: 4px 8px;
  cursor: pointer;
  white-space: nowrap;
}

.searchable-select-item:hover,
.searchable-select-item.active {
  background: var(--surface-muted);
  color: var(--text);
  text-decoration: underline;
}

/* Header "jump to anything" — a fake-input trigger button (looks like a search field, isn't
   one) that opens the real thing as a centered command-palette modal, either by clicking it
   or Ctrl+K/Cmd+K from anywhere on the page (shared/_global_search.html.erb,
   command_palette_controller.js). Not a live autocomplete dropdown hanging off an inline
   field — an earlier version of this feature was exactly that, replaced on request ("CTRL+K
   da otvori ovaj search prompt, a onda rezultati da se prikazu — ne kao autocomplete"). */
.global-search {
  position: relative;
  flex: 0 1 220px;
  min-width: 70px;
  margin: 0 8px;
}

.global-search-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  min-height: 2rem;
  padding: 0.3rem 0.5rem 0.3rem 0.65rem;
  background: var(--surface);
  color: var(--text-muted);
  font-size: 1rem;
  text-align: left;
}

.global-search-trigger-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.global-search-kbd {
  flex: 0 0 auto;
  padding: 1px 5px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface-muted);
  color: var(--text-muted);
  font-size: 1rem;
  font-family: monospace;
  white-space: nowrap;
}

.command-palette-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  justify-content: center;
  padding-top: 12vh;
}

/* Without this, toggling the `hidden` attribute (open()/close() in command_palette_controller.js)
   does nothing once the overlay has been shown once: an author `display: flex` rule above
   always outranks the browser's own built-in `[hidden] { display: none }` UA-stylesheet rule
   (author styles beat UA styles regardless of matched specificity, short of !important) — the
   overlay would stay laid out and keep intercepting clicks/covering the page even while
   "hidden", confirmed live via Playwright (a click on the reopened trigger button kept
   silently landing on the supposedly-hidden backdrop instead). */
.command-palette-overlay[hidden] {
  display: none;
}

.command-palette-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(17, 19, 24, 0.5);
  backdrop-filter: blur(3px);
}

.command-palette-panel {
  position: relative;
  width: min(90vw, 640px);
  max-height: 70vh;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 20px;
  box-shadow: var(--shadow-float);
  overflow: hidden;
}

/* input.command-palette-input:not(...):not(...), not just .command-palette-input — the base
   "input:not([type=checkbox]):not([type=radio])" rule (application.css above) reaches
   specificity (0,2,1) off its own two :not()s alone, beating a plain single-class selector
   regardless of source order (bitten this exact trap several times already this session —
   see the required-field and .auth-submit fixes above). Matching it here the same way. */
.command-palette-input,
input.command-palette-input:not([type="checkbox"]):not([type="radio"]) {
  flex: 0 0 auto;
  width: 100%;
  min-height: 3rem;
  padding: 0.85rem 1.25rem;
  border: 0;
  border-bottom: 1px solid var(--border-muted);
  border-radius: 0;
  background: transparent;
  font-size: 1.1rem;
}

.command-palette-input:focus {
  outline: none;
  box-shadow: none;
}

.command-palette-results {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 0;
}

.command-palette-hint {
  margin: 0;
  padding: 24px 20px;
  color: var(--text-muted);
  font-size: 0.75rem;
  text-align: center;
}

.command-palette-category {
  padding: 12px 20px 4px;
  color: var(--text-muted);
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.command-palette-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 20px;
  cursor: pointer;
}

.command-palette-item {
  margin: 0 8px;
  border-radius: var(--radius);
}

.command-palette-item:hover,
.command-palette-item.active {
  background: var(--accent-muted);
}

/* The document type (e.g. "Prodajni nalog") — a fixed width rather than shrink-to-fit so
   every row's tag lands in the same visual column regardless of how long that particular
   type's name is, the same way a table's own type/status column would. */
.command-palette-item-type {
  flex: 0 0 auto;
  width: 136px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  background: var(--surface-muted);
  color: var(--text-muted);
  font-size: 0.8rem;
  font-weight: 600;
  text-align: center;
}

.command-palette-item-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
  font-size: 0.95rem;
  font-weight: 500;
}

.command-palette-item-sublabel {
  flex: 0 0 auto;
  color: var(--text-muted);
  font-size: 0.85rem;
  white-space: nowrap;
}

/* Org chart (org_chart_controller.js) — one box per organizational unit, lines to its parent. */
[data-controller="org-chart"] {
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--border);
  cursor: grab;
}

.org-chart-link {
  fill: none;
  stroke: var(--border);
  stroke-width: 1.5px;
}

.org-chart-node rect {
  fill: var(--surface);
  stroke: var(--border);
  stroke-width: 1px;
}

.org-chart-name {
  font-size: 1rem;
  font-weight: 600;
  fill: var(--text);
}

.org-chart-meta {
  font-size: 1rem;
  fill: var(--text-muted);
}

/* "Izveštaji" — the trailing chip in every module's row, listing that module's reports.
   Native <details>/<summary>: no JS, opens on tap, every link navigates away. */
.module-nav-dropdown {
  position: relative;
}

.module-nav-dropdown summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0.3rem 0.85rem;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  font-size: 0.92rem;
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
}

.module-nav-dropdown summary::after {
  content: "▾";
  font-size: 0.8rem;
}

.module-nav-dropdown summary::-webkit-details-marker {
  display: none;
}

.module-nav-dropdown summary:hover {
  color: var(--text);
  background: var(--surface);
}

.module-nav-dropdown summary:focus-visible {
  outline: 2px solid transparent;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.module-nav-dropdown[open] summary {
  color: var(--text);
  background: var(--surface);
  border-style: solid;
}

.module-nav-dropdown.module-nav-active summary {
  background: var(--text);
  border: 1px solid var(--text);
  color: var(--surface);
  font-weight: 600;
}

.module-nav-dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
  /* Above Leaflet's own controls (z-index 1000) — vehicle_positions renders a map under it. */
  z-index: 1100;
  margin: 6px 0 0;
  padding: 6px;
  min-width: 240px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
}

.module-nav-dropdown-menu a {
  display: block;
  padding: 7px 10px;
  border-radius: 8px;
  color: var(--text);
  font-size: 0.95rem;
  text-decoration: none;
  white-space: nowrap;
}

.module-nav-dropdown-menu a:hover {
  background: var(--accent-muted);
  color: var(--accent-strong);
  text-decoration: none;
}

/* Source-document picker + "Učitaj" button (load_source_controller.js) at the top of a "new
   document" form — pick e.g. a Porudžbenica and pull its partner/items in without first going
   back to that document's own show page and clicking its "Nova X za ovaj Y" link. */
.load-source-row {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--border-muted);
}

.load-source-row > div {
  flex: 0 1 auto;
  min-width: 16rem;
}

/* The generic #resource-form row rule (line ~493: "#resource-form div:has(> label):not(...)")
   sets margin-bottom: 1rem for spacing stacked rows apart vertically — this div matches that
   selector too, and since align-items: flex-end on .load-source-row aligns each flex item by
   its margin box, that bottom margin was silently read as 16px of extra height on this div
   alone, pushing the "Učitaj" button (no bottom margin of its own) 16px below the select
   instead of level with it. .load-source-row itself already carries its own margin-bottom for
   spacing after the row, so this is safe to zero out — but needs specificity (1,2,1) to beat
   that rule's own (1,1,2) ("div" + ":has(> label)"'s own implicit element-level specificity +
   ":not(...)" all add up on top of its #id): a plain "#resource-form .load-source-row > div"
   only reaches (1,1,1) and silently loses despite the #id prefix and despite coming later in
   the file. Repeating the same :not(...) here isn't meaningful on its own (this div is never
   a nested-fields target anyway) — it's here purely to win the specificity race. */
#resource-form .load-source-row > div:not([data-nested-fields-target]) {
  margin-bottom: 0;
}

/* Real-time JMBG/bank-account-number feedback (jmbg_controller.js, bank_account_controller.js) */
.field-hint {
  margin-left: 0.5rem;
  font-size: 1rem;
  color: var(--text-muted);
}

.text-danger {
  color: var(--danger);
}

.text-danger-strong {
  color: var(--danger);
  font-weight: 700;
}

/* Validation error box at the top of a form — was a bare style="color: red" (Rails scaffold
   boilerplate) on every one of ~65 forms; one shared class instead, in the same
   filled-tint-plus-border language as .status-badge/.field-invalid elsewhere. */
.form-errors {
  margin-bottom: 1rem;
  padding: 0.65rem 0.9rem;
  border: 1px solid var(--danger);
  background: var(--status-danger-bg);
  color: var(--status-danger-fg);
  max-width: 700px;
}

.form-errors-heading {
  margin: 0 0 0.35rem;
  font-weight: 700;
}

.form-errors ul {
  margin: 0;
  padding-left: 1.25rem;
}

.field-invalid {
  border-color: var(--danger);
}

.field-invalid + .field-hint {
  color: var(--danger);
}

.field-valid + .field-hint {
  color: var(--success);
}

.money {
  text-align: right;
}

.qty {
  text-align: center;
}

.currency_s {
  font-family: monospace;
}

.plate_s {
  font-family: monospace;
}

.mono-value {
  font-family: monospace;
  font-variant-numeric: tabular-nums;
}

.pagination-controls {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: 1rem;
  font-size: 1rem;
}

/* Pagination footer row docked to the bottom of a .data-table (like a WinForms grid's
   status strip) instead of floating below it. Selector includes :last-child (this cell,
   being its row's only child, always matches it) to outrank .data-table td:last-child
   below, which would otherwise clamp this spanning cell to width: 1%. */
.data-table tfoot[data-page] td:last-child {
  width: auto;
  white-space: normal;
  background: linear-gradient(to bottom, var(--table-header-from) 0%, var(--table-header-to) 100%);
  border-top: 1px solid var(--table-border);
}

.data-table tfoot .pagination-controls,
.data-table tfoot .pagination-controls select {
  font-size: inherit;
}

.data-table tfoot .pagination-controls {
  margin-top: 0;
}

table {
  border-collapse: collapse;
}

table th,
table td {
  border-bottom: 1px solid var(--hairline);
  padding: 10px 14px;
}

.bl-table,
.bl-table th,
.bl-table td {
  border: 0px solid var(--hairline) !important;
}

/* Flat table look (Pravac B): no gradient header, no gridlines, no row-number gutter —
   just hairline row dividers and clear typography. Text stays selectable (no
   cursor:default/user-select:none) since copying a value out of a table, e.g. an invoice
   number, is a real everyday action in this app, unlike a native desktop grid. */
/* .NET DataGridView look (gray gradient header, thin steel-gray borders) restored per the
   user's call — this app is meant to read as a Windows/.NET line-of-business tool, not a
   flat modern web grid. */
/* A fieldset's min-width defaults to min-content, so the fieldset around a data-table grew to
   fit this page's longest nowrap cell (a long partner name on one sort order but not another)
   — and the table, 100% of it, with it: the table's total width changed with every sort,
   filter and page. Without that floor the fieldset keeps the content column's width and the
   table (see data_table_column_resize.js's fitToContainer) fits inside it. */
fieldset:has(> .data-table) {
  min-width: 0;
}

/* Rows and footers at the header's 0.9rem — one size through the whole table. */
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  color: var(--text);
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-subtle);
}

/* Flat SaaS list — horizontal row dividers only, no vertical gridlines between columns (every
   .data-table border-right below was dropped for this; border-bottom stays throughout so rows
   still read as distinct). */
/* Bold, uppercase, letter-spaced label treatment (vs. the old plain 600-weight sentence
   case) — reads as a distinct "column label" the way suse.com's own section headers do,
   rather than just a slightly heavier copy of the data below it. */
.data-table th {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--table-header-from);
  border-bottom: 1px solid var(--table-border);
  /* Faint dividers between header cells only — they show where a column can be dragged
     wider (data_table_column_resize.js); the body rows stay divider-free. */
  border-right: 1px solid var(--table-border-light);
  border-left: 1px solid var(--table-border-light);
  padding: 7px 12px;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  text-align: left;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Drag handle for column resizing (data_table_column_resize.js) — a thin strip over the
   header cell's right border. `.data-table th` is already `position: sticky`, which (like
   any positioned value) is enough of a containing block for this absolutely-positioned
   child, so no extra positioning is needed here. */
/* Sortable column labels read as labels, not links — coral only on hover. */
.data-table th a {
  color: inherit;
}

.data-table th a:hover {
  color: var(--accent-strong);
  text-decoration: none;
}

.col-resize-handle {
  position: absolute;
  top: 0;
  /* Inside the cell's own right edge: a neighbouring sticky header cell paints over anything
     that sticks out past it, which left only half of the strip grabbable. */
  right: 0;
  width: 7px;
  height: 100%;
  z-index: 11;
  cursor: col-resize;
  touch-action: none;
}

.col-resize-handle:hover {
  background: var(--accent);
  opacity: 0.6;
}

/* Set on <body> for the duration of a drag (see data_table_column_resize.js) so the resize
   cursor and disabled text-selection hold even when the pointer briefly leaves the thin
   handle strip mid-drag. */
body.col-resizing {
  cursor: col-resize;
  user-select: none;
}

/* A compact column's header (qty/date/status — see compactColumnWidth in
   data_table_column_resize.js, which sizes the column to its values, not its label): wraps at
   word boundaries instead of truncating, the column being exactly as wide as its label's
   longest word. Every header cell is bottom-aligned so a single-line label lines up with a
   wrapped one's last line instead of floating mid-height next to it. */
.data-table thead th {
  vertical-align: bottom;
}

.data-table thead th.col-compact {
  white-space: normal;
}

/* Date/datetime columns — same width:1% shrink-to-fit trick as the action column, plus a
   monospace face so a column of dates lines up digit-for-digit down the rows. */
.col-date {
  width: 1%;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

/* The trailing action-button column (Prikaži etc.) is always the last cell in every table
   built this way — width: 1% is the standard trick to shrink a table-layout:auto column
   down to its content's natural width instead of sharing in the table's 100% stretch. That
   only shrinks the column to whatever the button's OWN footprint is, though — see the button
   rule right below for actually shrinking that footprint down to "just the label plus a
   little padding", which is what actually makes the column narrow, not just non-stretching. */
.data-table th:last-child,
.data-table td:last-child {
  width: 1%;
  white-space: nowrap;
}

/* Action buttons inside a data-table row (Prikaži/Otvori/Primeni/... — a plain button_to with
   no .btn-* class of its own, e.g. purchase_orders/show.html.erb's "Prikaži" linking to a
   related goods receipt) — flush like .main-menu's own .nav-pill (no border/radius/background
   at rest) instead of the generic bordered/padded chrome every other plain <button> gets,
   whose min-height (2.25rem) on top of .data-table td's own padding made every row with an
   action button noticeably taller than a row without one. Orange fill on hover, the same
   --menu-bar-active-bg .nav-pill:hover uses, not the app's usual green --accent — this reads
   as "jump to a different record" the same way the main menu's own hover already does; at
   rest it's plain --accent text instead of .nav-pill's near-white (that assumes a dark bar
   behind it, invisible on this row's light background). :not([class]) so a button with an
   intentional look of its own (warehouses/show.html.erb's "Ukloni", styled .btn-destroy)
   keeps it. */
.data-table td button {
  font-size: inherit;
}

.data-table td button:not([class]) {
  min-height: 0;
  padding: 2px 10px;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--accent);
  font-weight: 600;
}

/* Hover fills the whole cell, not just the tightly-fitted button inside it — bigger, easier
   target to aim for, and reads as "this row's action" rather than just "this one small label".
   The background lives on the <td> itself (:has, since the button sits one level down inside
   its own button_to <form>) so it covers the cell's full padding box regardless of the
   inline form/button's own, much smaller box; the button just switches to white text so it
   stays legible against that fill. */
.data-table td:has(> button:not([class])):hover button:not([class]),
.data-table td:has(> form > button:not([class])):hover button:not([class]) {
  background: var(--accent);
  color: var(--accent-ink);
}

.data-table td button:not([class]):focus-visible {
  outline: 2px solid transparent;
  box-shadow: inset 0 0 0 2px var(--focus-ring);
}

.data-table td {
  padding: 6px 12px;
  border-bottom: 1px solid var(--table-border-light);
  border-right: 0;
  border-left: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The export dropdown (data_table_exports.js) replaces the existing field-0,0 header cell —
   normally a blank cell synthesized in CSS via `.data-table thead tr::before` below — with a
   real <th>, since a ::before pseudo-element can't host an interactive button. Hiding the
   pseudo-element only when JS has actually added its replacement means a table that (for
   whatever reason) isn't recognized as exportable still gets the plain blank cell as before. */
.data-table.has-export-header thead tr::before {
  display: none;
}

.data-table th.export-header-cell {
  position: sticky;
  top: 0;
  z-index: 10;
  width: 1%;
  white-space: nowrap;
  background: var(--table-header-from);
  border-bottom: 1px solid var(--table-border);
  padding: 2px 4px;
  text-align: center;
  /* Overrides the plain .data-table th rule's overflow: hidden (there to ellipsis-truncate
     long column headers) — this cell's own content is never long, but it does host the
     absolutely-positioned .export-menu-list dropdown, which that overflow:hidden was silently
     clipping to invisible the moment it opened. */
  overflow: visible;
}

/* Checkbox column injected client-side (data_table_bulk_actions.js) as the very first column
   of every paginated data-table — genuinely new, unlike the row-number/export column, since a
   checkbox needs a real interactive element and there's no existing slot to repurpose. */
.data-table th.select-col,
.data-table td.select-col {
  width: 1%;
  white-space: nowrap;
  text-align: center;
  padding: 0px 8px;
}

.data-table th.select-col {
  position: sticky;
  top: 0;
  z-index: 10;
}

/* Double-click-to-open (data_table_row_navigation.js, replacing the "Prikaži" button) —
   hints the row itself is now the clickable target. user-select: none stops the browser's
   native double-click-selects-the-word-under-the-cursor behavior from firing at the same time
   as the navigation — scoped to just these rows (not every .data-table) since plain text
   selection elsewhere in a table is a deliberate, kept behavior (see .data-table's own comment
   above) for copying values like an invoice number. */
.data-table tbody tr.row-navigable {
  cursor: pointer;
  user-select: none;
}

/* Shown only once at least one row is checked (data_table_bulk_actions.js) — "Izmeni" (only
   when exactly one row is selected) + "Obriši" (one or more), reusing the same
   .btn-edit/.btn-destroy look every show-page's own Izmeni/Obriši buttons already have.
   Docked into the existing .filter-form toolbar (same row as the search/filter fields),
   pushed to its far right via the auto margin — .filter-form is already display:flex, so this
   is the standard flexbox "everything after me goes right" trick. Tables with no filter form
   (a handful of simple lookup lists) fall back to floating just above the table instead, via
   the plain margin-bottom below. */
.bulk-actions-bar {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.5rem;
}

/* An unconditional `display: flex` above would otherwise beat the browser's own
   `[hidden] { display: none }` rule (comparable specificity, this one declared later in the
   cascade) — without this, JS setting `bar.hidden = true` stops actually hiding anything. */
.bulk-actions-bar[hidden] {
  display: none;
}

.filter-form .bulk-actions-bar {
  margin-bottom: 0;
  margin-left: auto;
}

.export-menu {
  position: relative;
  display: inline-block;
}

.export-menu-toggle {
  min-height: 0;
  padding: 1px 6px;
  font-size: 1rem;
  line-height: 1.4;
}

.export-menu-list {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 50;
  margin: 2px 0 0;
  padding: 0;
  list-style: none;
  min-width: 90px;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
  font-size: 1rem;
  font-weight: normal;
  text-align: left;
  text-transform: none;
}

.export-menu-list a {
  display: block;
  padding: 4px 10px;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}

.export-menu-list a:hover {
  background: var(--surface-muted);
  text-decoration: underline;
}

.data-table tbody tr:nth-child(even) td {
  background-color: var(--table-stripe);
}

/* Hover highlight (Windows Forms/WPF grid light-blue) — same specificity as the nth-child
   striping above and placed after it, so it wins over both odd and even rows without needing
   !important; .selected keeps its own !important blue regardless of hover. The matching
   ::before (row-number cell) rule lives further down, after the nth-child/selected ::before
   rules it needs to outrank on the same specificity-tie-broken-by-source-order basis. */
.data-table tbody tr:hover td {
  background-color: var(--table-hover);
}

/* A row whose bulk-select checkbox (data_table_bulk_actions.js) is checked — same light-blue
   as :hover, declared right after it so it wins the same-specificity tie by source order and
   stays highlighted regardless of hover/striping. The matching ::before rule lives further
   down, after the hover/striping ::before rules it needs to outrank the same way. */
.data-table tbody tr.row-checked td {
  background-color: var(--table-hover);
}

/* Row selection (Windows Forms/WPF blue) — the .selected class isn't wired to any click
   behavior yet, this is styling only; add a small Stimulus controller if click-to-select
   should actually work. */
.data-table tbody tr.selected td {
  background-color: var(--table-selected) !important;
  color: #fff !important;
}

/* Key/value "show" panel (`<table class="list-table"><tr><th>Label:</th><td>Value</td></tr>`,
   used across ~60 show views) — flat SaaS-panel treatment matching .data-table's own flattening:
   rounded, bordered, shadowed container; overflow: hidden clips the individual <th>/<td>
   corners to that rounding, safe here (unlike .data-table) since a list-table is never
   position: sticky. */
.list-table {
  width: 100%;
  margin-top: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-subtle);
  overflow: hidden;
}

.list-table tr:last-child th,
.list-table tr:last-child td {
  border-bottom: 0;
}

.list-table th {
  background: var(--table-header-from);
  color: var(--text-muted);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0;
  padding: 6px 12px;
  text-transform: none;
  text-align: left;
  white-space: nowrap;
  width: 1%;
}

.list-table td {
  background: var(--surface);
  padding: 6px 12px;
  font-size: 0.9rem;
}

/* Generic row-number column, generated purely in CSS (no markup change needed per table) —
   a counter incremented per <tbody> row, rendered as a leading table-cell styled like the
   reference's .row-header (gray gradient, steel-gray border) via ::before on <tr>. Doubles
   as the selected-row indicator: the number swaps for a small arrow, same idea as the
   reference's row-header::after, adapted to a synthesized cell (a real element can carry
   both ::before and ::after; this one is itself a ::before, so it can't also host a nested
   ::after — swapping its own content is the equivalent). */
.data-table tbody {
  counter-reset: row-number;
}

.data-table tbody tr {
  counter-increment: row-number;
}

.data-table tbody tr::before {
  content: counter(row-number);
  display: table-cell;
  /* width: 1%, the same shrink-to-fit trick .data-table th/td:last-child already uses for the
     trailing action column — under table-layout: auto, a specified width like 3ch is a
     MINIMUM the browser allocates the column even when the actual content (a 1- or 2-digit
     row number, the overwhelmingly common case) needs less, so it read as visibly too wide.
     1% instead just tells the algorithm "don't stretch this column further than its content
     needs" and lets the counter text itself determine the width row by row — PER_PAGE_OPTIONS
     topping out at 100 per page (listable.rb) means that content never exceeds 3 digits
     anyway, so this still naturally caps out exactly where 3ch was trying to pin it, just
     without forcing that width when fewer digits are showing. */
  width: 3ch;
  white-space: nowrap;
  background: linear-gradient(to bottom, var(--table-header-from) 0%, var(--table-header-to) 100%);
  border-bottom: 1px solid var(--table-border-light);
  padding: 1px 6px;
  text-align: center;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  vertical-align: middle;
}

/* A totals row (<tfoot> that isn't the pagination footer) gets the same leading gutter cell,
   empty — without it every totals cell sits one column to the left of its column. */
.data-table tfoot:not([data-page]) tr::before {
  content: "";
  display: table-cell;
}

.data-table tbody tr:nth-child(even)::before {
  background: linear-gradient(to bottom, var(--table-stripe) 0%, var(--table-stripe-to) 100%);
}

.data-table tbody tr.selected::before {
  content: "▶";
  font-size: 1rem;
  color: var(--table-selected-arrow);
  background-color: var(--table-selected);
}

.data-table tbody tr:not(.selected):hover::before {
  background: var(--table-hover-pseudo);
}

.data-table tbody tr.row-checked:not(.selected)::before {
  background: var(--table-hover-pseudo);
}

.data-table thead tr::before {
  content: "";
  display: table-cell;
  /* Must match .data-table tbody tr::before's own width — auto table layout treats thead/tbody
     as one shared column structure, so a mismatch here makes the browser reconcile the two
     into some wider width than either rule alone asked for. Same width: 1% shrink-to-fit
     reasoning as the body rule. */
  width: 3ch;
  white-space: nowrap;
  background: linear-gradient(to bottom, var(--table-header-from) 0%, var(--table-header-to) 100%);
  border-bottom: 1px solid var(--table-border);
  position: sticky;
  top: 0;
  z-index: 10;
}

/* Barcode scanner confirmation screens (WMS putaway/picking/cycle-count) — large touch
   targets and a full-element flash so a warehouse worker can use this from a handheld
   device without precise tapping, and get an unmistakable pass/fail signal per scan. */
.scanner-step {
  font-size: 1.5rem;
  font-weight: bold;
  text-align: center;
  margin-bottom: 1rem;
}

.scanner-input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  font-size: 2rem;
  font-family: monospace;
  text-align: center;
  padding: 1rem;
}

.scanner-status {
  font-size: 1.5rem;
  text-align: center;
  min-height: 2.5rem;
}

/* Feedback for a packaging-level scan (scanner_controller.js#applyPackaging) — "Prepoznato
   pakovanje: Paleta (50 kom)" after scanning a case/pallet barcode instead of a plain unit one.
   Empty (no min-height reservation, unlike .scanner-status above) when nothing was recognized,
   since most scans on a product with no packaging barcodes will never populate this at all. */
.scanner-packaging-hint {
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  color: var(--accent);
}

/* Clears every scanned step and goes back to step one without a page reload
   (scanner_controller.js#restart) — for a scan that matched the wrong physical item on purpose,
   or a worker who just wants to redo a count from zero. Text-link styling, not a full button, so
   it doesn't compete with the actual scan-confirm affordances (the input, the submit button). */
.scanner-restart {
  display: block;
  margin: 0 auto 1rem;
  background: none;
  border: none;
  color: var(--text-muted);
  text-decoration: underline;
  font-size: 0.9rem;
  cursor: pointer;
  padding: 0.25rem;
}

.scanner-restart:hover {
  color: var(--accent);
}

/* html5-qrcode's own live camera preview (scanner_controller.js#startCamera) — it renders its
   own <video>/<canvas>/scan-region overlay into this element; the rules here only shape the
   container around that (rounded corners, a cap so a landscape camera feed doesn't blow out a
   narrow phone screen) so it reads as an intentional part of this screen, not a raw embed. */
.scanner-camera {
  margin-bottom: 1rem;
  border-radius: var(--radius);
  overflow: hidden;
  border: 2px solid var(--border);
  background: #000;
}

.scanner-camera video {
  display: block;
  width: 100%;
  max-height: 320px;
  object-fit: cover;
}

/* Press-and-hold "trigger" (scanner_controller.js#armCamera/disarmCamera) — decoding only runs
   while this is held down, same idea as a real handheld scanner's trigger, instead of the
   camera continuously trying to decode on its own. user-select/touch-action: none so a long
   press doesn't trigger text selection or a scroll/zoom gesture on the phone while held. */
.scanner-trigger {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin-bottom: 1rem;
  padding: 1rem;
  border: none;
  border-radius: var(--radius);
  background: var(--accent);
  color: #fff;
  font: inherit;
  font-size: 1.1rem;
  font-weight: 700;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  transition: transform 0.05s ease, filter 0.05s ease;
}

.scanner-trigger:active {
  transform: scale(0.97);
  filter: brightness(0.9);
}

.scanner-trigger[hidden] {
  display: none;
}

/* Why the camera preview above didn't appear — insecure context (http, not https/localhost),
   permission denied, no camera, library blocked, ... (scanner_controller.js#describeCameraError)
   — shown instead of just leaving an unexplained plain text field. */
.scanner-camera-hint {
  margin: -0.5rem 0 1rem;
  padding: 0.6rem 0.8rem;
  border-radius: var(--radius);
  background: var(--status-warning-bg);
  color: var(--status-warning-fg);
  font-size: 1rem;
  text-align: center;
}

.scanner-flash-success,
.scanner-flash-error {
  animation: scanner-flash 0.4s ease-out;
}

.scanner-flash-success {
  --scanner-flash-color: #9cbfa8;
}

.scanner-flash-error {
  --scanner-flash-color: #d19d97;
}

@keyframes scanner-flash {
  0% { background-color: transparent; }
  30% { background-color: var(--scanner-flash-color, #cccccc); }
  100% { background-color: transparent; }
}

.map-pin-icon {
  background: none;
  border: none;
}

/* Category > Warehouse > Invoice > Item drill-down (partners#analytics). */
.analytics-summary {
  float: right;
  font-weight: normal;
  font-size: 1rem;
  color: var(--text-muted);
}

.analytics-tree summary {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  cursor: pointer;
  padding: 0.3rem 0;
}

.analytics-tree details {
  border-bottom: 1px solid var(--border-muted);
}

.analytics-tree details details {
  padding-left: 1.25rem;
  border-bottom: none;
}

.analytics-label {
  flex: 1;
}

.analytics-value {
  margin-left: auto;
  white-space: nowrap;
}

.analytics-items {
  list-style: none;
  margin: 0 0 0.5rem;
  padding-left: 1.25rem;
}

.analytics-items li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.2rem 0;
  font-size: 1rem;
}

.badge {
  display: inline-block;
  font-size: 1rem;
  padding: 0.12rem 0.45rem;
  border: 1px solid var(--border);
  white-space: nowrap;
}

.badge-warehouse,
.badge-discount,
.badge-promo {
  background: var(--surface);
  color: var(--text);
}

/* border-radius: var(--radius), not the old full 999px pill — the one place still holding
   out on the "minimal rounding, not the fully pill-shaped SaaS look" call made everywhere
   else in this file (see the theme comment at the top). */
.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.18rem 0.65rem 0.18rem 0.5rem;
  border-radius: var(--radius-pill);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  line-height: 1.4;
}

.status-badge svg {
  width: 0.85em;
  height: 0.85em;
  flex-shrink: 0;
}

.status-badge-success { background: var(--status-success-bg); color: var(--status-success-fg); }
.status-badge-danger { background: var(--status-danger-bg); color: var(--status-danger-fg); }
.status-badge-warning { background: var(--status-warning-bg); color: var(--status-warning-fg); }
.status-badge-info { background: var(--status-info-bg); color: var(--status-info-fg); }
.status-badge-neutral { background: var(--status-neutral-bg); color: var(--status-neutral-fg); }

/* Year-over-year sales report (partners#sales). */
.sales-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin-top: 1rem;
}

.sales-period {
  color: var(--text-muted);
  font-size: 1rem;
  white-space: nowrap;
}

.category-bucket legend {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.count-badge {
  background: var(--surface);
  color: var(--text);
  font-size: 1rem;
  font-weight: normal;
  padding: 0.05rem 0.5rem;
  border: 1px solid var(--border);
}

.sales-table td,
.sales-table th {
  vertical-align: middle;
}

.sales-subtable {
  margin: 0.5rem 0 0.5rem 1rem;
  box-shadow: none;
  border: 1px solid var(--border-muted);
}

.sales-table details summary {
  cursor: pointer;
}

.sales-total-row th {
  border-top: 2px solid var(--border);
}

/* Every column in a data-table's closing "Ukupno" row — including the label cell itself,
   even when it spans several columns via colspan — right-aligned, so the label sits flush
   against the totals immediately to its right instead of pinned to the table's left edge.
   These cells are <th> (semantically a row header for the totals beside it), which otherwise
   silently inherits .data-table th's column-LABEL styling below — 11px uppercase muted text,
   even position: sticky — wrong for a totals row sitting in the table body. Every property
   here exists to cancel one of those out, not to invent new styling from scratch. A tinted
   background (var(--surface-muted), the same tone .data-table already uses for its own hover/
   stripe rows) plus bold weight reads as "this row is different" without reaching for a status
   color (success/danger green/red) that would misleadingly suggest the total itself is
   good/bad news, the way an actual status color is used everywhere else in this app. */
.total-row th,
.total-row td {
  position: static;
  text-align: right;
  font-size: inherit;
  font-weight: 700;
  text-transform: none;
  letter-spacing: normal;
  color: var(--text);
  background: var(--surface-muted);
  border-top: 2px solid var(--border);
}

.sales-diff.diff-positive {
  color: var(--success);
}

.sales-diff.diff-negative {
  color: var(--danger);
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.legend-swatch {
  display: inline-block;
  width: 0.8rem;
  height: 0.8rem;
  border: 1px solid var(--border);
}

/* Partner loyalty tab. */
.loyalty-summary {
  display: flex;
  gap: 2rem;
  margin: 1rem 0;
}

.loyalty-stat {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.loyalty-stat-label {
  font-size: 1rem;
  color: var(--text-muted);
}

.loyalty-stat-value {
  font-size: 1.4rem;
  font-weight: bold;
}

.loyalty-stat-value.loyalty-expiring {
  color: var(--warning);
}

/* Partner kartica tab (aging of open AR invoices). */
.aging-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 1rem;
}

.aging-title {
  font-weight: bold;
}

.aging-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  font-size: 1rem;
  color: var(--text-muted);
}

.aging-legend .legend-swatch {
  border-radius: 50%;
}

.aging-bar {
  display: flex;
  height: 10px;
  border-radius: 5px;
  overflow: hidden;
  background: var(--surface-muted);
  margin: 0.5rem 0 1rem;
}

.aging-bar-segment {
  height: 100%;
}

.aging-pill {
  display: inline-block;
  font-size: 1rem;
  font-weight: normal;
  padding: 0.1rem 0.5rem;
  border: 1px solid var(--border);
  white-space: nowrap;
}

.aging-max-late,
.aging-late {
  color: var(--danger);
  font-weight: bold;
}

.aging-dot-d0_7, .aging-bg-d0_7 { background: #fff; }
.aging-dot-d8_14, .aging-bg-d8_14 { background: #eee; }
.aging-dot-d15_21, .aging-bg-d15_21 { background: #ddd; }
.aging-dot-d22_30, .aging-bg-d22_30 { background: #ccc; }
.aging-dot-d31_60, .aging-bg-d31_60 { background: #999; }
.aging-dot-d61_365, .aging-bg-d61_365 { background: #666; }
.aging-dot-d365_plus, .aging-bg-d365_plus { background: #000; }

.aging-summary-table th .aging-pill.aging-bg-d0_7 { background: #fff; color: #000; }
.aging-summary-table th .aging-pill.aging-bg-d8_14 { background: #eee; color: #000; }
.aging-summary-table th .aging-pill.aging-bg-d15_21 { background: #ddd; color: #000; }
.aging-summary-table th .aging-pill.aging-bg-d22_30 { background: #ccc; color: #000; }
.aging-summary-table th .aging-pill.aging-bg-d31_60 { background: #999; color: #fff; }
.aging-summary-table th .aging-pill.aging-bg-d61_365 { background: #666; color: #fff; }
.aging-summary-table th .aging-pill.aging-bg-d365_plus { background: #000; color: #fff; }

/* Sporo obrtne zalihe — "koliko je hitno" boja po redu, ne identitet serije (vidi ABC/RUC
   grafike, koji koriste kategoričku paletu za identitet, ne za hitnost). */
.stale-pill {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--border);
  font-size: 1rem;
  font-weight: bold;
}

.stale-d0_30 { background: #fff; color: #000; }
.stale-d31_60 { background: #eee; color: #000; }
.stale-d61_90 { background: #ccc; color: #000; }
.stale-d91_180 { background: #999; color: #fff; }
.stale-d180_plus { background: #666; color: #fff; }
.stale-never { background: #000; color: #fff; }

/* Isticanje dokumenata (TMS) — ista vizuelna logika kao .stale-pill, obrnut smer: manje dana
   do isteka je ozbiljnije, ne više dana od nečega. */
.expiry-pill {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--border);
  font-size: 1rem;
  font-weight: bold;
}

.expiry-d91_plus { background: #fff; color: #000; }
.expiry-d61_90 { background: #ccc; color: #000; }
.expiry-d31_60 { background: #999; color: #fff; }
.expiry-d0_30 { background: #666; color: #fff; }
.expiry-expired { background: #000; color: #fff; }

/* ABC analiza — klasa vrednosti (A = najvažniji, ne "hitnost" kao gornja paleta). */
.abc-pill { display: inline-block; padding: 0.15rem 0.5rem; border: 1px solid var(--border); font-size: 1rem; font-weight: bold; }
.abc-pill-a { background: #fff; color: #000; }
.abc-pill-b { background: #ccc; color: #000; }
.abc-pill-c { background: #666; color: #fff; }

/* Aging tables (shared/_partner_aging on Partner > Kartica; the combined table on the
   Accounting aging report) — clickable partner rows expanding into their own invoice rows. */
.aging-report-toolbar {
  margin-top: 1rem;
}

.btn-toggle-all {
  float: right;
  margin-bottom: 1rem;
}

.aging-partner-row {
  cursor: pointer;
}

.aging-partner-row:hover {
  background: var(--surface-muted);
}

.aging-invoice-row td {
  color: var(--text-muted);
}

.aging-row-collapsed {
  display: none;
}

/* The partner row is the only meaningful "#1" in each table — suppress the auto row-number
   cell (see .data-table tbody tr::before) on the invoice rows underneath it. */
.aging-summary-table tbody tr.aging-invoice-row {
  counter-increment: none;
}

.aging-summary-table tbody tr.aging-invoice-row::before {
  content: "";
}



/* Flash toasts (shared/_flash partial, rendered once from the layout) — fixed overlay so they
   never push page content down, unlike the old inline <p> flash lines they replaced. */
#flash-messages {
  position: fixed;
  top: calc(var(--menu-bar-height) + 0.75rem);
  right: 1rem;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  max-width: 22rem;
}

/* Card treatment (radius + elevated shadow, same language as fieldset/.dashboard-panel) plus a
   colored left accent bar in --flash-accent (already set per-variant below) instead of the old
   flat, all-around var(--text) border — a floating toast reads better with a status color you
   can recognize at a glance than a heavy neutral outline. */
.flash-toast {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--flash-accent);
  border-radius: var(--radius);
  box-shadow: var(--shadow-hover);
  padding: 0.65rem 0.75rem;
  font-size: 0.875rem;
  color: var(--text);
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.25s ease, transform 0.25s ease;
}

.flash-toast-hide {
  opacity: 0;
  transform: translateY(-0.5rem);
}

.flash-toast::before {
  flex-shrink: 0;
  font-weight: bold;
}

.flash-toast-notice {
  --flash-accent: var(--success);
}

.flash-toast-notice::before {
  content: "\2713";
  color: var(--success);
}

.flash-toast-alert {
  --flash-accent: var(--danger);
}

.flash-toast-alert::before {
  content: "\2715";
  color: var(--danger);
}

.flash-toast-message {
  flex: 1;
  padding-top: 0.05rem;
}

.flash-toast-close {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
}

.flash-toast-close:hover {
  color: var(--text);
}

/* Login / password-reset pages (layouts/auth.html.erb) — no bars, one centered card on the
   app ground with two soft coral glows and a faint board of bars behind it (the logo's own
   motif), everything through the theme tokens. */
.auth-body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(circle at 15% 12%, var(--auth-glow-1), transparent 42%),
    radial-gradient(circle at 85% 88%, var(--auth-glow-2), transparent 45%),
    var(--app-bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.4;
}

:root {
  --auth-glow-1: rgba(255, 93, 74, 0.18);
  --auth-glow-2: rgba(255, 170, 120, 0.22);
}

:root[data-theme="dark"] {
  --auth-glow-1: rgba(255, 122, 105, 0.14);
  --auth-glow-2: rgba(255, 170, 120, 0.07);
}

.auth-page {
  width: 100%;
  padding: 2rem 1rem;
}

.auth-card {
  max-width: 400px;
  margin: 0 auto;
  padding: 2.5rem 2.25rem 2.25rem;
  border: 1px solid var(--border);
  border-radius: 24px;
  background: var(--surface);
  box-shadow: var(--shadow-float);
  text-align: center;
}

.auth-logo {
  display: block;
  height: 44px;
  width: auto;
  margin: 0 auto 1.75rem;
  color: var(--text);
}

.auth-title {
  margin: 0 0 0.35rem;
  padding: 0;
  border: none;
  font-family: var(--font-display);
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
}

.auth-subtitle {
  margin: 0 0 1.75rem;
  color: var(--text-muted);
  font-size: 1rem;
}

.auth-form {
  text-align: left;
}

.auth-field {
  margin-bottom: 1rem;
}

.auth-field label {
  display: block;
  margin: 0 0 0.35rem;
  color: var(--text-muted);
  font-size: 0.9rem;
  font-weight: 600;
}

.auth-field input,
.auth-field select {
  width: 100%;
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-size: 1rem;
}

/* Both login fields are required — no amber "mandatory" edge on a two-field form. */
.auth-field input:required:not([type="checkbox"]):not([type="radio"]) {
  border-color: var(--border);
}

.auth-field input:focus,
.auth-field input:focus:required:not([type="checkbox"]):not([type="radio"]),
.auth-field select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* input[type="submit"].auth-submit matches the base input rule's (0,2,1) specificity so the
   coral fill isn't overridden by the plain white input background. */
.auth-submit,
input[type="submit"].auth-submit {
  display: block;
  width: 100%;
  margin-top: 0.75rem;
  padding: 0.75rem;
  border: 0;
  border-radius: 12px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 10px 22px -10px var(--accent);
  transition: background-color 0.15s ease, transform 0.1s ease;
}

.auth-submit:hover {
  background: var(--accent-strong);
}

.auth-submit:active {
  transform: scale(0.98);
}

.auth-submit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.auth-link {
  display: inline-block;
  margin-top: 1.25rem;
  color: var(--accent-strong);
  font-size: 0.95rem;
  text-decoration: none;
}

.auth-link:hover {
  text-decoration: underline;
}

/* Warehouse floor-plan map (sirius_wms locations#map) is a self-contained D3/SVG page with
   its own inline <style> (see the view) — not themed through this stylesheet's tokens, since
   it's a full-viewport visualization rather than a page built from this app's usual
   fieldset/data-table furniture. */

/* ── Home (home#index, HomeDashboard): greeting and quick actions, four figures that link to
   their detail, the sales curve next to "Čeka tebe" (unread notifications), then every
   shortcut the app has, grouped. */
.home-hello {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.home-hello h1 {
  margin: 0;
  font-size: 2.3rem;
}

.home-hello p {
  margin: 0.3rem 0 0;
  color: var(--text-muted);
  font-size: 1.05rem;
}

.home-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.home-kpis {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1rem;
  margin-bottom: 1rem;
}

.home-kpi,
.home-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.home-kpi {
  display: grid;
  gap: 0.4rem;
  align-content: start;
  padding: 1rem 1.1rem 0.9rem;
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.home-kpi:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-hover);
  text-decoration: none;
}

.home-kpi-label {
  color: var(--text-muted);
  font-size: 0.9rem;
  font-weight: 600;
}

.home-kpi-value {
  font-family: var(--font-display);
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.home-kpi-note {
  color: var(--text-muted);
  font-size: 0.85rem;
  font-style: normal;
  font-weight: 600;
}

.home-kpi-note.is-up {
  color: var(--success);
}

.home-kpi-note.is-down {
  color: var(--danger);
}

.home-spark {
  width: 100%;
  height: 30px;
  margin-top: 0.25rem;
  color: var(--accent);
}

.home-kpi-bar {
  display: block;
  height: 6px;
  margin: 0.75rem 0 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--surface-muted);
  overflow: hidden;
}

.home-kpi-bar i {
  display: block;
  height: 100%;
  background: var(--danger);
}

.home-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: 1rem;
  margin-bottom: 1rem;
}

.home-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 1rem 1.1rem 0.4rem;
}

.home-card-head h2 {
  margin: 0;
  font-size: 1.15rem;
}

.home-card-head a {
  color: var(--accent-strong);
  font-weight: 600;
  font-size: 0.9rem;
}

.home-card-head span {
  color: var(--text-muted);
  font-size: 0.9rem;
}

.home-chart-wrap {
  padding: 0.25rem 1rem 1rem;
}

.home-chart {
  display: block;
  width: 100%;
  height: auto;
}

.home-chart-grid {
  stroke: var(--border);
}

.home-chart-label {
  fill: var(--text-muted);
  font-size: 11px;
}

.home-chart-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.4;
  stroke-linejoin: round;
}

.home-chart-dot {
  fill: var(--accent);
  stroke: var(--surface);
  stroke-width: 2;
}

.home-chart-fill-top {
  stop-color: var(--accent);
  stop-opacity: 0.28;
}

.home-chart-fill-bottom {
  stop-color: var(--accent);
  stop-opacity: 0;
}

.home-waiting {
  padding: 0.25rem 0.5rem 0.75rem;
}

.home-empty {
  margin: 0;
  padding: 1rem 1.1rem 1.5rem;
  color: var(--text-muted);
  line-height: 1.5;
}

.home-directory-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.25rem 1.5rem;
  padding: 0.75rem 1.1rem 1.25rem;
}

.home-directory-grid h3 {
  margin: 0 0 0.4rem;
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.home-directory-grid a {
  display: block;
  padding: 0.25rem 0;
  color: var(--text);
}

.home-directory-grid a:hover {
  color: var(--accent-strong);
  text-decoration: none;
}

.home-directory-grid summary {
  margin-top: 0.25rem;
  color: var(--accent-strong);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.home-directory-grid summary::-webkit-details-marker {
  display: none;
}

.home-directory-grid details[open] summary {
  display: none;
}

@media (max-width: 1100px) {
  .home-kpis,
  .home-directory-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .home-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Appears (via dirty_form_controller.js) the moment any field in a form it wraps changes —
   sticky so it stays visible while scrolling a long form like the employee edit page. */
/* Opt-in (via class, not the bare `fieldset` element — many other forms rely on their fields
   stacking vertically, e.g. a nested-fields line-item block) — lets a fieldset's direct field
   wrappers flow left-to-right and wrap, filling the page width instead of stacking in one
   narrow column no matter how wide the viewport is. */
fieldset.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 0.25rem 2rem;
  align-items: start;
}

fieldset.form-grid > legend {
  grid-column: 1 / -1;
}

.dirty-form-bar[hidden] {
  display: none;
}

.dirty-form-bar {
  position: sticky;
  top: var(--menu-bar-height); /* sits right below the sticky .menu-bar instead of under it */
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem 1rem;
  margin-bottom: 1rem;
  background: var(--status-warning-bg);
  color: var(--status-warning-fg);
  border-radius: 6px;
  font-weight: 600;
}

.dirty-form-bar button {
  font-weight: 600;
}

/* Kanban-style dispatch board (SiriusTms::ShipmentsController#dispatch_board) — one column per
   milestone status, cards clickable to advance to the next one. Global, not per-engine, so any
   future board (a picking/putaway queue, say) can reuse the same classes. */
.dispatch-board {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  padding-bottom: 0.5rem;
}

.dispatch-board-column {
  flex: 0 0 260px;
  background: var(--surface-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem;
}

.dispatch-board-column-header {
  font-weight: 600;
  margin-bottom: 0.75rem;
  display: flex;
  justify-content: space-between;
  color: var(--text);
}

.dispatch-board-column-count {
  color: var(--text-muted);
  font-weight: 400;
}

.dispatch-board-card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-subtle);
  padding: 0.6rem 0.75rem;
  margin-bottom: 0.6rem;
  text-decoration: none;
  color: var(--text);
}

.dispatch-board-card:hover {
  border-color: var(--accent);
}

.dispatch-board-card-title {
  font-weight: 600;
  margin-bottom: 0.3rem;
}

.dispatch-board-card-meta {
  font-size: 1rem;
  color: var(--text-muted);
}

.dispatch-board-card-advance {
  margin-top: 0.5rem;
}

.dispatch-board-card-advance button {
  font-size: 1rem;
  padding: 0.2rem 0.5rem;
}

/* ── Tags (shared/_tags partial) — free-form labels on any Taggable record. ────────────────── */

.tag-list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin: 0.75rem 0;
}

/* shared/_tag_chips (read-only, index-table cells) reuses .tag-list's own layout but without
   the block-level breathing room meant for sitting inside a <fieldset> — a table cell already
   has its own padding via .data-table td. */
.tag-list-compact {
  margin: 0;
}

/* shared/_tags (the editable widget on a record's show page) — no longer boxed in a <fieldset>,
   just a plain flush line sitting directly above the record's own #resource-form-style display.
   Default bottom margin gives it breathing room from the form below (Product's show page: no
   module-nav row above it, so this is its own line). When it lands inside a .module-nav row
   instead (Partner's show page, next to the Pregled/Analitika/... tabs), margin-right: auto
   pushes it to the left edge while the tabs stay bunched right — same "primary action stays
   left, everything else right" trick .module-nav's own comment describes above. */
.tag-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.75rem;
}

.module-nav .tag-line {
  margin: 0 auto 0 0;
}

/* Same padding/weight as .status-badge, but a full pill (border-radius: 999px) — deliberately
   modeled directly on Proxmox VE's own tag styling (proxmox-widget-toolkit's "full" tag style,
   src/css/ext6-pmx.css — small rounded rectangle, not a pill: border-radius 3px, padding
   1px 6px there). Background color comes inline per chip (application_helper.rb's
   tag_chip_style, a direct Ruby port of Proxmox's own Utils.js stringToRGB — same hash, same
   70%-toward-white blend, so a given tag name lands on the exact color Proxmox itself would
   show) and .tag-chip-light-text/-dark-text (tag_chip_class, porting getTextContrastClass'
   simplified SAPC calculation) picks whichever text color actually contrasts against that
   particular background instead of assuming one fixed text color works for every hash output. */
.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.0625rem 0.375rem;
  border-radius: 3px;
  font-size: 1rem;
  font-weight: 600;
  white-space: nowrap;
}

.tag-chip-light-text {
  color: #fff;
}

.tag-chip-dark-text {
  color: #000;
}

.tag-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

.tag-chip-remove:hover {
  background: rgba(0, 0, 0, 0.1);
}

.tag-add-form {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.tag-add-input {
  min-height: 1.9rem;
  width: 11rem;
  padding: 0.2rem 0.55rem;
  font-size: 1rem;
}

/* Smaller than a normal .btn-edit so it doesn't tower over the compact input/chips next to
   it — this is the only place .btn-edit sits inline with a text field instead of in a button
   row of its own. */
.tag-add-form .btn-edit {
  min-height: 1.9rem;
  padding: 0.2rem 0.7rem;
  font-size: 1rem;
}

/* ===========================================================================================
   Kasa (POS) — layouts/pos.html.erb. A full-screen till instead of a page: a dark top bar with
   the till's setup and status, the product picker on the left, the receipt being rung up on
   the right, and a payment dialog. Everything sized for touch (big targets, big numbers) and
   built on the app's own tokens, so light/dark follow the rest of the app.
   =========================================================================================== */
.pos-body {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  font-size: 1rem;
}

.pos {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.pos-body .currency_s {
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}

.pos-body [hidden] {
  display: none !important;
}

/* --- top bar ------------------------------------------------------------------------------ */
.pos-topbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem 1rem;
  background: var(--menu-bar-bg);
  color: var(--menu-bar-text);
  flex-wrap: wrap;
}

.pos-brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1.15rem;
  font-weight: 700;
}

.pos-brand-mark {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius);
  background: var(--accent);
  color: #fff;
}

.pos-setting {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.pos-setting label {
  color: var(--menu-bar-text-muted);
  font-size: 0.8rem;
  font-weight: 500;
}

.pos-setting select {
  max-width: 13rem;
  min-height: 2rem;
  padding: 0.2rem 0.5rem;
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--menu-bar-border);
  color: var(--menu-bar-text);
}

.pos-setting select option {
  color: var(--text);
  background: var(--surface);
}

.pos-status-pills {
  display: flex;
  gap: 0.4rem;
}

.pos-pill {
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.pos-pill-ok { background: rgba(48, 186, 120, 0.2); color: #8ff0bf; }
.pos-pill-warning { background: rgba(254, 124, 63, 0.2); color: #ffc39a; }
.pos-pill-danger { background: rgba(224, 57, 62, 0.25); color: #ffb4b4; }

.pos-topbar-end {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-left: auto;
}

.pos-cashier { color: var(--menu-bar-text-muted); font-size: 0.9rem; }

.pos-clock {
  font-size: 1.1rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.pos-exit {
  padding: 0.3rem 0.8rem;
  border: 1px solid var(--menu-bar-border);
  border-radius: var(--radius);
  color: var(--menu-bar-text);
  text-decoration: none;
}

.pos-exit:hover { background: var(--menu-bar-hover-bg); }

/* --- main: catalog | ticket --------------------------------------------------------------- */
.pos-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(360px, 34rem);
  flex: 1;
  min-height: 0;
}

.pos-catalog {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1rem;
  min-height: 0;
}

.pos-scan {
  display: flex;
  gap: 0.5rem;
}

.pos-body input.pos-scan-input:not([type="checkbox"]) {
  flex: 1;
  width: auto;
  min-height: 3.25rem;
  font-size: 1.25rem;
  padding: 0.5rem 1rem;
}

.pos-body .pos-scan-button {
  min-height: 3.25rem;
  padding: 0 1.5rem;
  font-size: 1.1rem;
  font-weight: 600;
}

.pos-catalog-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  color: var(--text-muted);
  font-size: 0.85rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.pos-link {
  color: var(--accent);
  text-transform: none;
  letter-spacing: 0;
}

.pos-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10.5rem, 1fr));
  grid-auto-rows: min-content;
  gap: 0.6rem;
  overflow-y: auto;
  padding-bottom: 1rem;
}

.pos-tile-form {
  display: block;
}

.pos-body .pos-tile {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.2rem;
  width: 100%;
  height: 100%;
  min-height: 6.5rem;
  padding: 0.65rem 0.75rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-subtle);
  text-align: left;
}

.pos-body .pos-tile:hover:not(:disabled) {
  border-color: var(--accent);
  background: var(--accent-muted);
}

.pos-body .pos-tile:active:not(:disabled) { transform: scale(0.98); }
.pos-body .pos-tile:disabled { cursor: not-allowed; opacity: 0.5; }

.pos-tile-name {
  font-weight: 600;
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pos-tile-sku { color: var(--text-muted); font-size: 0.75rem; }

.pos-tile-foot {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: auto;
}

.pos-tile-price { font-size: 1.05rem; font-weight: 700; }
.pos-tile-stock { color: var(--text-muted); font-size: 0.75rem; }
.pos-tile-empty .pos-tile-stock { color: var(--danger); }

.pos-empty {
  grid-column: 1 / -1;
  margin: 2rem auto;
  color: var(--text-muted);
  text-align: center;
}

/* --- ticket (the receipt being rung up) --------------------------------------------------- */
.pos-ticket {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--surface);
  border-left: 1px solid var(--border);
}

.pos-customer {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface-muted);
}

.pos-customer-info {
  display: flex;
  flex-direction: column;
  flex: 1;
  font-size: 0.85rem;
}

.pos-customer-info strong { font-size: 1rem; }
.pos-customer-info span { color: var(--text-muted); }

.pos-customer-form {
  display: flex;
  gap: 0.4rem;
  flex: 1;
}

.pos-body .pos-customer-form input[type="text"] {
  flex: 1;
  width: auto;
}

.pos-lines {
  flex: 1;
  overflow-y: auto;
}

.pos-line {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 6.5rem auto auto;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 1rem;
  border-bottom: 1px solid var(--border-muted);
}

.pos-line-last { background: var(--accent-muted); }

.pos-line-name {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.pos-line-name span {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pos-line-name small { color: var(--text-muted); font-size: 0.75rem; }

.pos-qty {
  display: flex;
  align-items: center;
  gap: 0.2rem;
}

.pos-qty form { display: inline; }

.pos-body .pos-qty-button,
.pos-body .pos-icon-button {
  min-width: 2.25rem;
  min-height: 2.25rem;
  padding: 0;
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1;
}

.pos-body .pos-icon-button {
  border-color: transparent;
  background: transparent;
  color: var(--text-muted);
}

.pos-body .pos-icon-button:hover { color: var(--danger); background: var(--surface-muted); }

.pos-body input.pos-qty-input:not([type="checkbox"]) {
  width: 3.5rem;
  min-height: 2.25rem;
  padding: 0.2rem;
  text-align: center;
  font-weight: 600;
  -moz-appearance: textfield;
}

.pos-qty-input::-webkit-inner-spin-button,
.pos-qty-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }

.pos-qty-short .pos-qty-input { border-color: var(--danger); color: var(--danger); }

.pos-line-total {
  font-weight: 700;
  text-align: right;
}

.pos-summary {
  padding: 0.75rem 1rem 1rem;
  border-top: 1px solid var(--border);
  background: var(--surface-muted);
}

.pos-points {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
  font-size: 0.85rem;
}

.pos-body .pos-points input { width: 6rem; }

.pos-totals {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.15rem 1rem;
  margin: 0 0 0.5rem;
}

.pos-totals dt { color: var(--text-muted); }
.pos-totals dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
.pos-totals .pos-totals-minor { font-size: 0.8rem; }

.pos-earn {
  margin: 0 0 0.5rem;
  color: var(--success);
  font-size: 0.85rem;
}

.pos-due {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 0.6rem 0.9rem;
  margin-bottom: 0.75rem;
  border-radius: var(--radius);
  background: var(--menu-bar-bg);
  color: var(--menu-bar-text);
}

.pos-due > span { font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.05em; }
.pos-due strong { font-size: 2.25rem; line-height: 1.1; font-variant-numeric: tabular-nums; }

.pos-blocker {
  margin: 0 0 0.6rem;
  padding: 0.45rem 0.7rem;
  border-radius: var(--radius);
  background: var(--status-warning-bg);
  color: var(--status-warning-fg);
  font-size: 0.85rem;
}

.pos-actions {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
}

.pos-actions form { display: contents; }

.pos-body .pos-button {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;
  min-height: 3.75rem;
  padding: 0.4rem 0.6rem;
  font-size: 1.05rem;
  font-weight: 700;
  text-decoration: none;
}

.pos-body .pos-button kbd {
  font: inherit;
  font-size: 0.7rem;
  font-weight: 500;
  opacity: 0.75;
}

.pos-body .pos-button-pay {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.pos-body .pos-button-pay:hover:not(:disabled) { background: var(--accent); filter: brightness(1.08); }
.pos-body .pos-button-secondary { background: var(--surface); }
.pos-body .pos-button:disabled { opacity: 0.45; cursor: not-allowed; }

/* --- payment dialog ----------------------------------------------------------------------- */
.pos-dialog {
  width: min(46rem, calc(100vw - 2rem));
  max-height: calc(100dvh - 2rem);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) * 2);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-hover);
}

.pos-dialog::backdrop { background: rgba(0, 0, 0, 0.55); }

.pos-pay-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem 1.25rem;
  background: var(--menu-bar-bg);
  color: var(--menu-bar-text);
}

.pos-body .pos-pay-head h2 { margin: 0; padding: 0; border: 0; background: none; color: inherit; font-size: 1.4rem; }

.pos-pay-due {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

.pos-pay-due > span { font-size: 0.75rem; text-transform: uppercase; opacity: 0.75; }
.pos-pay-due strong { font-size: 2rem; line-height: 1.1; }

.pos-pay-modes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.5rem;
  padding: 1rem 1.25rem 0;
}

.pos-body .pos-pay-modes button {
  min-height: 3rem;
  font-size: 1.05rem;
  font-weight: 600;
}

.pos-body .pos-pay-modes button.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.pos-pay-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 15rem;
  gap: 1.25rem;
  padding: 1rem 1.25rem;
}

.pos-pay-fields {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.pos-pay-field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-weight: 600;
}

.pos-body .pos-pay-field input:not([type="checkbox"]) {
  width: 100%;
  min-height: 3.25rem;
  font-size: 1.6rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.pos-body .pos-pay-field input[readonly] { background: var(--surface-muted); }

.pos-pay-quick {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
}

.pos-body .pos-pay-quick button { min-height: 2.75rem; font-weight: 600; }

.pos-pay-result {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.3rem 1rem;
  align-items: baseline;
  margin: 0;
}

.pos-pay-result dt { color: var(--text-muted); }
.pos-pay-result dd { margin: 0; text-align: right; font-size: 1.25rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.pos-pay-result .pos-pay-change { font-size: 2rem; color: var(--success); }

.pos-pay-error {
  min-height: 1.25rem;
  margin: 0;
  color: var(--danger);
  font-weight: 600;
}

.pos-keypad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
  align-content: start;
}

.pos-body .pos-keypad button {
  min-height: 3.4rem;
  font-size: 1.4rem;
  font-weight: 600;
}

.pos-keypad-wide { grid-column: span 2; }

.pos-pay-actions {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 0.5rem;
  padding: 0 1.25rem 1.25rem;
}

/* --- receipt screen ----------------------------------------------------------------------- */
.pos-receipt-screen {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 2rem;
  height: 100%;
  padding: 2rem 1rem;
  overflow-y: auto;
}

.pos-receipt-side {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  width: 20rem;
}

.pos-change {
  display: flex;
  flex-direction: column;
  padding: 1.25rem;
  border-radius: calc(var(--radius) * 2);
  background: var(--menu-bar-bg);
  color: var(--menu-bar-text);
}

.pos-change > span { text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.8; }
.pos-change strong { font-size: 3rem; line-height: 1.1; color: #8ff0bf; font-variant-numeric: tabular-nums; }
.pos-change-none strong { color: inherit; }
.pos-change small { opacity: 0.75; }

.pos-receipt-actions {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.pos-receipt-form { display: contents; }

.pos-hint { color: var(--text-muted); font-size: 0.8rem; margin: 0; }

.pos-paper {
  width: 80mm;
  max-width: 100%;
  padding: 1rem 0.9rem 1.5rem;
  background: #fff;
  color: #111;
  box-shadow: var(--shadow-hover);
  font-family: "Courier New", monospace;
  font-size: 0.78rem;
}

.pos-paper p { margin: 0 0 0.4rem; }
.pos-paper-center { text-align: center; }
.pos-paper-small { font-size: 0.7rem; }

.pos-paper-training {
  padding: 0.3rem;
  border: 2px dashed #111;
  text-align: center;
  font-weight: 700;
}

/* The PFR lays the journal out for a 40-48 column printer; wrap rather than scroll anything wider. */
.pos-paper-journal {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font: inherit;
}

.pos-paper-qr {
  display: block;
  width: 60%;
  margin: 0.75rem auto;
  image-rendering: pixelated;
}

.pos-paper-items {
  width: 100%;
  border-collapse: collapse;
}

.pos-paper-items td { padding: 0.1rem 0; vertical-align: top; }
.pos-paper-items .money { text-align: right; }
.pos-paper-total td { padding-top: 0.4rem; border-top: 1px dashed #111; font-weight: 700; font-size: 0.9rem; }

@media print {
  .pos-body { height: auto; overflow: visible; background: #fff; }
  .pos-receipt-side, #flash-messages { display: none !important; }
  .pos-receipt-screen { display: block; padding: 0; }
  .pos-paper { width: 72mm; padding: 0; box-shadow: none; }
  @page { size: 80mm; margin: 4mm; }
}

/* --- narrow screens: catalog above the ticket --------------------------------------------- */
@media (max-width: 1200px) {
  .pos-cashier { display: none; }
}

@media (max-width: 900px) {
  .pos-body { height: auto; overflow: auto; }
  .pos-main { grid-template-columns: 1fr; }
  .pos-tiles { max-height: 45vh; }
  .pos-ticket { border-left: 0; border-top: 1px solid var(--border); }
  .pos-actions { grid-template-columns: repeat(3, 1fr); }
  .pos-pay-body { grid-template-columns: 1fr; }
  .pos-receipt-screen { flex-direction: column; align-items: center; }
  .pos-topbar-end { margin-left: 0; }
}

/* --- line discounts, parked carts ---------------------------------------------------------- */
.pos-discount-badge {
  padding: 0 0.3rem;
  border-radius: 999px;
  background: var(--status-warning-bg);
  color: var(--status-warning-fg);
  font-weight: 700;
}

.pos-line-name s { color: var(--text-muted); }
.pos-body .pos-discount-button { font-size: 1rem; }
.pos-body .pos-discount-button:hover { color: var(--accent); }

.pos-actions-secondary {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.pos-actions-secondary form { display: contents; }
.pos-actions-secondary-3 { grid-template-columns: 1fr 1fr 1fr; }
.pos-body .pos-actions-secondary .pos-button { min-height: 2.5rem; font-size: 0.95rem; }
.pos-actions { grid-template-columns: repeat(3, 1fr); }

.pos-body button.pos-parked-button {
  min-height: 0;
  background: var(--warning);
  border-color: var(--warning);
  color: #fff;
  font: inherit;
  font-weight: 600;
}

.pos-pay-actions-single { grid-template-columns: 1fr; }
.pos-dialog-body .pos-receipts td { vertical-align: middle; }
.pos-dialog-body .pos-receipts-actions form { display: inline; }

.pos-paper-copy {
  padding: 0.3rem;
  background: #111;
  color: #fff;
  text-align: center;
  font-weight: 700;
}

/* --- till setup, shift dialogs, receipts list, refunds ----------------------------------- */
.pos-till-name {
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--menu-bar-border);
  border-radius: var(--radius);
  font-weight: 600;
  white-space: nowrap;
}

.pos-body button.pos-pill {
  min-height: 0;
  border: 0;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
}

.pos-body button.pos-pill-ok { background: rgba(48, 186, 120, 0.2); color: #8ff0bf; }
.pos-body button.pos-pill-danger { background: rgba(224, 57, 62, 0.25); color: #ffb4b4; }

.pos-setup-notice {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: baseline;
  padding: 0.75rem 1rem;
  background: var(--status-warning-bg);
  color: var(--status-warning-fg);
}

.pos-dialog-small { width: min(30rem, calc(100vw - 2rem)); }
.pos-dialog-body { padding: 1rem 1.25rem; }

.pos-page {
  height: 100%;
  overflow-y: auto;
  padding: 1rem 1.25rem 2rem;
  max-width: 72rem;
  margin: 0 auto;
}

.pos-page-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.pos-body .pos-page-head h1 {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 1.4rem;
}

.pos-page-filter { margin-left: auto; }
.pos-page-meta { color: var(--text-muted); }

.pos-body .pos-button.pos-button-inline {
  display: inline-flex;
  min-height: 2.5rem;
  padding: 0.3rem 0.9rem;
  font-size: 0.95rem;
}

.pos-receipts {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.pos-receipts th,
.pos-receipts td {
  padding: 0.55rem 0.75rem;
  border-bottom: 1px solid var(--border-muted);
  text-align: left;
  vertical-align: middle;
}

.pos-receipts th { color: var(--text-muted); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.03em; }
.pos-receipts .money { text-align: right; font-variant-numeric: tabular-nums; }
.pos-receipts .qty { text-align: center; }
.pos-receipts tfoot td { font-weight: 700; border-bottom: 0; }
.pos-receipts small { color: var(--text-muted); }
.pos-receipts-actions { text-align: right; white-space: nowrap; }
.pos-receipts-actions .pos-button + .pos-button { margin-left: 0.4rem; }

.pos-refund-bottom {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(16rem, 22rem);
  gap: 0.75rem 1.5rem;
  align-items: end;
  margin-top: 1rem;
}

.pos-refund-bottom .pos-due { grid-column: 2; margin: 0; }
.pos-refund-bottom .pos-hint { grid-column: 1 / -1; }
.pos-refund-bottom .pos-button { grid-column: 2; }
.pos-refund-fields { grid-row: span 3; display: flex; flex-direction: column; gap: 0.75rem; }

.pos-refund-method {
  display: flex;
  gap: 1.5rem;
  margin: 0;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.pos-refund-method legend { padding: 0 0.3rem; font-weight: 600; }
.pos-refund-method label { display: flex; align-items: center; gap: 0.4rem; font-size: 1.05rem; }
.pos-refund-buyer { display: flex; gap: 0.5rem; }
.pos-body .pos-refund-buyer input[type="text"] { flex: 1; width: auto; }

/* --- the kasa on a phone (request variant :phone): camera scanning, one column ------------ */
.pos-body .pos-camera-button {
  min-height: 3.25rem;
  padding: 0 1rem;
  font-size: 1.05rem;
  font-weight: 700;
  white-space: nowrap;
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.pos-body .pos-camera-button:disabled { opacity: 0.45; }
.pos-body .pos-customer-form .pos-camera-button { min-height: 2.25rem; font-size: 0.95rem; }

.pos-camera {
  width: 100vw;
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  padding: 0;
  border: 0;
  background: #000;
  color: #fff;
}

.pos-camera[open] {
  display: flex;
  flex-direction: column;
}

.pos-camera-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem 1rem;
  font-size: 1.15rem;
}

.pos-body .pos-camera-head button {
  min-height: 2.75rem;
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.25);
  color: #fff;
  font-weight: 600;
}

/* Exactly as tall as the video html5-qrcode puts in it — it sizes the scan region from this
   box, and a region taller than the picture never decodes anything. */
.pos-camera-view {
  margin: auto 0;
  overflow: hidden;
}

.pos-camera-hint:empty { display: none; }

.pos-camera-hint,
.pos-camera-alert,
.pos-camera-status {
  margin: 0;
  padding: 0.75rem 1rem;
  text-align: center;
}

.pos-camera-hint { color: #ffc39a; }
.pos-camera-alert { background: var(--danger); color: #fff; font-weight: 600; }
.pos-camera-status { background: rgba(255, 255, 255, 0.08); font-size: 0.95rem; }
.pos-camera-status strong { font-size: 1.2rem; }

.pos-phone .pos-clock,
.pos-phone .pos-cashier,
.pos-phone .pos-keypad,
.pos-phone .pos-brand span:last-child { display: none; }

@media (max-width: 700px) {
  .pos-topbar { gap: 0.5rem; padding: 0.5rem 0.75rem; }
  .pos-setting { flex: 1 1 40%; }
  .pos-setting label { display: none; }
  .pos-setting select { max-width: none; width: 100%; }
  .pos-status-pills { order: 1; flex-wrap: wrap; }
  .pos-topbar-end { margin-left: auto; }
  .pos-catalog { padding: 0.75rem; }
  .pos-body input.pos-scan-input:not([type="checkbox"]) { min-width: 0; font-size: 1rem; }
  .pos-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); max-height: 32vh; }
  .pos-body .pos-tile { min-height: 5.5rem; }
  .pos-line { grid-template-columns: auto minmax(0, 1fr) auto auto; row-gap: 0.3rem; padding: 0.55rem 0.75rem; }
  .pos-line-name { grid-column: 1 / -1; }
  .pos-line-name span { white-space: normal; }
  .pos-line-total { justify-self: end; }
  /* Total and the pay buttons stay on screen however long the receipt gets. */
  .pos-summary { position: sticky; bottom: 0; padding: 0.6rem 0.75rem 0.75rem; box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.12); }
  .pos-totals, .pos-earn, .pos-points { display: none; }
  .pos-due { margin-bottom: 0.5rem; padding: 0.4rem 0.75rem; }
  .pos-due strong { font-size: 1.75rem; }
  .pos-body .pos-button { min-height: 3.1rem; font-size: 0.95rem; }
  .pos-body .pos-button kbd { display: none; }
  .pos-dialog { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; margin: 0; border-radius: 0; }
  .pos-pay-body { padding: 0.75rem; }
  .pos-pay-modes { padding: 0.75rem 0.75rem 0; }
  .pos-pay-actions { padding: 0 0.75rem 0.75rem; }
  .pos-page { padding: 0.75rem; }
  .pos-receipts th:nth-child(3), .pos-receipts td:nth-child(3), .pos-receipts th:nth-child(4), .pos-receipts td:nth-child(4) { display: none; }
  .pos-refund-bottom { grid-template-columns: 1fr; }
  .pos-refund-bottom .pos-due, .pos-refund-bottom .pos-button { grid-column: 1; }
}

/* --- KEP (Knjiga evidencije prometa) — printable as the book itself ------------------------ */
.kep-head p { margin: 0.5rem 0; }

@media print {
  .kep-table { font-size: 0.8rem; }
  .no-print,
  .menu-bar,
  .submenu-bar,
  .page-actions,
  .app-footer { display: none !important; }
}

/* --- Manual journal entry form (Nalog knjiženja) ----------------------------------------- */
.journal-entry-form .je-head {
  display: grid;
  grid-template-columns: minmax(180px, 220px) minmax(160px, 200px) minmax(260px, 1fr);
  gap: 0.75rem 1rem;
  align-items: end;
}

#resource-form.journal-entry-form .je-head > div {
  max-width: none;
  margin-bottom: 0;
}
.journal-entry-form .je-date-hint { margin: 0.4rem 0 0.9rem; }
.je-grid .stavke-row select { width: 100%; min-width: 0; }
.je-grid .stavke-row > * { min-width: 0; }
.je-num { text-align: right; }

.je-totals {
  grid-template-columns: var(--stavke-cols);
  background: var(--surface-muted);
  font-variant-numeric: tabular-nums;
}

.je-totals .je-totals-label { grid-column: 1 / span 4; font-weight: 600; }
.je-unbalanced .je-totals strong { color: var(--danger); }

.je-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}

.je-status { font-weight: 600; color: var(--success); }
.je-unbalanced .je-status { color: var(--danger); }

@media (max-width: 900px) {
  .journal-entry-form .je-head { grid-template-columns: 1fr; }
}

/* ── Notifikacije: the bell in the top bar, its panel, pop-ups, and the full list page ────── */
.notification-bell {
  position: relative;
}

.notification-toggle {
  position: relative;
}

.notification-count {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border: 2px solid var(--surface);
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 14px;
  text-align: center;
}

.notification-count[hidden] {
  display: none;
}

@media (prefers-reduced-motion: no-preference) {
  .notification-toggle.is-ringing svg {
    animation: notification-ring 0.8s ease-in-out;
    transform-origin: 50% 10%;
  }

  @keyframes notification-ring {
    0%, 100% { transform: rotate(0); }
    20% { transform: rotate(16deg); }
    40% { transform: rotate(-14deg); }
    60% { transform: rotate(8deg); }
    80% { transform: rotate(-4deg); }
  }
}

.notification-panel {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  z-index: 1200;
  width: min(400px, calc(100vw - 24px));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
  overflow: hidden;
}

.notification-panel[hidden] {
  display: none;
}

.notification-panel-head,
.notification-panel-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 16px;
}

.notification-panel-head {
  border-bottom: 1px solid var(--border-muted);
}

.notification-panel-head strong {
  font-family: var(--font-display);
  font-size: 1.1rem;
}

.notification-panel-foot {
  border-top: 1px solid var(--border-muted);
  font-size: 0.9rem;
  flex-wrap: wrap;
}

.notification-link-button {
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent-strong);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}

.notification-link-button:hover {
  background: none;
  text-decoration: underline;
}

.notification-list {
  max-height: min(60vh, 460px);
  overflow-y: auto;
  padding: 6px;
}

.notification-empty {
  margin: 0;
  padding: 22px 16px;
  color: var(--text-muted);
  font-size: 0.9rem;
  text-align: center;
}

.notification-panel:has(.notification-item) .notification-empty {
  display: none;
}

.notification-item {
  display: flex;
  gap: 10px;
  padding: 10px;
  border-radius: 12px;
  color: var(--text);
  text-decoration: none;
}

.notification-item:hover {
  background: var(--surface-muted);
  text-decoration: none;
}

.notification-dot {
  flex: none;
  width: 8px;
  height: 8px;
  margin-top: 6px;
  border-radius: 50%;
  background: transparent;
}

.notification-item.is-unread .notification-dot {
  background: var(--accent);
}

.notification-text {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.notification-event {
  color: var(--text-muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.notification-title {
  font-weight: 600;
  line-height: 1.3;
}

.notification-item:not(.is-unread) .notification-title {
  font-weight: 500;
  color: var(--text-muted);
}

.notification-body {
  color: var(--text-muted);
  font-size: 0.88rem;
  line-height: 1.35;
}

.notification-time {
  color: var(--text-muted);
  font-size: 0.78rem;
}

/* Pop-ups for notifications arriving while you work — top right, under the bell. */
.notification-toasts {
  position: fixed;
  top: calc(var(--topbar-height) + 12px);
  right: 16px;
  z-index: 1300;
  display: grid;
  gap: 10px;
  width: min(360px, calc(100vw - 32px));
  pointer-events: none;
}

.notification-toast {
  position: relative;
  display: grid;
  gap: 3px;
  padding: 14px 40px 14px 16px;
  border: 1px solid var(--border);
  border-left: 4px solid var(--accent);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--shadow-float);
  color: var(--text);
  text-decoration: none;
  pointer-events: auto;
  transition: opacity 0.35s ease, transform 0.35s ease;
}

.notification-toast:hover {
  text-decoration: none;
  border-color: var(--border-strong);
  border-left-color: var(--accent);
}

.notification-toast.is-leaving {
  opacity: 0;
  transform: translateX(12px);
}

@media (prefers-reduced-motion: no-preference) {
  .notification-toast {
    animation: notification-in 0.3s ease-out;
  }

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

.notification-toast-close {
  position: absolute;
  top: 8px;
  right: 8px;
  min-height: 0;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--text-muted);
  font-size: 1.1rem;
  line-height: 1;
}

/* The full list page */
.notification-filter {
  display: flex;
  gap: 6px;
  margin-bottom: 0.5rem;
}

.notification-filter a {
  padding: 0.3rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  font-size: 0.92rem;
}

.notification-filter a.is-active {
  background: var(--text);
  border-color: var(--text);
  color: var(--surface);
  font-weight: 600;
}

.notification-filter a:hover {
  text-decoration: none;
}

.notification-page {
  padding: 8px;
}

.notification-row {
  display: flex;
  align-items: center;
  gap: 12px;
  border-bottom: 1px solid var(--border-muted);
}

.notification-row:last-child {
  border-bottom: 0;
}

.notification-row .notification-item {
  flex: 1;
  min-width: 0;
}

.notification-row-actions {
  display: flex;
  gap: 6px;
  padding-right: 8px;
}

.notification-empty-page {
  padding: 24px;
  color: var(--text-muted);
  text-align: center;
}

.notification-pager {
  display: flex;
  justify-content: center;
  gap: 16px;
  color: var(--text-muted);
}

.notification-switch {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  font-weight: 500;
}

.notification-settings {
  width: 100%;
  border-collapse: collapse;
}

.notification-settings th {
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 0.9rem;
  font-weight: 600;
  text-align: left;
}

.notification-settings td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border-muted);
  vertical-align: top;
}

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

.notification-settings td small {
  color: var(--text-muted);
}

.notification-settings td:not(:first-child),
.notification-settings th:not(:first-child) {
  width: 120px;
  text-align: center;
}

/* Rekapitulacija PDV-a (shared/_vat_recap_footer) — the <tfoot> of a document's items table:
   one row per VAT rate in the table's own Osnovica / PDV % / PDV / Ukupno columns, then the
   totals and the amount to pay, the way a printed invoice ends. */
.data-table tfoot.vat-recap-footer td,
.data-table tfoot.vat-recap-footer th {
  /* Never cut a figure short: the columns are sized to the item rows above. */
  overflow: visible;
  text-overflow: clip;
  padding: 6px 12px;
  border-bottom: 0;
  background: var(--surface);
  font-variant-numeric: tabular-nums;
}

.data-table tfoot.vat-recap-footer td.qty {
  padding-inline: 4px;
}

.data-table tfoot.vat-recap-footer th {
  color: var(--text-muted);
  font-weight: 500;
  text-align: right;
}

.data-table tfoot.vat-recap-footer tr:first-child td,
.data-table tfoot.vat-recap-footer tr:first-child th {
  border-top: 2px solid var(--border);
}

.data-table tfoot.vat-recap-footer tr.vat-recap-total td,
.data-table tfoot.vat-recap-footer tr.vat-recap-total th {
  border-top: 1px solid var(--border);
  color: var(--text);
  font-weight: 700;
}

.data-table tfoot.vat-recap-footer tr.vat-recap-grand td,
.data-table tfoot.vat-recap-footer tr.vat-recap-grand th {
  border-top: 1px solid var(--border);
  padding-top: 10px;
  padding-bottom: 10px;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 700;
}

.data-table tfoot.vat-recap-footer tr.vat-recap-grand td {
  color: var(--accent-strong);
}

/* Roles: permissions grouped by module (show page and form), and the role choice on a user. */
.permission-groups {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1rem 1.5rem;
}

.permission-groups h3 {
  margin: 0 0 0.4rem;
  font-size: 0.95rem;
}

.permission-groups ul {
  margin: 0;
  padding-left: 1.1rem;
  color: var(--text);
}

.permission-groups li {
  padding: 2px 0;
}

#resource-form .permission-check,
.permission-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  padding: 3px 0;
  color: var(--text);
  font-weight: 400;
  white-space: normal;
}

.role-choices,
#resource-form div.role-choices:has(> label):not([data-nested-fields-target]) {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 8px;
  max-width: none;
}

/* A permission group's checkboxes stack under its heading at the group's own width. */
#resource-form .permission-groups > div:has(> label):not([data-nested-fields-target]) {
  max-width: none;
  gap: 0;
}

#resource-form .role-choice,
.role-choice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-weight: 400;
  white-space: normal;
  cursor: pointer;
}

.role-choice:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-muted);
}

.role-choice span {
  display: grid;
  gap: 2px;
}

.role-choice small {
  color: var(--text-muted);
  line-height: 1.35;
}

/* The form-wide label rule (#resource-form div:has(> label) … label) keeps labels on one
   line; a permission's name and a role's description need to wrap within their column. */
#resource-form div:has(> label):not([data-nested-fields-target]) label.permission-check,
#resource-form div:has(> label):not([data-nested-fields-target]) label.role-choice {
  white-space: normal;
  font-size: 0.95rem;
  font-weight: 400;
  color: var(--text);
}

.role-choice small {
  display: block;
  white-space: normal;
}

/* An opening invoice carried over from LOGIK (shared/_logik_opening_notice). */
.logik-notice {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-left: 4px solid var(--status-info-fg);
  border-radius: var(--radius);
  background: var(--status-info-bg);
  color: var(--text);
}

.logik-notice a {
  margin-left: 0.5rem;
  font-weight: 600;
}

/* Kalkulacija (SiriusProcurement::Calculation): a wide table that scrolls sideways on a
   narrow screen, its inputs compact, and the short hints under it. */
.table-scroll {
  overflow-x: auto;
}
.calculation-table th,
.calculation-table td {
  white-space: nowrap;
}
.calculation-table input[type="text"] {
  width: 7em;
  text-align: right;
}
.calculation-table tfoot th {
  border-top: 2px solid var(--border-muted);
  text-align: right;
}
.inline-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
}
.form-hint {
  color: var(--text-muted);
  font-size: 0.875em;
}

/* Šifra partnera/artikla — assigned by the app (SequentialNumber), shown but not typed in. */
input.readonly-code {
  background: var(--surface-muted);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  cursor: default;
}

/* Uvoz/izvoz JCI checklist (shared/_customs_checklist) on goods receipts, deliveries and
   invoices — info-blue while it's only a to-do, warning-amber while it blocks posting. */
.customs-checklist {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-left: 4px solid var(--status-info-fg);
  border-radius: var(--radius);
  background: var(--status-info-bg);
  color: var(--text);
}

.customs-checklist-blocking {
  border-left-color: var(--status-warning-fg);
  background: var(--status-warning-bg);
}

.customs-checklist h3 {
  margin: 0 0 0.25rem;
  font-size: 1rem;
}

.customs-checklist p {
  margin: 0 0 0.5rem;
}

.customs-checklist ol {
  margin: 0 0 0.5rem;
  padding: 0;
  list-style: none;
}

.customs-checklist li {
  display: flex;
  gap: 0.5rem;
  padding: 2px 0;
}

.customs-step-mark {
  width: 1.2em;
  font-weight: 700;
  text-align: center;
}

.customs-step-done .customs-step-mark {
  color: var(--status-success-fg);
}

.customs-step-open {
  font-weight: 600;
}

.customs-checklist-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* Paritet: the Incoterm code and its named place side by side (ApplicationHelper#incoterm_fields). */
.incoterm-fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0 1rem;
}

@media (max-width: 640px) {
  .incoterm-fields {
    grid-template-columns: 1fr;
  }
}

/* Theme toggle: the moon in the light theme, the sun in the dark one (shared/_theme_icon). */
.theme-toggle-btn svg {
  display: block;
}

.theme-icon-sun,
:root[data-theme="dark"] .theme-icon-moon {
  display: none !important;
}

:root[data-theme="dark"] .theme-icon-sun {
  display: block !important;
}
