/* ===== Reset & Base ===== */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

:root {
  /* --- Pact24 brand book (RGB values lifted from ci.pdf):
     brand-red:   #ED1C24  (CMYK 0,100,100,0  · RGB 237,28,36)
     brand-gray:  #808285  (CMYK 0,0,0,60     · RGB 128,130,133)
     brand-black: #231F20  (CMYK 0,0,0,100    · RGB 35,31,32)
     Everything below derives from those three.
     --blue is kept as a token name for back-compat; semantically it is the
     brand accent (red). --red stays as a deeper destructive/danger color so
     "delete" / "offline" doesn't collide with brand-primary chrome.
  */
  --font: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --bg: #FAFAFA;
  --text: #231F20;
  --text-secondary: #808285;
  --border: rgba(35, 31, 32, 0.09);
  --white: #ffffff;
  --black: #231F20;
  /* brand-primary (accent) */
  --brand-red: #ED1C24;
  --brand-red-dark: #C40F17;
  --brand-red-soft: #FCE4E5;
  --brand-gray: #808285;
  --brand-black: #231F20;
  /* legacy token aliases — code across the app references --blue for the
     accent color, so we keep the name and point it at brand-red. */
  --blue: #ED1C24;
  --blue-soft: #FCE4E5;
  /* status colors — green and yellow are needed for "online" / "warning"
     UX but aren't in the two-color brand. Picked muted neutrals so they
     read calm next to the brand red. */
  --green: #2E8456;
  --green-soft: #E4F0EA;
  --yellow: #CA8A04;
  --red: #A8362E;
  --gray-50: #F6F6F6;
  --gray-100: #F0F0F0;
  --gray-200: #E6E6E7;
  --gray-300: #C8C9CB;
  --radius: 14px;
  --radius-sm: 10px;
  --radius-full: 9999px;
  --shadow: 0 1px 0 rgba(35,31,32,0.02), 0 1px 2px rgba(35,31,32,0.04);
  --shadow-md: 0 1px 0 rgba(35,31,32,0.02), 0 8px 24px -12px rgba(35,31,32,0.12);
  --transition: 0.22s cubic-bezier(0.2, 0.7, 0.2, 1);
}

html {
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  min-height: 100vh;
}

a {
  color: var(--blue);
  text-decoration: none;
  transition: opacity var(--transition);
}
a:hover { opacity: 0.75; }

/* ===== App shell (sidebar + main) ===== */
.app {
  display: grid;
  grid-template-columns: 232px 1fr;
  min-height: 100vh;
}

@media (max-width: 880px) {
  .app { grid-template-columns: 1fr; }
}

/* Off-canvas drawer scrim — only materialises at the mobile breakpoint. */
.nav-scrim { display: none; }
@media (max-width: 880px) {
  .nav-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 45;
    background: rgba(11, 11, 14, 0.4);
    opacity: 0;
    pointer-events: none;
    transition: opacity 220ms ease;
  }
  .app.is-nav-open .nav-scrim {
    opacity: 1;
    pointer-events: auto;
  }
}

/* Prevent the page from scrolling behind an open drawer. Plain
   `overflow: hidden` on <body> is ignored by iOS Safari, so the controller
   pairs this class with position:fixed + a restored scroll offset. */
body.nav-locked {
  position: fixed;
  left: 0;
  right: 0;
  overflow: hidden;
}

/* ===== Sidebar ===== */
.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  padding: 18px 14px 14px;
  background: var(--gray-100);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 4px;
  z-index: 50;
}

@media (max-width: 880px) {
  /* Slide the sidebar off-canvas; the hamburger in the topbar toggles it.
     `visibility: hidden` (flipped only after the slide-out finishes) keeps the
     off-screen nav out of the tab order and screen-reader tree while closed. */
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 264px;
    max-width: 84vw;
    height: 100vh;
    height: 100dvh;
    overflow-y: auto;
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 240ms cubic-bezier(0.2, 0.7, 0.2, 1),
                visibility 0s linear 240ms;
    box-shadow: 0 18px 50px rgba(11, 11, 14, 0.22);
  }
  .app.is-nav-open .sidebar {
    transform: none;
    visibility: visible;
    /* Same slide curve as the closed state; only show immediately (no delay). */
    transition-delay: 0s, 0s;
  }
}

.brand {
  display: flex;
  align-items: center;
  padding: 6px 10px 16px;
  color: var(--text);
  margin-top: 2px;
}
.brand:hover { opacity: 1; }

.brand__logo {
  height: 28px;
  width: auto;
  display: block;
  object-fit: contain;
  /* The master PNG ships with the brand gray + brand red baked in;
     no additional tint needed. */
}

.nav-section {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 500;
  transition: background var(--transition), color var(--transition);
}
.nav-item:hover {
  background: rgba(11, 11, 14, 0.045);
  opacity: 1;
}
.nav-item__icon {
  width: 18px;
  height: 18px;
  color: var(--text-secondary);
  flex-shrink: 0;
  transition: color var(--transition);
}
.nav-item:hover .nav-item__icon { color: var(--text); }

.nav-item--active {
  background: var(--white);
  color: var(--text);
  box-shadow: 0 0 0 1px var(--border) inset, 0 1px 2px rgba(11, 11, 14, 0.03);
}
.nav-item--active .nav-item__icon { color: var(--blue); }
.nav-item--active::before {
  content: "";
  position: absolute;
  left: -14px;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 18px;
  border-radius: 0 3px 3px 0;
  background: var(--blue);
}

.nav-count {
  margin-left: auto;
  font-size: 11px;
  font-weight: 500;
  color: var(--text-secondary);
  padding: 1px 7px;
  border-radius: var(--radius-full);
  background: rgba(35, 31, 32, 0.05);
  line-height: 1.4;
}
.nav-item--active .nav-count {
  background: var(--blue-soft);
  color: var(--brand-red);
}

.sidebar__spacer { flex: 1; }

/* user-card details/menu */
.sidebar__user {
  position: relative;
  margin-top: 4px;
}
.sidebar__user[open] > summary {
  background: var(--gray-200);
}

.sidebar__user-summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: rgba(11, 11, 14, 0.03);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: background var(--transition);
  list-style: none;
}
.sidebar__user-summary::-webkit-details-marker { display: none; }
.sidebar__user-summary:hover { background: rgba(11, 11, 14, 0.06); }

.avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--blue), var(--brand-red-dark));
  color: var(--white);
  display: grid;
  place-items: center;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.02em;
  flex-shrink: 0;
}

.sidebar__user-info {
  flex: 1;
  min-width: 0;
  line-height: 1.25;
}
.sidebar__user-name {
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar__user-meta {
  font-size: 11px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar__user-chevron {
  color: var(--text-secondary);
  flex-shrink: 0;
  transition: transform var(--transition);
}
.sidebar__user[open] .sidebar__user-chevron {
  transform: rotate(180deg);
}

.sidebar__user-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  right: 0;
  padding: 6px;
  background: var(--white);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 100;
}

.sidebar__user-menu-section {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.sidebar__user-menu-section:last-child {
  border-bottom: none;
  margin-bottom: 0;
}

.sidebar__user-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  font-size: 13px;
  color: var(--text);
  border-radius: 8px;
  transition: background var(--transition);
}
.sidebar__user-menu-item:hover {
  background: var(--gray-100);
  opacity: 1;
}
.sidebar__user-menu-item--danger { color: var(--red); }
.sidebar__user-menu-item--danger:hover {
  background: rgba(168, 54, 46, 0.08);
}

.sidebar__user-menu-item svg {
  color: var(--text-secondary);
  flex-shrink: 0;
}
.sidebar__user-menu-item--danger svg { color: var(--red); }

/* admin company switcher inside the user menu */
.sidebar__user-menu .company-session-switcher {
  flex-direction: column;
  align-items: stretch !important;
  gap: 6px !important;
}
.sidebar__user-menu .company-session-switcher label {
  font-size: 10.5px !important;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-secondary) !important;
}
.sidebar__user-menu .nav-company-select {
  width: 100%;
  font-size: 12.5px;
  padding: 6px 8px;
}

/* ===== Settings sub-nav: keep .nav-link for /settings/* tabs ===== */
.nav-link {
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 500;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  transition: all var(--transition);
}
.nav-link:hover {
  color: var(--text);
  background: var(--gray-200);
  opacity: 1;
}
.nav-link--active {
  color: var(--text);
  background: var(--gray-200);
}
.nav-link--icon {
  padding: 6px 10px;
  display: inline-flex;
  align-items: center;
}

/* ===== Per-row "Akcje" dropdown on the Devices index ===== */
.row-actions-menu {
  position: relative;
  display: inline-block;
}
.row-actions-menu > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.row-actions-menu > summary::-webkit-details-marker { display: none; }
.row-actions-menu[open] > summary {
  color: var(--text);
  background: var(--gray-200);
}
.row-actions-menu__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 220px;
  padding: 6px;
  background: var(--white);
  border-radius: var(--radius-sm);
  border: 1px solid var(--gray-200);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 100;
  text-align: left;
}
.row-actions-menu__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 12px;
  font-size: 13px;
  color: var(--text);
  background: transparent;
  border: none;
  text-align: left;
  border-radius: 8px;
  cursor: pointer;
  transition: background var(--transition);
}
.row-actions-menu__item > svg { flex-shrink: 0; }
.row-actions-menu__item:hover:not(:disabled) {
  background: var(--gray-100);
  opacity: 1;
}
.row-actions-menu__item:disabled {
  color: var(--text-secondary);
  cursor: not-allowed;
  opacity: 0.6;
}
.row-actions-menu__item--danger {
  color: var(--red);
}
.row-actions-menu__item--danger:hover:not(:disabled) {
  background: rgba(168, 54, 46, 0.08);
  color: var(--red);
}
/* Touch devices need a ~44px tap target; the 13px/8px-padded items are ~33px,
   and the Akcje summary trigger (.btn--sm) is ~30px. */
@media (hover: none) and (pointer: coarse) {
  .row-actions-menu__item { min-height: 44px; }
  .row-actions-menu > summary { min-height: 44px; box-sizing: border-box; }
}

/* Inline icon-button cluster on campaigns table rows. Hidden by default;
   revealed on row hover or on keyboard focus inside the row. */
.row-actions-cluster {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  justify-content: flex-end;
  opacity: 0;
  transition: opacity var(--transition);
}
tr:hover .row-actions-cluster,
tr:focus-within .row-actions-cluster {
  opacity: 1;
}

/* Two-line table cell content: bold primary line + muted secondary line. */
.row-title {
  font-weight: 500;
  color: var(--text);
}
.row-sub {
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: 2px;
}

/* Settings sub-nav (tabs under page header on /settings/* pages) */
.settings-tabs {
  display: flex;
  gap: 4px;
  margin-top: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--gray-200);
}

/* ===== Locale toggle ===== */
.locale-toggle {
  font-size: 11px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: 6px;
  border: 1px solid var(--gray-300);
  color: var(--text-secondary);
  text-decoration: none;
  transition: all var(--transition);
  letter-spacing: 0.02em;
}
.locale-toggle:hover {
  background: var(--gray-100);
  color: var(--text);
  border-color: var(--gray-300);
  opacity: 1;
}

/* ===== Main Content ===== */
.main-content {
  max-width: 1200px;
  margin: 0 auto;
  padding: 32px 24px 64px;
}

/* Unauthenticated pages (login + password reset). Vertically stacks the
   brand logo, the form card, and any footer link with consistent gaps. */
.auth-shell {
  max-width: 440px;
  padding-top: 64px;
}

.auth-locale {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 24px;
}

.auth-brand {
  display: flex;
  justify-content: center;
  margin-bottom: 28px;
}
.auth-brand:hover { opacity: 1; }
.auth-brand__logo {
  height: 48px;
  width: auto;
  display: block;
}

.auth-card-wrap {
  max-width: 400px;
  margin: 0 auto;
}

.auth-title {
  text-align: center;
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.015em;
  margin-bottom: 24px;
}

/* Wraps the topbar + scrolling main content so they share the same column. */
.main-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Sticky topbar with breadcrumb (left), search placeholder + help (right). */
.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  height: 60px;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 48px;
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid var(--border);
}
@media (max-width: 1100px) { .topbar { padding: 0 32px; } }
@media (max-width: 880px)  { .topbar { padding: 0 20px; } }

/* Hamburger lives in the topbar; only relevant once the sidebar goes drawer.
   Compound selector so `display: none` wins: a bare `.topbar__menu` ties on
   specificity with the later `.topbar__icon-btn { display: grid }` and would
   lose on source order, leaving the hamburger visible on desktop too. */
.topbar__icon-btn.topbar__menu { display: none; margin-right: 2px; }
@media (max-width: 880px) {
  .topbar__icon-btn.topbar__menu { display: inline-flex; }
}

/* Enlarge tap targets on touch devices to ~44px (WCAG 2.5.5) without
   disturbing the dense mouse-driven desktop layout. */
@media (hover: none) and (pointer: coarse) {
  .nav-item { min-height: 44px; padding-top: 11px; padding-bottom: 11px; }
  .topbar__icon-btn { width: 40px; height: 40px; }
  .table-action { width: 38px; height: 38px; }
  .sidebar__user-menu-item { min-height: 42px; }
  .tabs__tab { min-height: 44px; }
}

.crumb {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-secondary);
  min-width: 0;
}
.crumb__base    { color: var(--text-secondary); }
.crumb__sep     { color: var(--gray-300); display: inline-flex; flex-shrink: 0; }
.crumb__current { color: var(--text); font-weight: 500; }

.topbar__search {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  width: 280px;
  height: 34px;
  padding: 0 12px;
  border-radius: var(--radius-full);
  background: var(--gray-50);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: 13px;
  cursor: text;
  transition: border-color var(--transition), background var(--transition);
}
.topbar__search:hover { border-color: var(--gray-300); }
.topbar__search { font: inherit; text-align: left; }
.topbar__search > svg { flex-shrink: 0; }
.topbar__search-placeholder {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.topbar__search kbd {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 10.5px;
  padding: 1.5px 6px;
  border-radius: 5px;
  background: var(--white);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  flex-shrink: 0;
}

.topbar__icon-btn {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-full);
  color: var(--text-secondary);
  display: grid;
  place-items: center;
  transition: background var(--transition), color var(--transition);
  text-decoration: none;
  flex-shrink: 0;
}
.topbar__icon-btn:hover {
  background: rgba(35, 31, 32, 0.05);
  color: var(--text);
  opacity: 1;
}
/* Drop the dark UA focus ring on tap/click; keep a blue ring for keyboard users. */
.topbar__icon-btn:focus { outline: none; }
.topbar__icon-btn:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* Notification bell — replaces the old mailto question icon in the
   topbar. The <details> shell is what makes click-to-toggle work
   without JS; the .topbar__bell-panel under it is absolutely
   positioned so it doesn't shift the topbar layout. */
.topbar__bell { position: relative; }
.topbar__bell-summary {
  list-style: none;
  cursor: pointer;
  position: relative;
}
.topbar__bell-summary::-webkit-details-marker,
.topbar__bell-summary::marker { display: none; }

.topbar__bell-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-full);
  background: #ED1C24; /* same red as the dot indicator on player offline */
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
}
.topbar__bell-badge--hidden { display: none; }

.topbar__bell-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: 360px;
  max-width: calc(100vw - 24px);
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.08);
  z-index: 50;
  overflow: hidden;
}
.topbar__bell-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}
.topbar__bell-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
.topbar__bell-clear-btn,
.topbar__bell-clear-btn button {
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: 12px;
  cursor: pointer;
  padding: 0;
}
.topbar__bell-clear-btn:hover,
.topbar__bell-clear-btn button:hover { color: var(--text); }

.topbar__bell-list {
  max-height: 420px;
  overflow-y: auto;
}
.topbar__bell-empty {
  padding: 24px 12px;
  text-align: center;
  font-size: 13px;
  color: var(--text-secondary);
}

/* The row is a container <div> holding two sibling forms: the mark-read
   "main" button (icon + body) and the "remove" × button. The flex layout
   that used to live on the row itself now sits on the main button. */
.topbar__bell-row {
  display: flex;
  align-items: stretch;
  width: 100%;
  border-bottom: 1px solid var(--border);
}
.topbar__bell-row:hover { background: rgba(35,31,32,0.04); }
.topbar__bell-row--unread { background: rgba(237,28,36,0.04); }

.topbar__bell-row-main { flex: 1 1 auto; min-width: 0; display: flex; }
.topbar__bell-row-main button {
  display: flex;
  width: 100%;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  text-align: left;
  background: none;
  border: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: none;
}

.topbar__bell-row-remove { flex: 0 0 auto; display: flex; }
.topbar__bell-row-remove button {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 10px;
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
}
.topbar__bell-row-remove button:hover,
.topbar__bell-row-remove button:focus-visible { color: #ED1C24; }

.topbar__bell-row-icon { flex-shrink: 0; color: var(--text-secondary); margin-top: 2px; }
.topbar__bell-row-body { flex: 1; min-width: 0; }
.topbar__bell-row-title { font-size: 13px; font-weight: 500; color: var(--text); }
.topbar__bell-row-time  { font-size: 11px; color: var(--text-secondary); margin-top: 2px; }

@media (max-width: 880px) {
  .topbar__search { width: auto; flex: 1; }
  .topbar__search-placeholder { display: none; }
  .topbar__search kbd { display: none; }
}

/* Command palette (⌘K) — global search overlay. */
.cmdk {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: 12vh 16px 16px;
  animation: cmdk-fade 140ms ease-out;
}
.cmdk[hidden] { display: none; }
.cmdk__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(35, 31, 32, 0.42);
  backdrop-filter: blur(2px);
}
.cmdk__panel {
  position: relative;
  width: min(640px, 100%);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  max-height: 76vh;
}
.cmdk__input-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  color: var(--text-secondary);
}
.cmdk__input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  font: inherit;
  font-size: 15px;
  color: var(--text);
}
.cmdk__close {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text-secondary);
}
.cmdk__close kbd {
  font-family: var(--font-mono);
  font-size: 10.5px;
  padding: 1.5px 6px;
  border-radius: 5px;
  background: var(--gray-50);
  border: 1px solid var(--border);
  color: var(--text-secondary);
}
.cmdk__results {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 0;
}
.cmdk__group + .cmdk__group { border-top: 1px solid var(--border); }
.cmdk__group-label {
  padding: 10px 16px 4px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.cmdk__item {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 9px 16px;
  text-decoration: none;
  color: var(--text);
  font-size: 14px;
  transition: background var(--transition);
}
.cmdk__item:hover,
.cmdk__item[data-active] {
  background: var(--gray-50);
}
.cmdk__item-primary { font-weight: 500; }
.cmdk__item-secondary {
  margin-left: auto;
  color: var(--text-secondary);
  font-size: 12.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 55%;
}
.cmdk__empty {
  padding: 28px 16px;
  text-align: center;
  color: var(--text-secondary);
  font-size: 13px;
}
.cmdk__hint {
  padding: 8px 14px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--text-secondary);
  background: var(--gray-50);
}
body.cmdk-open { overflow: hidden; }

@keyframes cmdk-fade {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* Main content area below the topbar. */
.main {
  min-width: 0;
  padding: 36px 48px 80px;
  max-width: 1240px;
  width: 100%;
  animation: page-rise 480ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

@media (max-width: 1100px) {
  .main { padding: 32px 32px 64px; }
}
@media (max-width: 880px) {
  .main { padding: 24px 20px 64px; }
}

@keyframes page-rise {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

/* ===== Typography ===== */
h1 {
  font-size: 32px;
  font-weight: 700;
  letter-spacing: -0.03em;
  margin-bottom: 8px;
}

h2 {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin-top: 32px;
  margin-bottom: 12px;
}

h3 {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 4px;
}

p { margin-bottom: 8px; }

small { font-size: 13px; color: var(--text-secondary); }

/* ===== Buttons ===== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-family: var(--font);
  font-size: 14px;
  font-weight: 500;
  padding: 10px 24px;
  border-radius: var(--radius-full);
  border: none;
  cursor: pointer;
  transition: all var(--transition);
  text-decoration: none;
  line-height: 1;
}

.btn--primary {
  background: var(--black);
  color: var(--white);
}
.btn--primary:hover {
  background: #333;
  opacity: 1;
}

.btn--secondary {
  background: var(--gray-200);
  color: var(--text);
}
.btn--secondary:hover {
  background: var(--gray-300);
  opacity: 1;
}

.btn--blue {
  background: var(--blue);
  color: var(--white);
}
.btn--blue:hover {
  background: var(--brand-red-dark);
  opacity: 1;
}

.btn--ghost {
  background: var(--gray-50);
  color: var(--text);
  border: 1px solid var(--border);
}
.btn--ghost > svg { color: var(--text-secondary); }
.btn--ghost:hover {
  background: var(--white);
  border-color: var(--gray-300);
  color: var(--text);
  opacity: 1;
}

.btn--danger {
  color: var(--red);
  background: transparent;
}
.btn--danger:hover {
  background: #fff5f5;
  opacity: 1;
}

.btn--sm {
  font-size: 12px;
  padding: 6px 14px;
}

/* Turbo submit spinner — applied automatically by Turbo during submission */
form.turbo-submitting button[type="submit"],
form[aria-busy="true"] button[type="submit"] {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}
form.turbo-submitting button[type="submit"]::after,
form[aria-busy="true"] button[type="submit"]::after {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
  top: 50%;
  left: 50%;
  margin: -8px 0 0 -8px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-top-color: white;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.btn__spinner {
  animation: spin 0.6s linear infinite;
  flex-shrink: 0;
}

.btn--disabled,
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.6;
  cursor: not-allowed;
}

/* ===== Cards ===== */
.card {
  background: var(--white);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  padding: 20px;
  transition: box-shadow var(--transition);
}
.card:hover {
  box-shadow: var(--shadow-md);
}

.card--flat {
  box-shadow: none;
}
.card--flat:hover {
  box-shadow: none;
}

/* ===== Device Template Card (TV / Radio picker) ===== */
.device-template-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 28px 20px;
  cursor: pointer;
  text-align: center;
  color: var(--text-secondary);
  transition: box-shadow var(--transition), border-color var(--transition), color var(--transition), transform var(--transition);
}
.device-template-card:hover {
  color: var(--text);
  border-color: var(--gray-300);
  transform: translateY(-1px);
}
.device-template-card__input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0;
  height: 0;
}
.device-template-card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 88px;
  height: 88px;
  border-radius: var(--radius);
  background: var(--gray-100);
  color: var(--text);
  transition: background var(--transition), color var(--transition);
}
.device-template-card:hover .device-template-card__icon {
  background: var(--gray-200);
}
.device-template-card__name {
  font-size: 17px;
  letter-spacing: -0.01em;
  color: var(--text);
}
.device-template-card:has(.device-template-card__input:checked) {
  border-color: var(--blue);
  box-shadow: 0 0 0 2px var(--blue) inset, var(--shadow-md);
  color: var(--text);
}
.device-template-card:has(.device-template-card__input:checked) .device-template-card__icon {
  background: var(--blue);
  color: var(--white);
}
.device-template-card__input:focus-visible + .device-template-card__icon {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
}


/* ===== Grid ===== */
.grid {
  display: grid;
  gap: 16px;
}
.grid--2 { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }

/* ===== Tables ===== */
.table-wrap {
  background: var(--white);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  /* Was `overflow: hidden`. Switched so action-icon tooltips popping above
     the first row aren't clipped by the wrap's rounded boundary. Cells
     have no border-radius of their own, so the visual rounded corners
     stay intact. */
  overflow: visible;
}

/* On narrow screens the wide data tables can't fit; let the wrap scroll
   horizontally instead of pushing the whole page wider than the viewport.
   (Setting overflow-x to auto forces overflow-y to auto too — acceptable
   here since the hover tooltips that `overflow: visible` protected aren't
   used on touch devices.) */
@media (max-width: 880px) {
  .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  /* Size to content so genuinely wide tables scroll while narrow ones still
     fit the viewport (min-width:100% keeps them filling the wrap). */
  .table-wrap table { width: max-content; min-width: 100%; }
}

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

th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-secondary);
  padding: 12px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--gray-50);
}

td {
  padding: 12px 16px;
  border-bottom: 1px solid var(--gray-200);
  font-size: 14px;
}

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

tr:hover td {
  background: var(--gray-50);
}

/* ===== Device table (location editor + preview) =====
   Columnar device row: Urządzenie · Zegar · Aktywność · Specyfikacja · Akcje.
   Scoped to .dev-table so it can't bleed into other .table-wrap tables. */
.dev-table td { vertical-align: top; }

/* Urządzenie: status dot + kind icon + name, with description + state chips below. */
.dev-name { display: flex; align-items: flex-start; gap: 9px; font-weight: 600; }
.dev-name__icon { color: var(--text-secondary); display: inline-flex; flex-shrink: 0; margin-top: 1px; }
.dev-name__text { display: inline-flex; flex-direction: column; min-width: 0; }
/* Name never wraps -> the column grows naturally instead of stacking onto 2-3 lines. */
.dev-name__title { white-space: nowrap; }
.dev-name__desc { font-weight: 400; font-size: 0.85em; color: var(--text-secondary); margin-top: 1px; }
.dev-name__meta { margin-top: 5px; display: flex; flex-wrap: wrap; gap: 5px; }

/* Zegar: a single value; muted "—" when the kind doesn't report skew. */
.dev-col-clock { min-width: 64px; }
.dev-val { font-size: 0.9rem; color: var(--text); white-space: nowrap; }
.dev-val--muted { color: var(--text-secondary); }
.dev-val--ok { color: var(--green); font-weight: 500; }
.dev-val--warn { color: var(--yellow); font-weight: 500; }
.dev-val--danger { color: var(--red); font-weight: 500; }

/* Aktywność: ostatnio widziane (prominent) over zainstalowano (smaller, paler). */
.dev-col-when { min-width: 140px; }
.dev-activity { display: flex; flex-direction: column; gap: 2px; }
.dev-activity__seen { font-size: 0.9rem; color: var(--text); white-space: nowrap; }
.dev-activity__seen--danger { color: var(--red); font-weight: 500; }
/* Amber for a degraded-but-up state (e.g. a scale whose push is failing);
   matches the .clock-skew--warning text tone. */
.dev-activity__seen--warn { color: #7a5300; font-weight: 500; }
/* Compound selector so the muted placeholder ("—" / never-seen) wins the
   specificity tie against .dev-activity__seen's own colour (defined later). */
.dev-activity__seen.dev-val--muted { color: var(--text-secondary); }
.dev-activity__installed { font-size: 0.78rem; color: var(--text-secondary); white-space: nowrap; }
/* Secondary "why"/last-contact line under a failure reason. */
.dev-activity__why { font-size: 0.78rem; color: var(--text-secondary); white-space: nowrap; }

/* Specyfikacja: heterogeneous fields, each with a micro-label. It's the last data
   column, so width:100% lets it absorb the table's slack and keep Akcje pinned right. */
.dev-col-spec { min-width: 150px; width: 100%; }
.dev-fields { display: flex; flex-direction: column; gap: 8px; }
.dev-field { display: flex; flex-direction: column; gap: 1px; }
.dev-field__label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-secondary); }
.dev-field__value { font-size: 0.9rem; color: var(--text); line-height: 1.2; }

.dev-col-actions { width: 1%; white-space: nowrap; text-align: right; }

/* Neutral chip (same 4px shape as the clock-skew chips) for an "off" state. */
.clock-skew--muted { background: var(--gray-100); color: var(--text-secondary); }

/* ===== Priority Slider ===== */
input[type="number"].priority-slider__number {
  width: 48px;
  min-width: 48px;
  max-width: 48px;
  height: 32px;
  text-align: center;
  font-size: 16px;
  font-weight: 400;
  padding: 0 2px;
  border: 1px solid var(--gray-300);
  border-radius: 6px;
  font-family: var(--font);
  color: #000000;
  background: #ffffff;
  line-height: 32px;
  box-shadow: none;
  outline: none;
}
input[type="number"].priority-slider__number:focus {
  border-color: var(--gray-300);
  outline: none;
  box-shadow: none;
  color: #000000;
  background: #ffffff;
}
input[type="number"].priority-slider__number:active {
  outline: none;
  box-shadow: none;
}
/* Keep native stepper arrows visible */
input[type="number"].priority-slider__number::-webkit-inner-spin-button {
  -webkit-appearance: inner-spin-button;
  opacity: 1;
  height: 26px;
  cursor: pointer;
}

.priority-slider__header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}

.priority-slider__label {
  font-size: 14px;
  font-weight: 500;
}

.priority-slider__track-wrap {
  position: relative;
  height: 28px;
  display: flex;
  align-items: center;
}

.priority-slider__ramp-bg,
.priority-slider__ramp-fill {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 14px;
  transform: translateY(-50%);
  border-radius: 3px;
  overflow: hidden;
  pointer-events: none;
}
.priority-slider__ramp-bg::before,
.priority-slider__ramp-fill::before {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  clip-path: polygon(0 90%, 100% 0, 100% 100%, 0 100%);
}
.priority-slider__ramp-bg::before {
  background: var(--gray-200);
}
.priority-slider__ramp-fill {
  z-index: 1;
  width: 50%;
}
.priority-slider__ramp-fill::before {
  background: var(--blue);
  opacity: 0.5;
}

.priority-slider__range {
  position: relative;
  width: 100%;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  margin: 0;
  height: 28px;
  cursor: pointer;
  z-index: 2;
  outline: none;
  border: none;
}
.priority-slider__range:focus {
  outline: none;
  box-shadow: none;
}

.priority-slider__range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--white);
  border: 2px solid var(--blue);
  box-shadow: 0 1px 3px rgba(0,0,0,0.12);
  cursor: grab;
}
.priority-slider__range:active::-webkit-slider-thumb {
  cursor: grabbing;
  background: var(--gray-50);
}

.priority-slider__range::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--white);
  border: 2px solid var(--blue);
  box-shadow: 0 1px 3px rgba(0,0,0,0.12);
  cursor: grab;
}
.priority-slider__range::-moz-range-track {
  background: transparent;
  border: none;
}
.priority-slider__range:focus::-moz-range-thumb {
  outline: none;
  box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}

.priority-slider__labels {
  display: flex;
  justify-content: space-between;
  font-size: 10px;
  color: var(--gray-300);
  margin-top: -2px;
  line-height: 1;
}

/* ===== Location Picker ===== */
.loc-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.search-wrap {
  position: relative;
  max-width: 300px;
  flex: 1;
}
.search-wrap__input {
  width: 100%;
  padding-right: 30px;
}
.search-wrap__clear {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  border: none;
  background: var(--gray-200);
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  padding: 0;
  transition: all var(--transition);
}
.search-wrap__clear:hover {
  background: var(--gray-300);
  color: var(--text);
}

.loc-toolbar__search {
  max-width: 300px;
  font-family: var(--font);
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid var(--gray-300);
  border-radius: 6px;
  background: var(--white);
  color: var(--text);
  outline: none;
}
.loc-toolbar__search:focus {
  border-color: var(--blue);
}
.loc-toolbar__search::placeholder {
  color: var(--gray-300);
}

.loc-toolbar__actions {
  display: flex;
  gap: 8px;
}

.loc-toolbar__link {
  font-family: var(--font);
  font-size: 12px;
  font-weight: 500;
  color: var(--blue);
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px 0;
  white-space: nowrap;
}
.loc-toolbar__link:hover {
  opacity: 0.7;
}

.loc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 6px;
}

.loc-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 4px;
  border: 1px solid var(--gray-200);
  cursor: pointer;
  transition: all var(--transition);
  background: var(--white);
}
.loc-item:hover {
  border-color: var(--gray-300);
  background: var(--gray-50);
}
.loc-item:has(.loc-item__check:checked) {
  background: rgba(237, 28, 36, 0.06);
  border-color: rgba(237, 28, 36, 0.25);
}
.loc-item:has(.loc-item__check:checked) .loc-item__name {
  color: var(--blue);
  font-weight: 500;
}

.loc-item__check {
  margin: 0;
  flex-shrink: 0;
}

.loc-item__name {
  font-size: 13px;
  font-weight: 400;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* ===== Device-kind scope cards (campaign form) ===== */
.scope-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.scope-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--gray-200);
  cursor: pointer;
  transition: all var(--transition);
  background: var(--white);
}
.scope-card:hover {
  border-color: var(--gray-300);
  background: var(--gray-50);
}
.scope-card:has(input:checked) {
  background: rgba(237, 28, 36, 0.06);
  border-color: rgba(237, 28, 36, 0.25);
}
.scope-card__head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.scope-card__icon {
  color: var(--text-secondary);
  flex-shrink: 0;
}
/* The shared DEVICE_SCOPE_ICONS svgs carry only a viewBox (no width/height) so
   each call site sizes them; the form's scope cards render them at 24px. */
.scope-card__icon svg {
  width: 24px;
  height: 24px;
}
.scope-card:has(input:checked) .scope-card__icon,
.scope-card:has(input:checked) .scope-card__title {
  color: var(--brand-red);
}
.scope-card__title {
  font-weight: 500;
  color: var(--text);
}
.scope-card__help {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0;
}
@media (max-width: 640px) {
  .scope-grid { grid-template-columns: 1fr; }
}

/* ===== Location filters (campaign form) ===== */
.loc-filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 12px 16px;
  margin-bottom: 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
}

.loc-filters__field {
  min-width: 0;
}

.loc-filters__label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  margin-bottom: 4px;
}

.loc-filters__hint {
  display: block;
  margin-top: 4px;
  font-size: 11px;
  color: var(--text-secondary);
  line-height: 1.4;
}

/* ===== Tag select ===== */
.tag-select {
  position: relative;
  width: 100%;
}

.tag-select__box {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  min-height: 38px;
  padding: 4px 8px;
  border: 1px solid var(--gray-300);
  border-radius: 6px;
  background: var(--white);
  cursor: text;
  transition: border-color var(--transition);
}

.tag-select--open .tag-select__box {
  border-color: var(--blue);
}

.tag-select__pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 4px 2px 8px;
  background: #3b8bd5;
  color: var(--white);
  border-radius: 4px;
  font-size: 13px;
  line-height: 1.4;
}

.tag-select__pill-label {
  white-space: nowrap;
}

.tag-select__pill-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: none;
  background: rgba(255, 255, 255, 0.2);
  color: var(--white);
  border-radius: 3px;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  font-family: inherit;
}

.tag-select__pill-x:hover {
  background: rgba(255, 255, 255, 0.4);
}

.tag-select__input {
  flex: 1 1 120px;
  min-width: 120px;
  border: none !important;
  outline: none !important;
  font-size: 13px;
  font-family: var(--font);
  background: transparent;
  padding: 4px 0;
  color: var(--text);
  max-width: none !important;
  width: auto !important;
}

.tag-select__input::placeholder {
  color: var(--gray-300);
}

.tag-select__dropdown {
  position: absolute;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  z-index: 50;
  margin: 0;
  padding: 4px 0;
  list-style: none;
  background: var(--white);
  border: 1px solid var(--gray-300);
  border-radius: 6px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
  max-height: 240px;
  overflow-y: auto;
}

.tag-select__option {
  padding: 8px 14px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text);
}

.tag-select__option:hover {
  background: rgba(237, 28, 36, 0.08);
}

.tag-select__empty {
  padding: 8px 14px;
  font-size: 13px;
  color: var(--text-secondary);
  cursor: default;
  list-style: none;
}

.tag-select__source {
  display: none;
}

/* ===== Forms ===== */
fieldset {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  background: var(--white);
}

legend {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  padding: 0 8px;
}

label {
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="date"],
input[type="time"],
input[type="url"],
input[type="search"],
select,
textarea {
  width: 100%;
  max-width: 400px;
  font-family: var(--font);
  font-size: 15px;
  padding: 10px 14px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  background: var(--white);
  color: var(--text);
  transition: border-color var(--transition), box-shadow var(--transition);
  outline: none;
}

input:focus, select:focus, textarea:focus {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(237, 28, 36, 0.15);
}

input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--blue);
  margin-right: 6px;
  vertical-align: middle;
}

input[type="radio"] {
  accent-color: var(--blue);
  margin-right: 6px;
}

input[type="file"] {
  font-family: var(--font);
  font-size: 14px;
}

input[type="submit"] {
  font-family: var(--font);
  font-size: 14px;
  font-weight: 500;
  padding: 10px 24px;
  border-radius: var(--radius-full);
  border: none;
  background: var(--black);
  color: var(--white);
  cursor: pointer;
  transition: all var(--transition);
}
input[type="submit"]:hover {
  background: #333;
}

/* ===== Flash messages ===== */
.flash {
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 24px;
}

.flash--notice {
  background: #f0fdf4;
  color: #166534;
  border: 1px solid #bbf7d0;
}

.flash--alert {
  background: #fef2f2;
  color: #991b1b;
  border: 1px solid #fecaca;
}

/* ===== Status dots ===== */
.status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 6px;
}
.status-dot--active { background: var(--green); }
.status-dot--pending { background: var(--yellow); }
.status-dot--draft { background: var(--gray-300); }
.status-dot--archived { background: var(--text-secondary); }

/* ===== Clock-skew badge (kiosk BIOS-battery health) ===== */
.clock-skew {
  display: inline-block;
  font-size: 0.8em;
  margin-top: 2px;
  padding: 1px 6px;
  border-radius: 4px;
  font-weight: 500;
}
.clock-skew--ok { background: #e6f4ea; color: #137333; }
.clock-skew--warning { background: #fef7e0; color: #7a5300; }
.clock-skew--danger { background: #fce8e6; color: #a50e0e; }

/* ===== Media grid ===== */
.media-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  text-align: center;
  transition: all var(--transition);
}
.media-card:hover {
  box-shadow: var(--shadow-md);
}

.media-card img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  margin-bottom: 8px;
}

/* ===== File upload zone ===== */
.file-upload-zone {
  text-align: center;
  padding: 8px 0 4px;
}

.file-upload-zone__input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.file-upload-zone__label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 28px;
  border: 1.5px solid var(--black);
  border-radius: var(--radius-full);
  background: var(--white);
  color: var(--black);
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all var(--transition);
  -webkit-font-smoothing: antialiased;
}

.file-upload-zone__label + .file-upload-zone__label {
  margin-left: 8px;
}

.file-upload-zone__label:hover {
  background: var(--black);
  color: var(--white);
}

/* Keyboard focus ring for button-driven upload zones (device/location photo),
   whose hidden input is tabindex="-1". The .file-upload-zone__input:focus rule
   below still serves the campaigns form, where the input keeps a native label. */
.file-upload-zone__label:focus-visible {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(237, 28, 36, 0.15);
}

.file-upload-zone__label:hover .file-upload-zone__icon {
  color: var(--white);
}

.file-upload-zone__icon {
  display: flex;
  align-items: center;
  color: var(--black);
  transition: color var(--transition);
}

.file-upload-zone__text {
  line-height: 1;
}

.file-upload-zone__hint {
  margin-top: 10px;
  font-size: 13px;
  color: var(--text-secondary);
}

.file-upload-zone__input:focus + .file-upload-zone__label {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(237, 28, 36, 0.15);
}

.file-upload-zone--dragover {
  background: rgba(237, 28, 36, 0.08);
  outline: 2px dashed var(--blue);
  outline-offset: -4px;
  border-radius: var(--radius-sm);
}

/* ===== Single-photo upload (location/device) ===== */
.photo-upload {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.photo-upload__preview {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 220px;
  max-height: 360px;
  border: 1.5px dashed var(--gray-200);
  border-radius: var(--radius-sm);
  background: var(--gray-50);
  color: var(--text-secondary);
  font-size: 13px;
  overflow: hidden;
}

.photo-upload__remove {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--gray-200);
  background: rgba(255, 255, 255, 0.92);
  color: var(--text-secondary);
  font-size: 20px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  text-decoration: none;
  transition: all var(--transition);
}

.photo-upload__remove:hover {
  background: #fee2e2;
  color: var(--red);
  border-color: #fecaca;
}

.photo-upload__preview--filled {
  border-style: solid;
  background: var(--white);
  padding: 0;
}

.photo-upload__preview img {
  display: block;
  max-width: 100%;
  max-height: 360px;
  object-fit: contain;
}

.photo-upload__placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 24px;
  text-align: center;
}

.photo-upload__placeholder-icon {
  color: var(--text-secondary);
  opacity: 0.6;
}

/* ===== Upload previews ===== */
.upload-previews {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 16px;
}

.upload-previews:empty {
  display: none;
}

.upload-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  background: var(--gray-50);
  transition: all var(--transition);
  position: relative;
}

.upload-card--completed {
  border-color: #bbf7d0;
  background: #f0fdf4;
}

.upload-card__thumb {
  width: 40px;
  height: 40px;
  border-radius: 8px;
  overflow: hidden;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--gray-200);
}

.upload-card__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.upload-card__thumb--video {
  background: linear-gradient(135deg, #eff6ff, #dbeafe);
  color: #2563eb;
}

.upload-card__thumb--audio {
  background: linear-gradient(135deg, #faf5ff, #f3e8ff);
  color: #7c3aed;
}

.upload-card__info {
  flex: 1;
  min-width: 0;
}

.upload-card__name {
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.upload-card__size {
  font-size: 11px;
  color: var(--text-secondary);
  margin-bottom: 4px;
}

.upload-card__progress-wrap {
  height: 4px;
  border-radius: 2px;
  background: var(--gray-200);
  overflow: hidden;
}

.upload-card__progress-bar {
  height: 100%;
  width: 0%;
  background: var(--blue);
  border-radius: 2px;
  transition: width 0.15s ease;
}

.upload-card--completed .upload-card__progress-bar {
  background: var(--green);
}

.upload-card__status {
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 2px;
}

.upload-card__status--error {
  color: var(--red);
}

.upload-card__remove {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-size: 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: all var(--transition);
  font-family: var(--font);
}

.upload-card__remove:hover {
  background: #fee2e2;
  color: var(--red);
}

/* ===== Toast ===== */
.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--black);
  color: var(--white);
  padding: 12px 24px;
  border-radius: var(--radius-full);
  font-size: 14px;
  font-weight: 500;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
  z-index: 200;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease, transform 0.3s ease;
  -webkit-font-smoothing: antialiased;
}

.toast--visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
}

/* ===== Location Map ===== */
.location-map {
  height: 360px;
  margin-top: 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  overflow: hidden;
  box-shadow: var(--shadow-md);
}

/* ===== Locations Overview Map ===== */
.location-overview-map__card {
  padding: 0;
  overflow: hidden;
}

.location-overview-map {
  width: 100%;
  height: 420px;
  background: var(--bg);
}

@media (max-width: 768px) {
  .location-overview-map { height: 280px; }
}

.map-info {
  font-family: 'Geist', system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text, #231f20);
  padding: 2px 4px 4px;
  min-width: 180px;
}

.map-info__title {
  display: block;
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 4px;
}

.map-info__address {
  color: var(--brand-gray);
  margin-bottom: 8px;
}

.map-info__link {
  display: inline-block;
  font-weight: 500;
  color: var(--brand-red);
  text-decoration: none;
}

.map-info__link:hover { text-decoration: underline; }

/* ===== File Manager ===== */
.fm__layout {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 24px;
  min-height: 500px;
}

@media (max-width: 768px) {
  .fm__layout { grid-template-columns: 1fr; }
}

.fm__sidebar {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.fm__sidebar-section {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.fm__sidebar-title {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  padding: 0 10px;
  margin-bottom: 4px;
}

.fm__sidebar-link {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
  transition: background var(--transition);
}
.fm__sidebar-link:hover {
  background: var(--gray-100);
  opacity: 1;
}
.fm__sidebar-link--active {
  background: var(--gray-200);
}
.fm__sidebar-link svg {
  flex-shrink: 0;
  color: var(--text-secondary);
}
.fm__sidebar-link--active svg {
  color: var(--blue);
}

.fm__main {
  min-width: 0;
}

.fm__toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

/* Bulk bar */
.fm__bulk-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  margin-bottom: 16px;
  background: var(--brand-red-soft);
  border: 1px solid var(--brand-red);
  border-radius: var(--radius-sm);
}
.fm__bulk-text {
  font-size: 13px;
  color: var(--brand-red-dark);
}

/* File card */
.fm__file-card {
  position: relative;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: all var(--transition);
}
.fm__file-card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--gray-300);
  transform: translateY(-1px);
}

.fm__file-check {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(255,255,255,0.9);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0,0,0,0.15);
  opacity: 0;
  transition: opacity var(--transition);
}
.fm__file-card:hover .fm__file-check {
  opacity: 1;
}
.fm__file-check input {
  margin: 0;
  width: 14px;
  height: 14px;
}
/* Always show if checked */
.fm__file-check:has(input:checked) {
  opacity: 1;
}

.fm__file-thumb {
  width: 100%;
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--gray-50);
}
.fm__file-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.fm__file-info {
  padding: 8px 10px;
}

.fm__file-name {
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: text;
  padding: 2px 0;
  border-radius: 3px;
  transition: background var(--transition);
}
.fm__file-name:hover {
  background: var(--gray-100);
}

.fm__file-meta {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 8px;
  align-items: center;
  margin-top: 4px;
  font-size: 11px;
  color: var(--text-secondary);
}

.fm__file-meta__badge {
  align-self: center;
}

.fm__file-meta__values {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.fm__file-meta__values > span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.fm__file-meta__status {
  margin-top: 2px;
}

.fm__file-actions {
  position: absolute;
  top: 8px;
  right: 8px;
  opacity: 0;
  transition: opacity var(--transition);
}
.fm__file-card:hover .fm__file-actions {
  opacity: 1;
}

/* Inline rename input */
.rename-input {
  font-family: var(--font);
  font-size: 12px;
  font-weight: 500;
  padding: 2px 4px;
  border: 1px solid var(--blue);
  border-radius: 4px;
  outline: none;
  width: 100%;
  box-shadow: 0 0 0 3px rgba(237, 28, 36, 0.15);
}

/* ===== Tabs ===== */
.tabs {
  display: flex;
  border-bottom: 1px solid var(--border);
  margin: -20px -20px 20px -20px;
  padding: 0 8px;
  gap: 0;
}

/* Same problem .subnav-scroll solves for the admin sub-nav: this non-wrapping
   flex strip is wider than a phone viewport, and as a direct child of the card
   it widened the whole PAGE — which in turn stretched the fixed .nav-scrim past
   the viewport and left every mobile page scrolling horizontally by ~11px.
   Scroll the strip instead (CLAUDE.md mobile rule). */
@media (max-width: 880px) {
  .tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .tabs__tab { flex: 0 0 auto; }
}

.tabs__tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 12px 18px;
  font-family: var(--font);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 8px 8px 0 0;
  cursor: pointer;
  transition: all var(--transition);
  -webkit-font-smoothing: antialiased;
  position: relative;
  bottom: -1px;
}

.tabs__tab:hover {
  color: var(--text);
  background: var(--gray-50);
}

.tabs__tab--active {
  color: var(--text);
  background: var(--white);
  border-bottom-color: var(--blue);
}

.tabs__tab--active:hover {
  background: var(--white);
}

.tabs__tab svg {
  opacity: 0.5;
}
.tabs__tab--active svg {
  opacity: 1;
  color: var(--blue);
}

.tabs__panel {
  transition: opacity 0.2s ease;
}

.tabs__panel--visible {
  opacity: 1;
}

.tabs__panel--hidden {
  display: none;
  opacity: 0;
}

/* ===== File Browser ===== */
.browser__toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.browser__breadcrumbs {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  min-width: 0;
  overflow-x: auto;
}

.browser__crumb {
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
  padding: 2px 6px;
  border-radius: 4px;
  transition: all var(--transition);
}
.browser__crumb:hover {
  background: var(--gray-100);
  color: var(--text);
  opacity: 1;
}
.browser__crumb--active {
  color: var(--text);
  font-weight: 500;
}

.browser__crumb-sep {
  color: var(--gray-300);
  font-size: 12px;
}

/* Browser grid */
.browser__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
}

.browser__folder-card-wrap {
  position: relative;
}

.fm__folder-check {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 2;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(255,255,255,0.9);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0,0,0,0.15);
  opacity: 0;
  transition: opacity var(--transition);
}
.browser__folder-card-wrap:hover .fm__folder-check {
  opacity: 1;
}
.fm__folder-check:has(input:checked) {
  opacity: 1;
}
.fm__folder-check input {
  margin: 0;
  width: 14px;
  height: 14px;
}

.browser__folder-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 16px 8px 12px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  text-decoration: none;
  color: var(--text);
  transition: all var(--transition);
  text-align: center;
}
.browser__folder-card:hover {
  background: var(--gray-100);
  border-color: var(--gray-200);
  opacity: 1;
}

.browser__folder-icon {
  color: #5A7BB0;
}

.browser__folder-name {
  font-size: 12px;
  font-weight: 500;
  line-height: 1.3;
  word-break: break-word;
}

.browser__folder-count {
  font-size: 11px;
  color: var(--text-secondary);
}

/* Browser list */
.browser__list {
  display: flex;
  flex-direction: column;
}

.browser__list-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  transition: background var(--transition);
  text-decoration: none;
  color: var(--text);
  cursor: pointer;
}
.browser__list-row:hover {
  background: var(--gray-100);
  opacity: 1;
}

.browser__list-row--folder {
  font-weight: 500;
}

.browser__list-icon {
  flex-shrink: 0;
  width: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.browser__list-icon--folder {
  color: #5A7BB0;
}

.browser__list-name {
  flex: 1;
  font-size: 13px;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.browser__list-meta {
  font-size: 11px;
  color: var(--text-secondary);
  flex-shrink: 0;
}

.browser__list-check {
  flex-shrink: 0;
}

/* Browser empty */
.browser__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 32px;
  color: var(--text-secondary);
  font-size: 13px;
}

/* ===== Selection Tray ===== */
.tray__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.tray__item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--gray-50);
  transition: background var(--transition);
}
.tray__item:hover {
  background: var(--gray-100);
}

.tray__pos {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--gray-200);
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.tray__name {
  flex: 1;
  font-size: 13px;
  font-weight: 500;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tray__actions {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
  opacity: 0;
  transition: opacity var(--transition);
}
.tray__item:hover .tray__actions {
  opacity: 1;
}

.tray__btn {
  width: 24px;
  height: 24px;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: var(--text-secondary);
  font-size: 10px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all var(--transition);
  font-family: var(--font);
}
.tray__btn:hover {
  background: var(--gray-200);
  color: var(--text);
}
.tray__btn--remove:hover {
  background: #fee2e2;
  color: var(--red);
}

/* ===== Badges ===== */
.badge {
  display: inline-block;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 2px 6px;
  border-radius: 4px;
  text-transform: uppercase;
  -webkit-font-smoothing: antialiased;
}
.badge--green  { background: #dcfce7; color: #166534; }
.badge--blue   { background: #dbeafe; color: #1e40af; }
.badge--purple { background: #f3e8ff; color: #6b21a8; }
.badge--gray   { background: #e5e7eb; color: #374151; }
.badge--red    { background: #fee2e2; color: #991b1b; }

.media-picker__badge {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 1;
}

/* ===== Media preview (video) ===== */
.media-preview {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: #000;
}

.media-preview__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.media-preview__overlay {
  position: absolute;
  bottom: 6px;
  right: 6px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
}

.media-preview__duration {
  position: absolute;
  bottom: 6px;
  left: 6px;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.02em;
}

/* ===== Media placeholders ===== */
.media-placeholder {
  width: 100%;
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--gray-100);
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  font-size: 11px;
  font-weight: 500;
}

.media-placeholder--audio {
  background: linear-gradient(135deg, #faf5ff, #f3e8ff);
  color: #7c3aed;
}

.media-placeholder--video {
  background: linear-gradient(135deg, #eff6ff, #dbeafe);
  color: #2563eb;
}

/* Small fixed-size variant used by the campaign report row (helpers'
   medium_report_thumbnail dash fallback). Overrides the base 100%-of-
   container sizing with a 56x56 cell that matches .report-media__thumb. */
.media-placeholder--sm {
  width: 56px;
  height: 56px;
  aspect-ratio: unset;
  font-size: 18px;
  line-height: 1;
  gap: 0;
}

/* ===== Utilities ===== */
.flex { display: flex; }
.flex-wrap { flex-wrap: wrap; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-6 { gap: 6px; }
.gap-8 { gap: 8px; }
.gap-12 { gap: 12px; }
.gap-16 { gap: 16px; }
.gap-24 { gap: 24px; }
.mt-16 { margin-top: 16px; }
.mt-24 { margin-top: 24px; }
.mt-32 { margin-top: 32px; }
.mb-8 { margin-bottom: 8px; }
.mb-16 { margin-bottom: 16px; }
.mb-24 { margin-bottom: 24px; }
.text-right { text-align: right; }
.text-center { text-align: center; }
.text-secondary { color: var(--text-secondary); }
.text-sm { font-size: 13px; }

/* ===== Page header ===== */
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 24px;
}
.page-header h1 { margin-bottom: 0; }

/* ===== Toolbar ===== */
.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 24px;
  flex-wrap: wrap;
}
.toolbar__spacer { flex: 1; min-width: 0; }

/* Export CSV + grid/table toggle grouped at the far right, sitting close
   together (small gap) so they read as adjacent, neighbouring controls. */
.toolbar__actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

/* Left-aligned filter cluster: search box + (optional) category filter, parted
   by a subtle vertical divider. Wraps to full width on phones. */
.toolbar__filters {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1 1 auto;
  min-width: 0;
  flex-wrap: wrap;
}
.toolbar__divider {
  flex: 0 0 auto;
  width: 1px;
  align-self: stretch;
  min-height: 24px;
  background: var(--border);
}
.loc-cat-filter {
  flex: 1 1 220px;
  min-width: 200px;
  max-width: 340px;
}

/* Live "showing X of Y" summary sitting just under the toolbar. */
.list-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: -12px 0 18px;
  font-size: 13px;
  color: var(--text-secondary);
}
.list-summary__dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--blue);
}

/* list-search (Stimulus) hides non-matching rows with this class instead of an
   inline display, so a row's own inline layout (e.g. the franchisee owner rows'
   display:flex) survives being re-shown. !important beats any inline display. */
.js-search-hidden { display: none !important; }

@media (max-width: 640px) {
  .toolbar__divider { display: none; }
  .toolbar__filters .search-wrap,
  .toolbar__filters .loc-cat-filter {
    flex: 1 1 100%;
    max-width: none;
  }
}

/* ===== Filter pills (Filtry / Sortuj on campaigns toolbar) ===== */
.filter-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border);
  background: var(--white);
  color: var(--text);
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
  transition: border-color var(--transition), background var(--transition);
}
.filter-pill::-webkit-details-marker { display: none; }
.filter-pill > svg { color: var(--text-secondary); flex-shrink: 0; }
.filter-pill > strong { font-weight: 500; color: var(--text); }
.filter-pill:hover { border-color: var(--gray-300); }
.filter-pill--active,
.filter-pill-menu[open] > .filter-pill {
  background: var(--gray-50);
  border-color: var(--gray-300);
}
.filter-pill__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--radius-full);
  background: var(--brand-red);
  color: var(--white);
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 500;
  margin-left: 2px;
}

.filter-pill-menu {
  position: relative;
  display: inline-block;
}
.filter-pill-menu__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 240px;
  padding: 12px;
  background: var(--white);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.filter-pill-menu__panel--sort {
  min-width: 220px;
  padding: 6px;
  gap: 2px;
}

.filter-pill-menu__label {
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-secondary);
  padding: 4px 8px 2px;
}

.filter-pill-menu__input {
  width: 100%;
  max-width: none;
  padding: 8px 10px;
  font-size: 13px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.filter-pill-menu__input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--brand-red-soft);
}

.filter-pill-menu__form {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.filter-pill-menu__actions {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-top: 4px;
}

.filter-pill-menu__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 10px;
  font-size: 13px;
  color: var(--text);
  border-radius: 7px;
  transition: background var(--transition);
  text-decoration: none;
}
.filter-pill-menu__item:hover {
  background: var(--gray-100);
  opacity: 1;
}
.filter-pill-menu__item--active {
  color: var(--brand-red);
  font-weight: 500;
}
.filter-pill-menu__item--active svg { color: var(--brand-red); }

/* ===== Segmented Control ===== */
.segmented-control {
  display: inline-flex;
  background: var(--gray-200);
  border-radius: var(--radius-sm);
  padding: 3px;
  gap: 2px;
  margin-bottom: 24px;
}

.segmented-control__btn {
  font-family: var(--font);
  font-size: 13px;
  font-weight: 500;
  padding: 6px 14px;
  border-radius: 8px;
  color: var(--text-secondary);
  text-decoration: none;
  transition: all var(--transition);
  white-space: nowrap;
  -webkit-font-smoothing: antialiased;
}
.segmented-control__btn:hover {
  color: var(--text);
  opacity: 1;
}
.segmented-control__btn--active {
  background: var(--white);
  color: var(--text);
  box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}
.segmented-control__btn--active:hover {
  opacity: 1;
}

/* ===== Segmented control — pill variant (campaigns index) =====
   Pill-shaped, hairline border, with inline monospace counts next to
   each label. Counts turn brand-red when the segment is active. */
.seg {
  display: inline-flex;
  padding: 3px;
  background: var(--gray-50);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
}

.seg__btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 12px;
  border-radius: var(--radius-full);
  font-family: var(--font);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--transition), color var(--transition);
  -webkit-font-smoothing: antialiased;
}
.seg__btn:hover {
  color: var(--text);
  opacity: 1;
}
.seg__btn--active {
  background: var(--white);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(35, 31, 32, 0.06);
}
.seg__btn--active:hover {
  opacity: 1;
}

/* .subnav-scroll solves this for the admin sub-nav, but every other .seg (the
   activity time windows, the locations tabs) is a bare inline-flex item that
   refuses to shrink below its content, so a full chip row pushes the page
   sideways on a phone. Let the pill shrink and scroll its own chips instead. */
@media (max-width: 880px) {
  .seg {
    max-width: 100%;
    min-width: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .seg__btn { flex: 0 0 auto; }
}

.seg__count {
  font-family: var(--font-mono);
  font-feature-settings: 'tnum';
  font-variant-numeric: tabular-nums;
  font-size: 10.5px;
  color: var(--text-secondary);
}
.seg__btn--active .seg__count {
  color: var(--brand-red);
}

/* The admin sub-nav packs many tabs into a single non-wrapping .seg pill. On a
   phone that pill is wider than the viewport, so scroll it horizontally inside
   this wrapper instead of widening the whole page (CLAUDE.md mobile rule). */
.subnav-scroll {
  max-width: 100%;
  margin-bottom: 24px;
}
@media (max-width: 880px) {
  .subnav-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* ===== View Toggle ===== */
.view-toggle {
  display: inline-flex;
  background: var(--gray-200);
  border-radius: var(--radius-sm);
  padding: 3px;
  gap: 2px;
}

.view-toggle__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 30px;
  border-radius: 7px;
  color: var(--text-secondary);
  transition: all var(--transition);
  text-decoration: none;
}
.view-toggle__btn:hover {
  color: var(--text);
  opacity: 1;
}
.view-toggle__btn--active {
  background: var(--white);
  color: var(--text);
  box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}
.view-toggle__btn--active:hover {
  opacity: 1;
}

/* ===== Table avatar ===== */
.table-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  border: 1px solid var(--gray-200);
}

.table-avatar--empty {
  background: var(--gray-100);
  display: flex;
  align-items: center;
  justify-content: center;
}

.table-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 6px;
  transition: all var(--transition);
  text-decoration: none;
}
.table-action {
  color: var(--text-secondary);
}
.table-action:hover {
  background: var(--gray-100);
  color: var(--text);
  opacity: 1;
}
/* Edit is a non-destructive action — keep it neutral so it doesn't
   visually compete with the brand-red Delete next to it. */
.table-action--edit {
  color: var(--text-secondary);
}
.table-action--edit:hover {
  background: var(--gray-100);
  color: var(--text);
}
/* Delete is destructive — use the loud brand red so the warning signal
   is unambiguous (matches the old behavior before the rebrand swap
   accidentally made Edit red and Delete muted). */
.table-action--delete {
  color: var(--brand-red);
}
.table-action--delete:hover {
  background: var(--brand-red-soft);
  color: var(--brand-red-dark);
}
.table-action--start {
  color: var(--green);
}
.table-action--start:hover {
  background: rgba(46, 132, 86, 0.1);
}
.table-action--pause {
  color: var(--yellow);
}
.table-action--pause:hover {
  background: rgba(202, 138, 4, 0.12);
}

/* CSS-only tooltip on .table-action[data-tooltip]. Renders the attribute
   text as a small dark chip above the icon, with a tiny arrow. ~400 ms
   reveal delay so accidental flyovers don't trigger; hide is immediate. */
.table-action[data-tooltip],
.device-scope-icon[data-tooltip] {
  position: relative;
}
.table-action[data-tooltip]::after,
.table-action[data-tooltip]::before,
.device-scope-icon[data-tooltip]::after,
.device-scope-icon[data-tooltip]::before {
  position: absolute;
  left: 50%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.18s ease 0s;
}
.table-action[data-tooltip]::after,
.device-scope-icon[data-tooltip]::after {
  content: attr(data-tooltip);
  bottom: calc(100% + 6px);
  transform: translateX(-50%);
  padding: 5px 9px;
  background: var(--text);
  color: var(--bg);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: -0.005em;
  white-space: nowrap;
  border-radius: 6px;
  box-shadow: var(--shadow-md);
  z-index: 100;
}
.table-action[data-tooltip]::before,
.device-scope-icon[data-tooltip]::before {
  content: "";
  bottom: calc(100% + 2px);
  transform: translateX(-50%);
  border: 4px solid transparent;
  border-top-color: var(--text);
}
.table-action[data-tooltip]:hover::after,
.table-action[data-tooltip]:hover::before,
.table-action[data-tooltip]:focus-visible::after,
.table-action[data-tooltip]:focus-visible::before,
.device-scope-icon[data-tooltip]:hover::after,
.device-scope-icon[data-tooltip]:hover::before,
.device-scope-icon[data-tooltip]:focus-visible::after,
.device-scope-icon[data-tooltip]:focus-visible::before {
  opacity: 1;
  transition-delay: 0.4s;
}

/* Single device-kind-scope icon shown in the campaigns list "Urządzenia"
   column. Sizes the shared 24px scope SVG down to row scale. */
.device-scope-icon {
  display: inline-flex;
  align-items: center;
  color: var(--text-secondary);
}
.device-scope-icon svg {
  width: 18px;
  height: 18px;
}

.btn--outline-success {
  background: transparent;
  color: var(--green);
  border: 1px solid var(--green);
}
.btn--outline-success:hover {
  background: rgba(46, 132, 86, 0.08);
  opacity: 1;
}

.btn--outline-warning {
  background: transparent;
  color: var(--yellow);
  border: 1px solid var(--yellow);
}
.btn--outline-warning:hover {
  background: rgba(255, 159, 10, 0.08);
  opacity: 1;
}

/* ===== Empty state ===== */
.empty-state {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 60px 24px;
  text-align: center;
}

.empty-state__icon {
  font-size: 48px;
  margin-bottom: 16px;
  opacity: 0.3;
  line-height: 1;
}

.empty-state__text {
  color: var(--text-secondary);
  font-size: 16px;
  margin-bottom: 20px;
}

.empty-state__action {
  margin-bottom: 0;
}

/* ===== Sort chevron ===== */
.sort-chevron {
  vertical-align: middle;
  margin-left: 2px;
}

/* ===== Sort links ===== */
.sort-link {
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  transition: color var(--transition);
}
.sort-link:hover {
  color: var(--text);
  opacity: 1;
}
.sort-link--active {
  color: var(--text);
}

/* ===== Pills ===== */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 400;
  padding: 3px 10px 3px 8px;
  border-radius: var(--radius-full);
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.pill--green  { background: var(--green-soft); color: var(--green); }
.pill--yellow { background: #F6EEDA; color: #CA8A04; }
.pill--gray   { background: var(--gray-200); color: var(--text-secondary); }
.pill--red    { background: #F6E4E1; color: #A8362E; }
.pill--rose   { background: #F6E4E1; color: #A8362E; }
.pill--blue   { background: #E6ECF6; color: #2A5DB0; }

.pill .status-dot { margin-right: 0; }

.status-dot--scheduled { background: #2A5DB0; }
.status-dot--expired   { background: #A8362E; opacity: 0.8; }

/* ===== Stat cards ===== */
.stat-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 20px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--text);
  transition: all var(--transition);
  cursor: pointer;
}
.stat-card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--gray-300);
  transform: translateY(-1px);
  opacity: 1;
}

.stat-card__icon {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 4px;
}
.stat-card--draft .stat-card__icon {
  background: rgba(237, 28, 36, 0.08);
  color: var(--blue);
}
.stat-card--active .stat-card__icon {
  background: rgba(46, 132, 86, 0.08);
  color: var(--green);
}
.stat-card--paused .stat-card__icon {
  background: rgba(255, 159, 10, 0.08);
  color: var(--yellow);
}
.stat-card--locations .stat-card__icon {
  background: rgba(237, 28, 36, 0.08);
  color: var(--blue);
}
.stat-card--online .stat-card__icon {
  background: rgba(46, 132, 86, 0.08);
  color: var(--green);
}
.stat-card--offline .stat-card__icon {
  background: rgba(255, 59, 48, 0.08);
  color: var(--red);
}

/* Per-device-kind icon tints. iOS system-palette picks (purple/cyan/orange)
   that don't collide with the existing semantic colours: blue=locations/
   drafts, green=online/active, red=offline, yellow=paused. */
.stat-card--device-player .stat-card__icon {
  background: rgba(175, 82, 222, 0.08);
  color: #af52de;
}
.stat-card--device-radio .stat-card__icon {
  background: rgba(90, 200, 250, 0.08);
  color: #5ac8fa;
}
.stat-card--device-receipt_screen .stat-card__icon {
  background: rgba(255, 149, 0, 0.08);
  color: #ff9500;
}

/* Inert stat cards have no link target — the dashboard's device-kind
   sections render as <div>, not <a>, because there's no global devices
   index page to navigate to. Cancels the hover lift + pointer cursor
   so the user doesn't expect a click target. Card chrome (padding,
   border, icon tint, typography) stays identical to the link cards. */
.stat-card--inert {
  cursor: default;
}
.stat-card--inert:hover {
  box-shadow: none;
  border-color: var(--border);
  transform: none;
}

.stat-card__value {
  font-size: 32px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
}

.stat-card__label {
  font-size: 13px;
  color: var(--text-secondary);
  font-weight: 500;
}

/* ===== Location card ===== */
.location-card {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.location-card__header {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.location-card__icon {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  background: var(--gray-100);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 18px;
}

.location-card__devices {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.device-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--gray-200);
}
.device-row:last-child { border-bottom: none; }

/* ===== Campaign card ===== */
.campaign-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
}

.campaign-card__actions {
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding-top: 8px;
}

.campaign-card__top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

.campaign-card__meta {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}

.campaign-card__meta-item {
  font-size: 13px;
  color: var(--text-secondary);
}
.campaign-card__meta-item strong {
  color: var(--text);
}

.campaign-card__locations {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.location-tag {
  font-size: 11px;
  font-weight: 500;
  padding: 2px 8px;
  border-radius: var(--radius-full);
  background: var(--gray-100);
  color: var(--text-secondary);
  border: 1px solid var(--gray-200);
}

/* ===== Form sections ===== */
.form-section {
  margin-bottom: 40px;
}

.form-section__title {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 6px;
}

.form-section__subtitle {
  font-size: 14px;
  color: var(--text-secondary);
  margin-bottom: 20px;
}

/* ===== Media picker ===== */
.media-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 12px;
}

.media-picker__item {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  border: 2px solid transparent;
  transition: all var(--transition);
  cursor: pointer;
  background: var(--white);
}

.media-picker__item:hover {
  box-shadow: var(--shadow-md);
}

.media-picker__item--selected {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(237, 28, 36, 0.15);
}

.media-picker__thumb {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
  background: var(--gray-100);
}

.media-picker__placeholder {
  width: 100%;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--gray-100);
  color: var(--text-secondary);
  font-size: 12px;
}

.media-picker__check {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(255,255,255,0.9);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0,0,0,0.15);
}

.media-picker__check input[type="checkbox"] {
  margin: 0;
  width: 14px;
  height: 14px;
}

.media-picker__label {
  padding: 8px 10px;
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ===== Assigned media (edit view) ===== */
.assigned-media {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
}

.assigned-media__item {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--white);
  border: 1px solid var(--border);
  transition: all var(--transition);
}
.assigned-media__item:hover { box-shadow: var(--shadow-md); }

.assigned-media__thumb {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
}

.assigned-media__info {
  padding: 8px 10px;
}

.assigned-media__thumb-wrap {
  position: relative;
}

.assigned-media__download {
  position: absolute;
  right: 6px;
  bottom: 6px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  opacity: 0;
  transition: opacity var(--transition), background var(--transition);
  z-index: 2;
}
.assigned-media__item:hover .assigned-media__download {
  opacity: 1;
}
.assigned-media__download:hover {
  background: rgba(0, 0, 0, 0.8);
}

.assigned-media__remove {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(0,0,0,0.5);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  text-decoration: none;
  opacity: 0;
  transition: opacity var(--transition);
  line-height: 1;
  z-index: 3;
}
.assigned-media__item:hover .assigned-media__remove {
  opacity: 1;
}
.assigned-media__remove:hover {
  background: var(--red);
  opacity: 1;
}

.status-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-right: 8px;
  vertical-align: middle;
  flex-shrink: 0;
}
.status-dot--online  { background-color: #28a745; }
.status-dot--warning { background-color: #f0ad4e; }
.status-dot--offline { background-color: #dc3545; }

/* Per-kind device glyph cluster on the locations grid card + table. The glyph
   inherits its colour from .dev-glyph--<status>; the count badge sits over the
   icon's top-right corner and gets a matching coloured background per status
   (see the .dev-glyph--<status> .dev-glyph__count rules below). It can't use
   currentColor for the background because the badge sets its own white text
   colour, which currentColor would then resolve to — making it white-on-white. */
/* flex-wrap for the long tail: one glyph per kind fits a 240px card, but the
   cluster splits a kind by status, so a store with mixed-status kiosks can run
   to a dozen glyphs. Folds rather than widening the card past a 390px phone. */
.dev-cluster { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.dev-glyph { position: relative; display: inline-flex; line-height: 0; }
.dev-glyph--healthy { color: #28a745; }
.dev-glyph--warning { color: #f0ad4e; }
.dev-glyph--offline { color: #dc3545; }
.dev-glyph__count {
  position: absolute;
  top: -5px;
  right: -7px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  border-radius: 999px;
  background: #6c757d;
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  line-height: 14px;
  text-align: center;
}
.dev-glyph--healthy .dev-glyph__count { background: #28a745; }
.dev-glyph--warning .dev-glyph__count { background: #f0ad4e; }
.dev-glyph--offline .dev-glyph__count { background: #dc3545; }

.devices-filters {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 16px;
}
.devices-filters__row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.devices-filters__label {
  font-size: 0.85em;
  color: var(--text-secondary);
  min-width: 88px;
}
/* A single filter .seg (e.g. the kind row) is wider than a phone. Below the fine
   breakpoint, stack the label above the chips and let the chip group scroll
   horizontally rather than pushing the whole page wider than the viewport
   (CLAUDE.md mobile rule). Shared by the tenant /devices and admin devices list. */
@media (max-width: 640px) {
  .devices-filters__row {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
  }
  .devices-filters__row .seg {
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

.badge--default-always {
  display: inline-block;
  padding: 2px 8px;
  font-size: 0.75em;
  font-weight: 600;
  border-radius: 4px;
  background-color: #dbeafe;
  color: #1e40af;
}
.badge--default-fallback {
  display: inline-block;
  padding: 2px 8px;
  font-size: 0.75em;
  font-weight: 600;
  border-radius: 4px;
  background-color: #fef3c7;
  color: #92400e;
}

/* ===== Device preview ===== */
.preview-stage {
  position: relative;
  min-height: 80vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  padding: 40px 20px;
  isolation: isolate;
}
.preview-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, #1a1f2c 0%, #0a0d14 100%);
  filter: blur(40px);
  opacity: 0.55;
  z-index: -1;
}

.preview-device-name {
  margin: 0;
  font-size: 28px;
  font-weight: 600;
  color: var(--text);
  text-align: center;
}

.preview-tv {
  position: relative;
  width: min(80vw, 960px);
  aspect-ratio: 16 / 9;
}
/* Totem (portrait): flip the bezel to a vertical 9:16 frame, narrow it to fit
   the stage height, and move the speaker out of the now-tall right edge. */
.preview-tv--portrait {
  width: auto;
  height: min(70vh, 640px);
  aspect-ratio: 9 / 16;
}
.preview-tv--portrait .preview-speaker {
  right: auto;
  left: 12px;
}
.preview-tv__bezel {
  position: absolute;
  inset: 0;
  background: #0a0a0a;
  border-radius: 18px;
  padding: 14px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4), inset 0 2px 6px rgba(255, 255, 255, 0.08);
}
.preview-tv__screen {
  position: absolute;
  inset: 14px;
  background: #000;
  border-radius: 6px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.65);
  font-size: 14px;
}
.preview-tv__screen img,
.preview-tv__screen video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.preview-radio {
  width: min(60vw, 640px);
  aspect-ratio: 2 / 1;
  background: #0a0a0a;
  border-radius: 24px;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
}
.preview-radio__waveform {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 72%;
  height: 55%;
}

.preview-radio__bar {
  flex: 1 1 4px;
  max-width: 6px;
  min-width: 3px;
  height: 100%;
  background: var(--blue);
  transform-origin: center;
  transform: scaleY(0.9);
  opacity: 0.85;
  border-radius: 2px;
}

.preview-radio--playing .preview-radio__bar {
  animation: preview-radio-wave var(--duration, 1000ms) ease-in-out infinite;
  animation-delay: var(--delay, 0ms);
}

@keyframes preview-radio-wave {
  0%, 100% { transform: scaleY(0.9); }
  25%      { transform: scaleY(0.25); }
  50%      { transform: scaleY(1); }
  75%      { transform: scaleY(0.45); }
}

@media (prefers-reduced-motion: reduce) {
  .preview-radio--playing .preview-radio__bar {
    animation: none;
  }
  .sidebar,
  .app.is-nav-open .sidebar,
  .nav-scrim {
    transition: none;
  }
}

.preview-speaker {
  position: absolute;
  right: 16px;
  bottom: 16px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.15);
  color: white;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--transition);
  backdrop-filter: blur(8px);
  z-index: 2;
}
.preview-speaker:hover { background: rgba(255, 255, 255, 0.28); }
.preview-speaker--unmuted { background: var(--blue); }

.preview-caption {
  text-align: center;
  color: var(--text);
}
.preview-location-name {
  font-size: 18px;
  font-weight: 500;
}
.preview-location-address {
  font-size: 14px;
  color: #3a3a3f;
}

.preview-empty {
  color: rgba(255, 255, 255, 0.65);
  font-size: 14px;
}

.preview-campaigns-wrap {
  margin-top: 4px;
  text-align: center;
  max-width: 540px;
}
.preview-campaigns-title {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #3a3a3f;
  margin-bottom: 6px;
}
.preview-campaigns {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 8px;
  justify-content: center;
}
.preview-campaign {
  font-size: 13px;
  padding: 4px 10px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.5);
  color: #3a3a3f;
  border: 1px solid rgba(0, 0, 0, 0.08);
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.preview-campaign--active {
  background: var(--blue);
  color: white;
  border-color: var(--blue);
  font-weight: 500;
}

/* ===== Scale preview (tabbed, per display mode) ===== */
.scale-preview-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 16px;
}
.scale-preview-tab {
  font-size: 14px;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.04);
  color: var(--text-secondary, #555);
  border: 1px solid rgba(0, 0, 0, 0.08);
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.scale-preview-tab--active {
  background: var(--blue);
  color: white;
  border-color: var(--blue);
  font-weight: 500;
}
.scale-preview-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 520px;
  margin: 0 auto;
  aspect-ratio: 16 / 10;
  background: radial-gradient(ellipse at center, #1a1f2c 0%, #0a0d14 100%);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.scale-preview-stage img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}
.scale-preview-empty {
  color: #6b7280;
  font-size: 14px;
  padding: 24px;
  text-align: center;
}
.scale-preview-dims {
  text-align: center;
  margin: 8px 0 0;
}
.scale-preview-caption {
  text-align: center;
  margin: 4px 0 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--blue);
}

.preview-close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.75);
  color: #1d1d1f;
  text-decoration: none;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
  backdrop-filter: blur(8px);
  transition: background var(--transition), transform var(--transition);
  z-index: 3;
}
.preview-close:hover {
  background: rgba(255, 255, 255, 0.95);
  transform: scale(1.05);
}
.preview-audio-placeholder,
.preview-unknown {
  color: rgba(255, 255, 255, 0.85);
  font-size: 14px;
  padding: 24px;
  text-align: center;
}

/* ----- Cashregister kiosk preview ---------------------------------------
 * Sister to .preview-tv. Renders a mini-kiosk: TV stage on the left,
 * themed receipt pane on the right, operator-driven state controls below.
 * The pane reuses the live cashregister CSS (.receipt-lines, .receipt-line,
 * .receipt-running-total, .receipt-summary*) so the visual treatment stays
 * locked to production. Theme variables (--theme-bg/-fg/-accent,
 * --theme-summary-bg/-fg) are set by cashregister_preview_controller on
 * the root element.
 */
.preview-cashregister {
  --theme-bg: #ffffff;
  --theme-fg: #1f1f1f;
  --theme-accent: #0a6cff;
  --theme-summary-bg: #0a6cff;
  --theme-summary-fg: #ffffff;
  position: relative;
  width: min(95vw, 1200px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.preview-cashregister__chassis {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  display: flex;
  background: #0a0a0a;
  border-radius: 18px;
  padding: 14px;
  box-shadow:
    0 20px 60px rgba(0, 0, 0, 0.4),
    inset 0 2px 6px rgba(255, 255, 255, 0.08);
  overflow: hidden;
}

.preview-cashregister__tv {
  position: relative;
  background: #000;
  border-radius: 6px;
  overflow: hidden;
  flex-basis: calc(100% - var(--receipt-basis, 50%));
  flex-grow: 0;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: flex-basis 400ms ease;
}
/* Margin between TV and receipt only when the receipt pane is visible —
   in FULLSCREEN the receipt collapses to 0% and a constant right-margin
   would leave a stripe of bezel showing. */
.preview-cashregister:not([data-mode="fullscreen"]) .preview-cashregister__tv {
  margin-right: 6px;
}
.preview-cashregister__tv .preview-tv__screen {
  position: absolute;
  inset: 0;
  background: #000;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.65);
  font-size: 14px;
  overflow: hidden;
}
.preview-cashregister__tv .preview-tv__screen img,
.preview-cashregister__tv .preview-tv__screen video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.preview-cashregister__receipt {
  position: relative;
  background: var(--theme-bg);
  color: var(--theme-fg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: 6px;
  flex-basis: var(--receipt-basis, 50%);
  flex-grow: 0;
  flex-shrink: 0;
  transition: flex-basis 400ms ease;
}

/* State control bar — three pill buttons below the chassis. */
.preview-state-controls {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
}
.preview-state-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-radius: var(--radius-full);
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.85);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  backdrop-filter: blur(8px);
  transition: background var(--transition), color var(--transition), border-color var(--transition), transform var(--transition);
}
.preview-state-btn:hover {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}
.preview-state-btn[aria-pressed="true"] {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
  box-shadow: 0 6px 18px -8px rgba(10, 108, 255, 0.6);
}
.preview-state-btn__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.7;
}
.preview-state-btn[aria-pressed="true"] .preview-state-btn__dot {
  opacity: 1;
}

/* ===== Cashregister (Ekran z paragonem) ===== */
/*
 * Theme palette is written by receipt_controller as CSS variables on the
 * --theme-* root. Defaults below match ReceiptTheme.fetch("standard") so a
 * theme-less mount still renders sensibly.
 */
.cashregister {
  --theme-bg: #ffffff;
  --theme-fg: #1f1f1f;
  --theme-accent: #0a6cff;
  --theme-summary-bg: #0a6cff;
  --theme-summary-fg: #ffffff;
  display: flex;
  position: relative;
  width: 100vw;
  height: 100vh;
  background: #000;
  font-family: var(--font);
  cursor: none;
}
.cashregister__tv {
  position: relative;
  background: #000;
  /* --receipt-basis is owned by receipt_controller: set inline on the
     .cashregister root (boot value 0% = FULLSCREEN), and toggled by the
     state machine on every mode change. A [data-mode="fullscreen"] CSS
     rule here can't override the variable because inline styles on the
     same element win on specificity. */
  flex-basis: calc(100% - var(--receipt-basis, 50%));
  flex-grow: 0;
  flex-shrink: 0;
  transition: flex-basis 400ms ease;
}
.cashregister__receipt {
  position: relative;
  background: var(--theme-bg);
  color: var(--theme-fg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  flex-basis: var(--receipt-basis, 50%);
  flex-grow: 0;
  flex-shrink: 0;
  transition: flex-basis 400ms ease;
}
.receipt-lines {
  list-style: none;
  margin: 0;
  padding: 24px;
  flex: 1;
  /* Stays scrollable so receipt_controller.renderLines can pin the latest
     line into view via scrollTop = scrollHeight; the scrollbar gutter is
     hidden below so the customer never sees it. */
  overflow-y: auto;
  scrollbar-width: none; /* Firefox + standard */
  font-size: 1.25rem;
}
.receipt-lines::-webkit-scrollbar { /* Chromium / Iron */
  width: 0;
  height: 0;
}
.receipt-line {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 12px;
  padding: 6px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--theme-fg) 12%, transparent);
}
.receipt-line__qty   { font-variant-numeric: tabular-nums; opacity: 0.7; }
.receipt-line__name  { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.receipt-line__total { font-variant-numeric: tabular-nums; font-weight: 600; }

/* Promo signals — applied when the pos-agent matcher flags a line. */
.receipt-line[data-promo="matched"] .receipt-line__qty {
  /* Left-edge accent so the customer scans which lines are "in the promo"
     at a glance even when the inline badge gets text-overflow-clipped. */
  box-shadow: -3px 0 0 0 color-mix(in srgb, var(--theme-accent, #1f7a3a) 75%, transparent);
}
.receipt-line__badge {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--theme-accent, #1f7a3a) 18%, var(--theme-bg, #fff));
  color: color-mix(in srgb, var(--theme-accent, #1f7a3a) 90%, var(--theme-fg, #111));
  font-size: 0.72em;
  font-weight: 700;
  letter-spacing: 0.02em;
  vertical-align: 0.1em;
  font-variant-numeric: normal;
}

/* Computed-promo breakdown (Opust / Cena końcowa) — shown live for a promo
   the POS only applies at the summary screen (min-receipt). Renders as a
   full-width block under the qty/name/total row; the regular total above it
   is struck through to signal it's superseded by the computed final price. */
.receipt-line__total--struck {
  text-decoration: line-through;
  opacity: 0.55;
  font-weight: 500;
}
.receipt-line__promo {
  grid-column: 1 / -1;
  margin-top: 4px;
  padding-left: 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.receipt-line__promo-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 0.92em;
}
.receipt-line__promo-label { opacity: 0.85; }
.receipt-line__promo-amount { font-variant-numeric: tabular-nums; }
.receipt-line__promo-row--discount {
  color: color-mix(in srgb, var(--theme-accent, #1f7a3a) 80%, var(--theme-fg, #111));
}
.receipt-line__promo-row--final { font-weight: 700; }

/* Hint banner ("Dodaj 1 szt., aby aktywować…") shown above the lines
   list during RECEIPT mode. Controller toggles data-visible. */
.receipt-promo-banner {
  display: none;
  align-items: center;
  gap: 10px;
  margin: 8px 24px 4px;
  padding: 10px 16px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--theme-accent, #1f7a3a) 14%, var(--theme-bg, #fff));
  color: color-mix(in srgb, var(--theme-fg, #111) 85%, var(--theme-accent, #1f7a3a));
  font-size: 1.05rem;
  font-weight: 500;
  line-height: 1.3;
}
.receipt-promo-banner[data-visible="true"] { display: flex; }
.receipt-promo-banner__icon { font-size: 1.2em; flex-shrink: 0; }
.receipt-promo-banner__text { flex: 1; }

/* Running savings — sits below the "Razem" amount. Smaller than the
   running total but visually paired with it (same right-edge alignment,
   same tabular numerics). Hidden when no savings accrued. */
.receipt-running-savings {
  flex-shrink: 0;
  padding: 6px 24px 12px;
  background: color-mix(in srgb, var(--theme-bg) 92%, var(--theme-fg) 8%);
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
  grid-template-columns: 1fr auto auto;
  gap: 6px;
  display: none;
  color: color-mix(in srgb, var(--theme-accent, #1f7a3a) 85%, var(--theme-fg, #111));
}
.receipt-running-savings[data-visible="true"] { display: grid; }
.receipt-running-savings__label { text-align: left; opacity: 0.85; font-weight: 600; }
.receipt-running-savings__amount { text-align: right; font-weight: 700; }
.receipt-running-savings__currency { opacity: 0.7; }

.receipt-running-total {
  flex-shrink: 0;
  padding: 12px 24px;
  border-top: 1px solid color-mix(in srgb, var(--theme-fg) 18%, transparent);
  background: color-mix(in srgb, var(--theme-bg) 92%, var(--theme-fg) 8%);
  font-size: 1.5rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  grid-template-columns: 1fr auto;
  gap: 16px;
  display: none;
}
.receipt-running-total[data-visible="true"] { display: grid; }
.receipt-running-total__label  { text-align: left;  opacity: 0.7; }
.receipt-running-total__amount { text-align: right; font-weight: 700; }

/* ---- SUMMARY: receipt-artefact card -------------------------------------
 *
 * The summary moment is a "printed receipt" sitting on a theme-coloured
 * field. Two type families do the work: a serif display face for the
 * warmth-words ("Dziękujemy", "Zapraszamy ponownie") and a monospace face
 * for the numerics so amounts column on the baseline. No web fonts are
 * loaded — kiosks run offline — so both stacks fall back to fonts
 * shipped on the Windows kiosk image (Cambria/Constantia, Consolas).
 *
 * Entry is a single orchestrated landing: backdrop → card → thanks → label
 * → total → rows (staggered) → welcome. Welcome lands at ~900ms — comfortably
 * inside any realistic summary_duration_seconds. The model validation floor
 * is 0.3s but that's only set by tests; field default is 30s.
 *
 * Anchored on the `[data-visible="true"]` flip — the controller already
 * drives that. Keyframes deliberately stay on transform + opacity only
 * (no `filter:blur`, no `letter-spacing` tween) to keep the entry on the
 * compositor and avoid paint/layout cost on entry-level kiosk GPUs.
 */
.receipt-summary {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 40%, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.28) 78%),
    var(--theme-summary-bg);
  color: var(--theme-summary-fg);
  display: none;
  align-items: center;
  justify-content: center;
  padding: 32px;
  z-index: 5;
}
.receipt-summary[data-visible="true"] {
  display: flex;
  animation: receipt-backdrop-in 200ms ease-out both;
}

.receipt-summary__card {
  /* Paper palette is intentionally hard-coded, not theme-derived: every
     real receipt is printed on warm off-white stock regardless of the
     shop's brand colour, and the theme-summary-bg is still visible as
     the frame around the card. */
  --paper:       #fffdf4;
  --paper-edge:  #f3eedd;
  --ink:         #1a1612;
  --ink-soft:    #6a6258;
  --rule:        rgba(26, 22, 18, 0.16);
  --rule-strong: rgba(26, 22, 18, 0.42);

  --serif: "Cormorant Garamond", "Cormorant", "Hoefler Text", "Iowan Old Style",
           "Palatino Linotype", "Book Antiqua", Cambria, Constantia, Georgia, serif;
  --mono:  "JetBrains Mono", "IBM Plex Mono", "SF Mono", Menlo, Consolas,
           "DejaVu Sans Mono", "Courier New", monospace;

  width: min(560px, 92%);
  padding: 56px 56px 44px;
  border-radius: 10px;
  background-color: var(--paper);
  /* Faint paper grain — two offset dot patterns. Cheap and ships nowhere
     near the kiosk's GPU budget. */
  background-image:
    radial-gradient(rgba(26, 22, 18, 0.045) 1px, transparent 1.4px),
    radial-gradient(rgba(26, 22, 18, 0.025) 1px, transparent 1.4px),
    linear-gradient(180deg, var(--paper) 0%, var(--paper-edge) 100%);
  background-size: 7px 7px, 13px 13px, 100% 100%;
  background-position: 0 0, 3px 5px, 0 0;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.85) inset,
    0 0 0 1px rgba(26, 22, 18, 0.05),
    0 28px 70px -22px rgba(0, 0, 0, 0.55),
    0 10px 28px -14px rgba(0, 0, 0, 0.45);

  color: var(--ink);
  font-family: var(--serif);
  display: flex;
  flex-direction: column;
  gap: 28px;
  text-align: center;
  position: relative;

  animation: receipt-card-in 380ms cubic-bezier(0.16, 0.84, 0.32, 1) 60ms both;
}

.receipt-summary__thanks {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(2.6rem, 5.2vw, 3.6rem);
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--ink);
  animation: receipt-thanks-in 280ms cubic-bezier(0.2, 0.7, 0.3, 1) 200ms both;
}

.receipt-summary__total-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

/* Small-caps label flanked by rules — "— RAZEM —" editorial framing. */
.receipt-summary__label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  font-family: var(--serif);
  font-variant: small-caps;
  font-weight: 600;
  letter-spacing: 0.18em;
  font-size: 0.95rem;
  color: var(--ink-soft);
  text-transform: lowercase;
  animation: receipt-thanks-in 240ms ease-out 320ms both;
}
.receipt-summary__label::before,
.receipt-summary__label::after {
  content: "";
  flex: 0 0 auto;
  width: 56px;
  height: 0;
  border-top: 1px solid var(--rule-strong);
}

.receipt-summary__total {
  font-family: var(--mono);
  font-weight: 600;
  font-size: clamp(3.4rem, 7.6vw, 5rem);
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.18em;
  animation: receipt-total-in 360ms cubic-bezier(0.16, 0.84, 0.32, 1) 400ms both;
}

/* Big "Oszczędziłeś X zł" highlight shown on the summary card between
   the totals rows and the goodbye line. Same monospace numeric stack
   as the total so amounts align visually, but smaller and tinted
   with the theme's accent so it reads as a feel-good moment rather
   than another row of bookkeeping. Hidden when no savings accrued —
   non-promo carts look exactly as they did before the matcher. */
.receipt-summary__savings {
  display: none;
  margin: 22px auto 0;
  padding: 14px 20px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--theme-accent, #1f7a3a) 16%, var(--theme-bg, #fff));
  color: color-mix(in srgb, var(--theme-accent, #1f7a3a) 90%, var(--ink, #111));
  text-align: center;
  font-family: var(--mono);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.6rem, 3.2vw, 2.4rem);
  line-height: 1.1;
}
.receipt-summary__savings[data-visible="true"] { display: block; }
.receipt-summary__savings-label {
  display: block;
  font-family: var(--serif, serif);
  font-style: italic;
  font-weight: 500;
  font-size: 0.62em;
  letter-spacing: 0.02em;
  margin-bottom: 4px;
  opacity: 0.85;
}
.receipt-summary__savings-amount { font-weight: 700; }
.receipt-summary__savings-currency {
  font-family: var(--serif, serif);
  font-style: italic;
  font-weight: 500;
  font-size: 0.5em;
  margin-left: 0.18em;
  opacity: 0.75;
  transform: translateY(-0.4em);
  display: inline-block;
}
.receipt-summary__currency {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 500;
  font-size: 0.36em;
  color: var(--ink-soft);
  letter-spacing: 0;
  transform: translateY(-0.55em);
}

.receipt-summary__rows {
  margin: 0;
  padding: 26px 0 0;
  border-top: 1.5px dashed var(--rule-strong);
  display: flex;
  flex-direction: column;
  gap: 14px;
  text-align: left;
}

/* Three-column grid: label · leader · value. The middle column is the
   only flex column (1fr); the leader pseudo-element lives there with a
   dotted border-bottom, so dots appear only in the gap between label
   and value by construction — no background-masking, no rectangles
   showing through on textured surfaces. */
.receipt-summary__row {
  display: grid;
  grid-template-columns: max-content 1fr max-content;
  align-items: baseline;
  font-size: 1.25rem;
  /* Per-row stagger driven by source order — payment-type row may be
     hidden at runtime; nth-child still lays the rhythm down because we
     count from the DOM, not the layout. */
  animation: receipt-row-in 240ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
}
.receipt-summary__row:nth-child(1) { animation-delay: 540ms; }
.receipt-summary__row:nth-child(2) { animation-delay: 600ms; }
.receipt-summary__row:nth-child(3) { animation-delay: 660ms; }
.receipt-summary__row:nth-child(4) { animation-delay: 720ms; }

.receipt-summary__row::after {
  content: "";
  grid-column: 2;
  height: 2px;
  margin: 0 12px;
  /* radial-gradient produces a more visible dot pattern than
     border-bottom: dotted does at hairline thicknesses — Chromium packs
     border-dots tightly at 1.5px and the leader reads as a solid rule.
     This tiles dots at 8px intervals, matching the receipt-printer
     aesthetic we had before the rectangles issue. */
  background-image: radial-gradient(var(--rule-strong) 1px, transparent 1.4px);
  background-size: 8px 2px;
  background-repeat: repeat-x;
  align-self: center;
  transform: translateY(0.18em);
}

.receipt-summary__row dt {
  grid-column: 1;
  margin: 0;
  font-family: var(--serif);
  font-variant: small-caps;
  font-weight: 600;
  letter-spacing: 0.14em;
  font-size: 0.95rem;
  color: var(--ink-soft);
  white-space: nowrap;
}
.receipt-summary__row dd {
  grid-column: 3;
  margin: 0;
  font-family: var(--mono);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: 1.25rem;
  color: var(--ink);
}

.receipt-summary__welcome {
  padding-top: 22px;
  border-top: 1.5px dashed var(--rule-strong);
  font-family: var(--serif);
  font-style: italic;
  font-weight: 500;
  font-size: 1.5rem;
  color: var(--ink-soft);
  animation: receipt-welcome-in 260ms ease-out 820ms both;
}

@keyframes receipt-backdrop-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes receipt-card-in {
  from { opacity: 0; transform: translateY(28px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0)    scale(1);    }
}
@keyframes receipt-thanks-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0);   }
}
@keyframes receipt-total-in {
  from { opacity: 0; transform: scale(0.9); }
  to   { opacity: 1; transform: scale(1);   }
}
@keyframes receipt-row-in {
  from { opacity: 0; transform: translateX(-10px); }
  to   { opacity: 1; transform: translateX(0);     }
}
@keyframes receipt-welcome-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0);   }
}

.pos-source-status {
  position: absolute;
  bottom: 12px;
  right: 12px;
  z-index: 4;
  font-size: 0.75rem;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  padding: 4px 8px;
  border-radius: 4px;
  pointer-events: none;
  opacity: 0.7;
  transition: opacity 0.4s ease-out;
}
.pos-source-status[data-visible="false"] { opacity: 0; }
.pos-source-status[data-level="error"] { background: rgba(180, 30, 30, 0.85); opacity: 0.95; }
.pos-source-status[data-level="warn"]  { background: rgba(180, 130, 30, 0.85); opacity: 0.9; }
.pos-source-status[data-level="error"][data-visible="false"],
.pos-source-status[data-level="warn"][data-visible="false"] { opacity: 0; }

/* Themes — just decorations / filters; palette already lives in CSS vars. */
.cashregister.theme-mourning_grey,
.theme-mourning_grey { filter: grayscale(0.95); }

/* ============================================================
   Quiet Studio additions: dashboard hero, fleet panel, pills,
   monospace numeric helpers.
   ============================================================ */

/* Tabular numerics anywhere `.num` is applied. */
.num,
.stat-card__value {
  font-family: var(--font-mono);
  font-feature-settings: 'tnum';
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* ---- Dashboard hero ---- */
.page-hero {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 32px;
}

.page-hero__main { min-width: 0; }

.page-hero__eyebrow {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.page-hero__title {
  margin: 8px 0 6px;
  font-size: 36px;
  font-weight: 300;        /* matches the brand wordmark's light weight */
  letter-spacing: -0.02em;
  line-height: 1.05;
  color: var(--text);
}
.page-hero__icon {
  color: var(--brand-red);
  vertical-align: -3px;
  margin-right: 10px;
}
.page-hero__title em {
  /* "24" in the brand logo is a colour change, not italic. We mirror
     that treatment for the user's name in the greeting. */
  font-style: normal;
  font-weight: 400;
  color: var(--brand-red);
}

.page-hero__sub {
  color: var(--text-secondary);
  font-size: 14px;
}

/* ---- Dashboard sections ---- */
.dash-section {
  margin-top: 40px;
}
.dash-section__title {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.005em;
  margin: 0 0 14px;
  color: var(--text);
}

/* Cover thumbnail of the location's uploaded photo on the show page. */
.location-photo {
  width: 100%;
  max-width: 480px;
  height: 200px;
  object-fit: cover;
  border-radius: 8px;
  margin-bottom: 16px;
}

/* Read-only key/value list, used on the location show page. */
.detail-list {
  margin: 0;
}
.detail-list__row {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: 16px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.detail-list__row:last-child {
  border-bottom: none;
}
.detail-list__row dt {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
}
.detail-list__row dd {
  margin: 0;
  font-size: 14px;
  color: var(--text);
}
@media (max-width: 640px) {
  .detail-list__row {
    grid-template-columns: 1fr;
    gap: 4px;
  }
}

/* Activity log: "Show older / Newest" keyset-pagination control under the table. */
.load-more {
  display: flex;
  justify-content: center;
  padding: 16px 0 4px;
}

/* Activity log: full field-level diff on the activity show page —
   field | before | after. Stacks to one column on a phone. */
.change-grid {
  margin-top: 16px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.change-grid__row {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
  gap: 16px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  align-items: baseline;
  font-size: 14px;
}
.change-grid__row:last-child {
  border-bottom: none;
}
.change-grid__row--head {
  background: var(--gray-50);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
}
.change-grid__field {
  font-weight: 500;
  color: var(--text-secondary);
}
.change-grid__old {
  color: var(--text-secondary);
  text-decoration: line-through;
  overflow-wrap: anywhere;
}
.change-grid__new {
  color: var(--text);
  overflow-wrap: anywhere;
}
@media (max-width: 640px) {
  .change-grid__row {
    grid-template-columns: 1fr;
    gap: 4px;
  }
  .change-grid__row--head {
    display: none;
  }
  .change-grid__old {
    text-decoration: none;
  }
  .change-grid__old::before {
    content: "− ";
  }
  .change-grid__new::before {
    content: "+ ";
  }
}

/* Plain stacked list of records (e.g. the campaigns assigned to a location on
   the show page). Each row separated by a hairline, last row borderless. */
.record-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.record-list__item {
  padding: 6px 0;
  border-bottom: 1px solid var(--border);
}
.record-list__item:last-child {
  border-bottom: none;
}
/* Empty-state line shown in place of the list when there are no records. */
.record-list__empty {
  margin: 0;
  font-size: 13px;
  color: var(--text-secondary);
}

/* Sub-heading above each fleet-version table on /locations management.
   Smaller than .dash-section__title so it sits as a third-level header
   inside a section grid cell. */
.fleet-table__title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  margin: 0 0 10px;
  letter-spacing: -0.005em;
}

/* Optional right-side "Otwórz urządzenia →" link next to a section title. */
.dash-section__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 14px;
}
.dash-section__header .dash-section__title {
  margin: 0;
}

.dash-section__link {
  font-size: 13px;
  font-weight: 500;
  color: var(--brand-red);
  text-decoration: none;
  white-space: nowrap;
  transition: opacity var(--transition);
}
.dash-section__link:hover {
  opacity: 0.7;
}

.dash-stats {
  /* tighter than .grid--3 default minmax — three even columns */
  grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 880px) {
  .dash-stats { grid-template-columns: 1fr; }
}

/* ---- Campaigns stat cards (new chip-first layout) ----
   Replaces the icon-first .stat-card design for the three campaign tiles
   on the dashboard. Locations status block still uses .stat-card. */
.stat-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}
@media (max-width: 880px) {
  .stat-row { grid-template-columns: 1fr; }
}

.stat {
  position: relative;
  overflow: hidden;
  display: block;
  padding: 20px 22px 18px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
}
.stat:hover {
  border-color: var(--gray-300);
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
  opacity: 1;
}

.stat__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 14px;
}

.stat__label {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.005em;
}

.stat__chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: var(--radius-full);
  font-size: 11px;
  font-weight: 500;
  white-space: nowrap;
}
.stat__chip .dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
}

.stat--ok   .stat__chip { background: var(--green-soft); color: var(--green); }
.stat--ok   .stat__chip .dot {
  background: var(--green);
  box-shadow: 0 0 0 3px rgba(46, 132, 86, 0.18);
  animation: stat-pulse 2.4s ease-out infinite;
}
.stat--info .stat__chip { background: #E6ECF6; color: #2A5DB0; }
.stat--info .stat__chip .dot { background: #2A5DB0; }
.stat--warn .stat__chip { background: #F6EEDA; color: #CA8A04; }
.stat--warn .stat__chip .dot { background: #CA8A04; }
.stat--bad  .stat__chip { background: var(--brand-red-soft); color: var(--brand-red); }
.stat--bad  .stat__chip .dot { background: var(--brand-red); }

@keyframes stat-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(46, 132, 86, 0.35); }
  70%  { box-shadow: 0 0 0 7px rgba(46, 132, 86, 0); }
  100% { box-shadow: 0 0 0 0 rgba(46, 132, 86, 0); }
}

.stat__value {
  font-family: var(--font-mono);
  font-feature-settings: 'tnum';
  font-variant-numeric: tabular-nums;
  font-size: 44px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--text);
}

.stat__delta {
  margin-top: 8px;
  font-size: 12.5px;
  color: var(--text-secondary);
  line-height: 1.45;
}
.stat__delta strong { color: var(--text); font-weight: 600; }

/* ---- Sieć urządzeń panel ---- */
.fleet-panel {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
/* The device-fleet panel carries one card per device kind (5: players, totems,
   radios, cashregisters, scales). The modules panel reuses .fleet-panel but has
   its own card count, so the wider grid is scoped to the fleet variant only. */
.fleet-panel--devices { grid-template-columns: repeat(5, 1fr); }
@media (max-width: 1180px) {
  .fleet-panel { grid-template-columns: 1fr 1fr; }
  .fleet-panel--devices { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 640px) {
  .fleet-panel,
  .fleet-panel--devices { grid-template-columns: 1fr; }
}

.fleet-cell {
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  border-right: 1px solid var(--border);
}
.fleet-cell:last-child { border-right: none; }
@media (max-width: 880px) {
  .fleet-cell { border-right: none; border-bottom: 1px solid var(--border); }
  .fleet-cell:last-child { border-bottom: none; }
}

.fleet-cell__head {
  display: flex;
  align-items: center;
  gap: 10px;
  /* Reserve two lines so the bar below aligns across cells whether the
     kind name wraps (e.g. "Odtwarzacze TV") or fits on one line. */
  min-height: 39px;
}
.fleet-cell__icon {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  background: var(--gray-100);
  color: var(--text-secondary);
  flex-shrink: 0;
}
/* Harmonious per-kind tints — no rainbow. The activity-log chip (.type-chip)
   reuses the same per-kind colour as the icon square via the shared
   .fleet-cell--<kind> ancestor on the cell, so each tint pair lives in ONE place. */
.fleet-cell--player .fleet-cell__icon,
.fleet-cell--player .type-chip          { background: #ECF1EE; color: #2B6757; }
.fleet-cell--totem .fleet-cell__icon,
.fleet-cell--totem .type-chip           { background: #EDECF6; color: #4B3F8F; }
.fleet-cell--radio .fleet-cell__icon,
.fleet-cell--radio .type-chip           { background: #EEEAE0; color: #7A5C1F; }
.fleet-cell--receipt_screen .fleet-cell__icon,
.fleet-cell--receipt_screen .type-chip  { background: #E9EEF6; color: #2A5DB0; }
.fleet-cell--scale .fleet-cell__icon,
.fleet-cell--scale .type-chip           { background: #E4EFF1; color: #216C79; }
.fleet-cell--module .fleet-cell__icon   { background: #E6F2EF; color: #1F7A5C; }
.fleet-cell--pact24_server .fleet-cell__icon,
.fleet-cell--pact24_server .type-chip   { background: #EAEDF1; color: #475569; }

/* Activity log "Obiekt" cell: a type-tinted icon square + headline + a small
   type chip. Reuses .fleet-cell__icon and the .fleet-cell--<kind> tints above. */
.activity-resource {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.activity-resource__text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.activity-resource__text .row-title {
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.type-chip {
  align-self: flex-start;
  display: inline-block;
  max-width: 100%;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.35;
  padding: 1px 8px;
  border-radius: var(--radius-full);
  background: var(--gray-200);
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.fleet-cell__name {
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.3;
}
.fleet-cell__count {
  margin-left: auto;
  font-size: 12px;
  color: var(--text-secondary);
}
/* "including N TVs" — the splitter line under the device count. Its own row so
   it never squeezes the count on a phone, right-aligned to the count above it.
   Scoped to this element rather than to .fleet-cell__count, which the module
   coverage and cashier support cards share. */
.fleet-cell__screens { display: block; text-align: right; }

.fleet-cell__bar {
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--gray-200);
  overflow: hidden;
  display: flex;
}
.fleet-cell__bar-fill {
  height: 100%;
  transition: width 800ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
.fleet-cell__bar-fill--on  { background: var(--green); }
.fleet-cell__bar-fill--off { background: var(--red); }

.fleet-cell__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  font-size: 12px;
  color: var(--text-secondary);
}
.fleet-cell__legend .num { color: var(--text); font-weight: 500; }
.fleet-cell__legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  display: inline-block;
}
.dot--on  { background: var(--green); }
.dot--off { background: var(--red); }
.dot--muted { background: var(--gray-300); }

/* ---- Wsparcie Kasjera (Cashier Support) dashboard module card ---- */
/* The modules panel carries two cards (Promotions + Cashier Support); its own
   2-col grid mirrors .fleet-panel--devices' scoped column count. */
.fleet-panel--modules { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 640px) { .fleet-panel--modules { grid-template-columns: 1fr; } }

/* position: relative anchors the flow connector to this cell's left border;
   the harmonious muted-gold tint is its own identity next to Promotions' green. */
.fleet-cell--cashier_support { position: relative; }
.fleet-cell--cashier_support .fleet-cell__icon { position: relative; background: #F1EADB; color: #7A5C1F; }

/* Module-scope chips: this module covers Promotions + Loyalty. */
.ws-scope { display: flex; align-items: center; gap: 7px; margin-top: 2px; }
.ws-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  font-weight: 500;
  color: #7A5C1F;
  background: #F4EEE0;
  border: 1px solid rgba(122, 92, 31, 0.16);
  padding: 3px 9px 3px 7px;
  border-radius: var(--radius-full);
}
.ws-tag svg { width: 11px; height: 11px; }
.ws-scope__plus { color: #7A5C1F; font-weight: 600; font-size: 12px; }

/* Flow connector capsule on the card's left border (promo data → overlay). */
.flow-link { position: absolute; top: 65px; left: 0; transform: translate(-50%, -50%); z-index: 3; pointer-events: none; }
.flow-link__cap {
  display: flex;
  align-items: center;
  gap: 4px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  padding: 4px 7px;
  box-shadow: 0 4px 14px -6px rgba(35, 31, 32, 0.22);
}
.flow-link__rail { position: relative; width: 22px; height: 8px; }
.flow-link__rail svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.flow-link__lane { stroke: var(--gray-200); stroke-width: 2; fill: none; }
.flow-link__lane--cut { stroke: #E0C3C0; }
.flow-link__flow {
  stroke: url(#cs-flowgrad);
  stroke-width: 2;
  fill: none;
  stroke-dasharray: 4 6;
  stroke-linecap: round;
  animation: flow-dash 1s linear infinite;
}
.flow-link__pk { fill: #7A5C1F; }
.flow-link__arrow { color: #7A5C1F; display: flex; }
.flow-link--broken .flow-link__cap { background: #FBF4F3; border-color: #F0D7D4; }
.flow-link--broken .flow-link__x { color: var(--brand-red); display: flex; }
@keyframes flow-dash { to { stroke-dashoffset: -20; } }
@media (max-width: 880px) { .flow-link { display: none; } }

/* Locked (not provisioned) upsell state. */
.fleet-cell--locked {
  background: repeating-linear-gradient(135deg, transparent, transparent 9px, rgba(35, 31, 32, 0.018) 9px, rgba(35, 31, 32, 0.018) 10px);
}
.fleet-cell--locked .lockwrap { display: flex; flex-direction: column; gap: 12px; filter: grayscale(1); opacity: 0.42; }
.fleet-cell--locked .fleet-cell__icon { background: var(--gray-100); color: var(--text-secondary); }
.lock-badge {
  position: absolute;
  right: -4px;
  bottom: -4px;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--text);
  color: var(--white);
  display: grid;
  place-items: center;
  box-shadow: 0 0 0 2px var(--white);
}
.locked-cta {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
  padding: 11px 13px;
  background: var(--white);
  border: 1px dashed #E7B9B6;
  border-radius: var(--radius-sm);
}
.locked-cta__txt { font-size: 12px; color: var(--text-secondary); line-height: 1.4; }
.locked-cta__txt b { color: var(--text); font-weight: 600; }
.locked-cta__btn {
  margin-left: auto;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--white);
  background: var(--brand-red);
  padding: 7px 13px;
  border-radius: var(--radius-full);
  text-decoration: none;
  box-shadow: 0 6px 16px -8px rgba(237, 28, 36, 0.6);
}
.locked-cta__btn:hover { background: var(--brand-red-dark); }

/* ---- Status pill variants (semantic aliases over the base .pill) ---- */
.pill--ok   { background: var(--green-soft); color: var(--green); }
.pill--ok .dot   { background: var(--green); }
.pill--info { background: #E6ECF6; color: #2A5DB0; }
.pill--info .dot { background: #2A5DB0; }
.pill--warn { background: #F6EEDA; color: #CA8A04; }
.pill--warn .dot { background: #CA8A04; }
.pill--bad  { background: #F6E4E1; color: #A8362E; }
.pill--bad .dot  { background: #A8362E; }
.pill--mute { background: var(--gray-200); color: var(--text-secondary); }
.pill--mute .dot { background: var(--text-secondary); }

/* ===== Campaign report media table — thumbnails =====
   Used by app/views/campaigns/report.html.erb (.report-media__preview-col
   on the first th/td) and by app/helpers/campaigns_helper.rb's
   medium_report_thumbnail helper. The dash fallback reuses
   .media-placeholder + .media-placeholder--sm for visual consistency
   with the rest of the app. */
.report-media__preview-col {
  width: 72px;
  padding-right: 8px;
}
.report-media__thumb {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: 4px;
  display: block;
  background: var(--gray-100);
}


/* ============================================================
   KASA — redesigned customer receipt screen (design-lab preview)
   Reachable at /devices/:identifier/receipt_design. Fully namespaced
   under `.kasa` so it never collides with the live `.cashregister`
   layout or other app styles. Themed entirely via CSS variables that
   kasa_controller.js writes from the device's ReceiptTheme tokens.
   ============================================================ */
.kasa {
  /* font stacks: kiosks fall back to installed fonts; the lab loads
     web fonts (Fraunces / Bricolage Grotesque / Spline Sans Mono). */
  --k-display:"Fraunces", Cambria, Georgia, serif;
  --k-ui:"Bricolage Grotesque", system-ui, sans-serif;
  --k-mono:"Spline Sans Mono","JetBrains Mono", Consolas, monospace;
  --k-ease:cubic-bezier(0.16,0.84,0.32,1);
  /* token fallbacks (Lewiatan) — JS overrides per theme */
  --brand:#1f62b0; --brand-deep:#0e306c; --brand-bright:#2e7bd6; --brand-glow:#7fb2ee;
  --accent:#ffd200; --accent-light:#ffec4d; --accent-deep:#d6a400;
  --panel-a:#1f62b0; --panel-b:#123a82; --panel-c:#0e2a60; --panel-d:#08193c;
  --panel-glow:rgba(46,123,214,0.26);
  --ink:#14213d; --ink-soft:#5b6b86; --ink-faint:#9aa8be;
  --paper:#fbfbf6; --paper-warm:#eef2f8; --paper-edge:#e1e8f2; --card:#ffffff;
  --rule:rgba(20,33,61,0.10); --rule-strong:rgba(20,33,61,0.26);
  --brick:#d6402b; --coin-ink:#6b5400;
  /* warm "what you're losing" amber for the karta layout's regret window */
  --loss:#ff8a5b; --loss-deep:#e0542a;

  /* Geometry knobs, written per device by kasa_live_controller#applyLayout (and
     by the configurator's live preview). Defaults reproduce the pre-scale
     rendering exactly, so a device that never opts in is byte-identical.
       --k-scale  how many device px one layout px paints as (see .kasa-scene)
       --k-type   type multiplier inside the distance mode only
     The receipt/media split is deliberately NOT a knob here: `split_ratio` means
     the TV side (every other consumer reads it as `100 - split_ratio`), the
     redesign has always drawn a fixed 46%, and `.kasa-receipt`'s max-width would
     make most of a slider's travel inert anyway. */
  --k-scale:1; --k-type:1;

  position:relative; width:100%; height:100%;
  overflow:hidden; font-family:var(--k-ui);
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}

/* ---- scale (the whole scene, one number) ----
   A 13" panel pushed back from the till renders the scene's smallest critical
   text at ~10' from a metre — well under the 16' ISO 9241-303 floor — because
   the kiosk runs at --force-device-scale-factor=1, so one layout px is one
   physical px on a dense little screen. Rather than restate ~184 hardcoded
   font sizes across the density tiers, lay the scene out on a SMALLER canvas
   and paint it larger: at --k-scale:1.5 a 1920x1080 panel composes as 1280x720
   and everything — type, icons, wallet, gaps, the media stage — grows together.

   `transform` rather than `zoom`: zoom's resolution of percentages and viewport
   units still varies across the Chromium builds our kiosks run (SR-Ware Iron on
   Win7 machines is old), while a transform plus the inverse sizing below is
   arithmetic. The element is deliberately a WRAPPER and not `.kasa` itself:
   a transformed element becomes the containing block for `position:fixed`
   descendants, and the design lab's dock is fixed — it stays a child of `.kasa`,
   outside this wrapper, so it keeps Stimulus scope without being scaled.

   Every geometry decision downstream (density tiers, fit-driven line counts)
   must therefore reason about the LOGICAL viewport, not window.innerWidth —
   see Device#logical_screen_width and kasa_live_controller#applyDensity. */
.kasa-scene {
  display:flex; width:calc(100% / var(--k-scale)); height:calc(100% / var(--k-scale));
  transform:scale(var(--k-scale)); transform-origin:0 0;
  /* RULE FOR EVERYTHING INSIDE THIS ELEMENT: use var(--k-vh) / var(--k-vw),
     never bare vh/vw. A transform is a paint-time effect, so viewport units keep
     resolving against the REAL viewport and are then multiplied again by the
     scale — at 150% a `54vh` cap claims 81%% of the 720px canvas it was meant to
     take 54%% of, and squeezes the media stage to nothing. Declaring the
     corrected units here means a future rule inherits the fix instead of having
     to remember the mechanism. */
  --k-vh: calc(1vh / var(--k-scale));
  --k-vw: calc(1vw / var(--k-scale));
}

/* ---- stage (left: media + communication) ---- */
.kasa-stage {
  position:relative; flex:1 1 auto; min-width:0;
  display:flex; flex-direction:column;
  background:
    radial-gradient(120% 90% at 18% 12%, var(--panel-a) 0%, transparent 55%),
    radial-gradient(130% 100% at 86% 92%, var(--panel-b) 0%, transparent 60%),
    linear-gradient(160deg, var(--panel-c) 0%, var(--panel-d) 100%);
  overflow:hidden; transition:flex-basis 480ms var(--k-ease);
}
.kasa-stage::before { content:""; position:absolute; inset:-20%;
  background-image:radial-gradient(rgba(255,255,255,0.05) 1px, transparent 1.4px);
  background-size:26px 26px; animation:kasa-drift 60s linear infinite; pointer-events:none; }
@keyframes kasa-drift { to { transform:translate(26px,26px);} }
/* The idle stage is intentionally brand-free — fullscreen shows ONLY the
   campaign image/video (no overlaid badge). */

.kasa-mediaframe { flex:1; display:grid; place-items:center; padding:0; min-height:0; transition:padding 480ms var(--k-ease); }
.kasa-media { position:relative; overflow:hidden; width:100%; height:100%; border-radius:0; box-shadow:none;
  transition:border-radius 480ms var(--k-ease), box-shadow 480ms var(--k-ease); }
.kasa-media__art { position:absolute; inset:0;
  background:
    radial-gradient(58% 78% at 72% 22%, var(--accent-light) 0%, transparent 58%),
    radial-gradient(80% 95% at 22% 88%, var(--brand-bright) 0%, transparent 62%),
    linear-gradient(135deg, var(--brand) 0%, var(--brand-deep) 100%);
  animation:kasa-kenburns 18s ease-in-out infinite alternate; }
@keyframes kasa-kenburns { from { transform:scale(1) translate(0,0);} to { transform:scale(1.12) translate(-2%,1%);} }
/* Live kiosk only: the kasa scene is a direct child of <body> in the
   cashregister layout, whereas the configurator nests it inside .rcfg__canvas.
   So `body > .kasa` targets the kiosk alone — hide the cursor there (matches the
   legacy .cashregister) without hiding it in the operator preview. */
body > .kasa { cursor:none; }
/* Live kiosk only: stop the two full-stage infinite animations. The ken-burns
   __art layer sits behind opaque campaign media (never seen) and the drifting
   grain is imperceptible — but both keep the compositor busy 24/7 on weak kiosk
   GPUs. The small loyalty/pay flourishes stay; the admin lab keeps full motion. */
body > .kasa .kasa-media__art { animation:none; }
body > .kasa .kasa-stage::before { animation:none; }
/* Live kiosk only: the real campaign media (player_controller targets) fills
   .kasa-media on top of the animated __art backdrop. `contain` letterboxes
   onto the gradient so portrait/odd-ratio media still looks intentional. The
   lab keeps its static __art/__copy/__tag ad and renders none of these. */
.kasa-media__el { position:absolute; inset:0; width:100%; height:100%; object-fit:contain; z-index:1; }
/* No `display:flex` here: player_controller toggles inline display:block/none on
   this target, which would win over a flex rule. Centre via absolute positioning
   instead so it holds whatever display the controller sets. */
.kasa-media__nocontent { position:absolute; left:0; right:0; top:50%; transform:translateY(-50%); z-index:2; text-align:center;
  color:rgba(255,255,255,0.82); font-family:var(--k-ui); font-size:1.4rem; letter-spacing:0.02em; }
.kasa-media__offline { position:absolute; top:14px; right:14px; z-index:3; width:12px; height:12px; border-radius:50%;
  background:#ef4444; box-shadow:0 0 0 5px rgba(239,68,68,0.18); }
/* Lab-only demo photo filling the media frame (cover) behind the ad copy/tag.
   The live kiosk renders player img/video (.kasa-media__el) here instead. */
.kasa-media__photo { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:0; }
.kasa-media__copy { position:absolute; left:0; right:0; bottom:0; padding:46px; color:#fff; text-align:left; z-index:2; }
/* Scrim under the headline so white copy stays legible over the photo. */
.kasa-media__copy::before { content:""; position:absolute; inset:-60px 0 0 0; z-index:-1;
  background:linear-gradient(to top, rgba(6,14,28,0.88) 8%, rgba(6,14,28,0.45) 50%, transparent); }
.kasa-media__copy .kicker { text-transform:uppercase; letter-spacing:0.32em; font-size:13px; opacity:0.8; margin-bottom:12px; }
.kasa-media__copy h2 { font-family:var(--k-display); font-weight:300; font-size:clamp(34px,calc(4 * var(--k-vw)),62px); line-height:0.98; letter-spacing:-0.02em; }
.kasa-media__copy h2 em { font-style:italic; font-weight:500; color:var(--accent-light); }
.kasa-media__tag { position:absolute; top:26px; right:26px; font-family:var(--k-display); font-weight:700; font-size:30px; color:#14213d;
  background:var(--accent-light); padding:8px 16px; border-radius:14px; transform:rotate(4deg); box-shadow:0 10px 24px -8px rgba(0,0,0,0.5); }

.kasa-comm { flex-shrink:0; max-height:0; opacity:0; overflow:hidden;
  transition:max-height 480ms var(--k-ease), opacity 360ms var(--k-ease), padding 480ms var(--k-ease); }
.kasa-comm--top, .kasa-comm--bottom { padding:0 34px; }
/* Promo zone holds a STACK of promo cards (one per active promotion action).
   Scrolls if a basket triggers more than fit; on a kiosk the realistic count
   (a few) fits and the media frame above flexes smaller to make room. The cap
   is viewport-relative, NOT max-height:100% — a percentage max-height doesn't
   resolve against the content-driven (indefinite-height) comm parent, so the
   scroll would never bound and overflowing cards would clip silently.
   Divided by --k-scale because the scene is laid out on viewport/scale: a bare
   54vh still resolves against the REAL viewport, so at 150% it would claim 81%
   of the logical canvas and squeeze the media frame to nothing. */
.kasa-promos { display:flex; flex-direction:column; gap:10px; max-height:calc(54 * var(--k-vh)); overflow-y:auto; scrollbar-width:none; }
.kasa-promos::-webkit-scrollbar { width:0; height:0; }

.kasa-loyalty { display:flex; align-items:center; gap:20px; padding:18px 22px; border-radius:20px;
  background:linear-gradient(180deg, rgba(255,255,255,0.10), rgba(255,255,255,0.04)); border:1px solid rgba(255,255,255,0.14); color:#eaf6ee; }
.kasa-loyalty__card { position:relative; width:96px; height:62px; flex-shrink:0; border-radius:11px; overflow:hidden;
  background:linear-gradient(135deg, var(--accent) 0%, var(--accent-deep) 100%); box-shadow:0 10px 22px -8px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.3); }
.kasa-loyalty__card::before { content:""; position:absolute; left:10px; top:14px; width:22px; height:16px; border-radius:3px; background:rgba(255,255,255,0.45); }
.kasa-loyalty__beam { position:absolute; inset:0; background:linear-gradient(100deg, transparent 40%, rgba(255,255,255,0.55) 50%, transparent 60%); transform:translateX(-120%); animation:kasa-beam 2.6s ease-in-out infinite; }
@keyframes kasa-beam { 0%,60% { transform:translateX(-120%);} 85%,100% { transform:translateX(120%);} }
.kasa-loyalty__body { flex:1; }
.kasa-loyalty__title { font-family:var(--k-display); font-style:italic; font-weight:500; font-size:24px; line-height:1.05; }
.kasa-loyalty__sub { font-size:15px; color:rgba(234,246,238,0.7); margin-top:3px; }
.kasa-loyalty__arrow { font-size:30px; color:var(--accent-light); animation:kasa-nudge 1.4s ease-in-out infinite; }
@keyframes kasa-nudge { 0%,100% { transform:translateX(0);} 50% { transform:translateX(6px);} }
.kasa-loyalty[data-state="welcome"] .kasa-loyalty__beam, .kasa-loyalty[data-state="welcome"] .kasa-loyalty__arrow { display:none; }

/* Compact card — sized so several stack in the promo zone without overflowing. */
.kasa-promo { display:flex; align-items:center; gap:15px; padding:13px 17px; border-radius:16px; flex-shrink:0;
  background:linear-gradient(180deg, rgba(246,206,94,0.18), rgba(246,206,94,0.07)); border:1px solid rgba(246,206,94,0.34); color:#fdf6e5; position:relative; overflow:hidden; }
.kasa-promo__icon { width:44px; height:44px; flex-shrink:0; display:grid; place-items:center; border-radius:13px;
  background:linear-gradient(160deg, var(--accent) 0%, var(--accent-deep) 100%); font-size:23px; box-shadow:0 8px 18px -10px rgba(0,0,0,0.6); }
.kasa-promo__body { flex:1; min-width:0; }
.kasa-promo__eyebrow { text-transform:uppercase; letter-spacing:0.16em; font-size:11px; color:var(--accent-light); margin-bottom:3px; }
.kasa-promo__title { font-family:var(--k-display); font-weight:500; font-size:18px; line-height:1.15; letter-spacing:-0.01em; }
.kasa-promo__title b { color:var(--accent-light); font-weight:700; }
.kasa-promo__note { font-size:12px; color:rgba(253,246,229,0.62); margin-top:4px; }
.kasa-meter { margin-top:8px; height:10px; border-radius:999px; background:rgba(0,0,0,0.28); overflow:hidden; }
.kasa-meter__fill { height:100%; border-radius:999px; width:0%; background:linear-gradient(90deg, var(--accent), var(--accent-light)); box-shadow:0 0 12px color-mix(in srgb, var(--accent) 60%, transparent); transition:width 640ms var(--k-ease); }
.kasa-meter__row { display:flex; justify-content:space-between; align-items:baseline; margin-top:5px; font-size:12px; color:rgba(253,246,229,0.75); }
.kasa-meter__row b { font-family:var(--k-mono); font-weight:700; color:#fff; }
/* Compact promo strip (multipack / weighted): product as the title, a single
   subrow (call-to-action / per-kg price + dots), and the savings pinned right as
   a price tag — fills the space the left-aligned content used to waste, ~⅓ shorter. */
.kasa-promo__icon .kasa-plus { width:24px; height:24px; }
.kasa-promo__icon .kasa-plus path { stroke:var(--brand-deep); stroke-width:2.6; stroke-linecap:round; fill:none; }
.kasa-promo--strip { gap:14px; padding:11px 15px 11px 14px; }
.kasa-promo--strip .kasa-promo__icon { width:42px; height:42px; }
.kasa-promo--strip .kasa-promo__title { font-size:16px; }
.kasa-promo__subrow { display:flex; align-items:center; gap:11px; margin-top:6px; flex-wrap:wrap; }
.kasa-promo__cta { font-size:12px; color:rgba(253,246,229,0.82); }
.kasa-promo__subrow .kasa-dots { margin-top:0; }
.kasa-promo__reward { flex-shrink:0; align-self:stretch; display:flex; flex-direction:column; align-items:flex-end; justify-content:center;
  position:relative; padding-left:15px; }
.kasa-promo__reward::before { content:""; position:absolute; left:0; top:14%; bottom:14%; width:1px;
  background:linear-gradient(180deg, transparent, rgba(255,255,255,0.22), transparent); }
.kasa-promo__reward-lead { text-transform:uppercase; letter-spacing:0.12em; font-size:10px; color:rgba(253,246,229,0.6); margin-bottom:2px; white-space:nowrap; }
.kasa-promo__reward-amt { font-family:var(--k-mono); font-weight:700; font-size:24px; line-height:0.95; letter-spacing:-0.02em; color:var(--accent-light); white-space:nowrap; }
.kasa-promo[data-done="true"] .kasa-promo__reward-lead { color:rgba(234,241,251,0.7); }
.kasa-promo[data-done="true"] .kasa-promo__reward-amt { color:var(--brand-glow); }
.kasa-dots { display:flex; gap:7px; margin-top:8px; }
.kasa-dots i { width:13px; height:13px; border-radius:50%; background:rgba(0,0,0,0.3); border:1.5px solid color-mix(in srgb, var(--accent-light) 50%, transparent); }
.kasa-dots i.on { background:var(--accent-light); border-color:var(--accent-light); box-shadow:0 0 10px color-mix(in srgb, var(--accent-light) 70%, transparent); }
.kasa-promo[data-done="true"] { background:linear-gradient(180deg, color-mix(in srgb, var(--brand-bright) 24%, transparent), color-mix(in srgb, var(--brand-bright) 10%, transparent)); border-color:color-mix(in srgb, var(--brand-glow) 55%, transparent); }
.kasa-promo[data-done="true"] .kasa-promo__icon { background:linear-gradient(160deg, var(--brand-bright), var(--brand)); }
.kasa-promo[data-done="true"] .kasa-promo__eyebrow, .kasa-promo[data-done="true"] .kasa-promo__title b { color:var(--brand-glow); }
.kasa-promo[data-done="true"] .kasa-meter__fill { background:linear-gradient(90deg, var(--brand-bright), var(--brand-glow)); }

/* ---- loyalty-card-gated promos ----
   A promo (or a card-only price) available ONLY with the customer's loyalty
   card. The "card flag" is a cobalt pill carrying a yellow mini-card glyph + the
   brand; card-gated cards also get an accent rail on the left edge. */
.kasa-cardflag { display:inline-flex; align-items:center; gap:7px; padding:3px 10px 3px 6px; border-radius:999px;
  background:linear-gradient(135deg, var(--brand-bright), var(--brand-deep)); color:#fff;
  font-family:var(--k-display); font-style:italic; font-weight:600; font-size:12.5px; letter-spacing:-0.01em; white-space:nowrap;
  box-shadow:0 4px 11px -5px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.22); }
.kasa-cardflag svg { width:23px; height:16px; flex-shrink:0; }
.kasa-cardflag .body { fill:var(--accent); } .kasa-cardflag .chip { fill:var(--brand-deep); } .kasa-cardflag .stripe { fill:var(--accent-deep); }
.kasa-promo__flagrow { margin-bottom:6px; }

/* variant (a): existing promo cards, gated — accent rail + the flag chip */
.kasa-promo--card { border-color:color-mix(in srgb, var(--accent) 50%, transparent); padding-left:21px;
  background:linear-gradient(180deg, rgba(246,206,94,0.22), rgba(246,206,94,0.08)); }
.kasa-promo--card::before, .kasa-promo--price::before { content:""; position:absolute; left:0; top:0; bottom:0; width:5px;
  background:linear-gradient(180deg, var(--accent-light), var(--accent-deep)); }

/* variant (b): card-only price — regular price struck → special card price */
.kasa-promo--price { align-items:stretch; padding:15px 18px 15px 21px;
  background:linear-gradient(165deg, color-mix(in srgb, var(--brand-bright) 30%, transparent), color-mix(in srgb, var(--brand-deep) 16%, transparent));
  border-color:color-mix(in srgb, var(--brand-glow) 45%, transparent); }
.kasa-promo--price .kasa-promo__icon { align-self:flex-start; background:linear-gradient(160deg, var(--brand-bright), var(--brand-deep)); color:#fff; }
.kasa-price { display:flex; align-items:center; gap:14px; margin-top:9px; flex-wrap:wrap; }
.kasa-price__was { font-family:var(--k-mono); font-size:18px; color:rgba(253,246,229,0.55); text-decoration:line-through; text-decoration-color:rgba(253,246,229,0.4); }
.kasa-price__arrow { color:var(--accent-light); font-size:18px; }
.kasa-price__now { display:inline-flex; align-items:baseline; gap:5px; padding:5px 14px; border-radius:13px;
  background:linear-gradient(135deg, var(--accent-light), var(--accent)); color:var(--brand-deep);
  box-shadow:0 8px 20px -8px color-mix(in srgb, var(--accent) 55%, transparent), inset 0 1px 0 rgba(255,255,255,0.5); }
.kasa-price__now b { font-family:var(--k-mono); font-weight:700; font-size:30px; line-height:1; letter-spacing:-0.02em; }
.kasa-price__now span { font-family:var(--k-mono); font-weight:700; font-size:15px; }
.kasa-price__save { font-size:12px; color:var(--accent-light); font-weight:600; align-self:center; }
.kasa-promo__note .scan { color:var(--accent-light); }

/* scene mode: idle vs serving.
   idle    → media fills the stage edge-to-edge (fullscreen, set on the base
             .kasa-media rule: full width/height, no radius/shadow).
   serving → the same media animates into a centred window on the left stage,
             framed to match the receipt panel's rounded cards (22px radius,
             themed accent hairline + deep shadow) so the two sides read as one
             cohesive scene. The mediaframe centres it (place-items:center);
             generous padding floats it clear of the loyalty/promo comm zones. */
.kasa[data-mode="serving"] .kasa-mediaframe { padding:30px 44px; }
/* The media fills the padded frame (keeps the base 100%×100% — do NOT switch to
   width/height:auto: the media's children are absolutely positioned, so an
   auto-sized box with aspect-ratio collapses to 0 and the window vanishes). The
   frame padding + the loyalty/promo zones above & below centre it on the stage;
   a light inner hairline + dark outer ring + deep shadow make it read as a
   deliberate framed screen on any media. */
.kasa[data-mode="serving"] .kasa-media {
  border-radius:22px;
  border:1px solid rgba(255,255,255,0.22);
  box-shadow:0 40px 80px -26px rgba(0,0,0,0.85), 0 0 0 1px rgba(0,0,0,0.28), inset 0 1px 0 rgba(255,255,255,0.20); }
.kasa[data-mode="serving"] .kasa-comm { max-height:240px; opacity:1; }
.kasa[data-mode="serving"] .kasa-comm--top { padding-top:80px; }
/* The promo zone may carry several cards — let it grow (up to ~half the stage,
   then scroll) so all active promotions show. It only takes the height its
   cards need, so a single promo keeps the media large; more promos shrink the
   media frame (flex:1 above) to fit. */
.kasa[data-mode="serving"] .kasa-comm--bottom { max-height:calc(54 * var(--k-vh)); padding-top:6px; padding-bottom:22px; }

/* ---- receipt (right) ---- */
.kasa-receipt { position:relative; flex:0 0 0; max-width:720px; width:46%;
  display:flex; flex-direction:column;
  background:radial-gradient(rgba(32,32,26,0.030) 1px, transparent 1.5px), linear-gradient(180deg, var(--paper) 0%, var(--paper-warm) 100%);
  background-size:8px 8px, 100% 100%; color:var(--ink); box-shadow:-40px 0 80px -30px rgba(0,0,0,0.55);
  overflow:hidden; flex-basis:0; opacity:0; transition:flex-basis 480ms var(--k-ease), opacity 360ms var(--k-ease); }
.kasa[data-mode="serving"] .kasa-receipt { flex-basis:46%; opacity:1; }

.kasa-cart { flex-shrink:0; display:flex; align-items:center; gap:18px; padding:30px 38px 22px; border-bottom:1.5px solid var(--rule); position:relative; }
.kasa-cart__icon { position:relative; width:56px; height:56px; flex-shrink:0; display:grid; place-items:center; border-radius:16px; background:linear-gradient(160deg, var(--brand) 0%, var(--brand-deep) 100%); color:#fff; box-shadow:0 10px 22px -10px color-mix(in srgb, var(--brand) 70%, transparent), inset 0 1px 0 rgba(255,255,255,0.25); }
.kasa-cart__icon svg { width:30px; height:30px; }
.kasa-cart__count { display:flex; align-items:baseline; gap:10px; }
.kasa-cart__count b { font-family:var(--k-display); font-weight:600; font-size:46px; line-height:1; letter-spacing:-0.02em; font-variant-numeric:tabular-nums; }
.kasa-cart__count span { font-size:17px; color:var(--ink-soft); }
.kasa-cart__spacer { flex:1; }
.kasa-cart__loyalty { display:flex; flex-direction:column; align-items:flex-end; gap:6px; font-size:12px; color:var(--ink-faint); text-transform:uppercase; letter-spacing:0.18em; }
.kasa-cart__loyalty b { font-family:var(--k-display); font-size:18px; color:var(--brand); letter-spacing:0; text-transform:none; }
.kasa-cart__loyalty-caption { font-size:11px; color:var(--ink-faint); text-transform:uppercase; letter-spacing:0.18em; }

/* ---- brand logo (receipt header + summary card) ----
   Two render paths from devices/_kasa_logo:
   • --brand: the official Lewiatan logo (current brand mark) as a contained
     background image from --kasa-lewiatan-logo, sized by height with the
     artwork's intrinsic aspect ratio (450×146 ≈ 3.08:1). Transparent PNG, so
     drop-shadow follows the swoosh silhouette. Authentic colours — not tinted.
   • --word: themed text wordmark (leaf mark + brand name) for other themes —
     tinted entirely from theme tokens. */
.kasa-logo { display:inline-flex; align-items:center; gap:9px; line-height:1; }
.kasa-logo__mark { flex-shrink:0; overflow:visible; }
.kasa-logo__leaf { fill:var(--accent); }
.kasa-logo__vein { stroke:var(--brand-deep); stroke-width:2.2; stroke-linecap:round; }
.kasa-logo__word { font-family:var(--k-display); font-style:italic; font-weight:600; letter-spacing:-0.01em; color:var(--brand); white-space:nowrap; text-transform:none; }
.kasa-logo__art { display:block; flex-shrink:0; width:auto; aspect-ratio:450 / 146;
  background:var(--kasa-lewiatan-logo) center/contain no-repeat; }
.kasa-logo--receipt .kasa-logo__mark { width:25px; height:25px; }
.kasa-logo--receipt .kasa-logo__word { font-size:21px; }
.kasa-logo--receipt .kasa-logo__art { height:40px; }
.kasa-logo--summary { align-self:center; gap:13px; flex-shrink:0; animation:kasa-rise 460ms var(--k-ease) 60ms both; }
.kasa-logo--summary .kasa-logo__mark { width:42px; height:42px; }
.kasa-logo--summary .kasa-logo__word { font-size:clamp(28px,calc(4.5 * var(--k-vw)),38px); }
.kasa-logo--summary .kasa-logo__art { height:clamp(40px,calc(6 * var(--k-vh)),56px); filter:drop-shadow(0 8px 16px rgba(0,0,0,0.28)); }
.kasa-tick { animation:kasa-tick 420ms var(--k-ease); }
@keyframes kasa-tick { 0% { transform:scale(1);} 35% { transform:scale(1.18); color:var(--brand);} 100% { transform:scale(1);} }

.kasa-lines { list-style:none; margin:0; flex:1; overflow-y:auto; padding:14px 30px 8px; scrollbar-width:none; }
.kasa-lines::-webkit-scrollbar { width:0; height:0; }
.kasa-line { padding:18px 0 16px; border-bottom:1px solid var(--rule); animation:kasa-line-in 520ms var(--k-ease) both; }
.kasa-line:last-child { border-bottom:0; }
@keyframes kasa-line-in { 0% { opacity:0; transform:translateY(16px) scale(0.985);} 60% { opacity:1;} 100% { opacity:1; transform:none;} }
.kasa-line.fresh { position:relative; }
.kasa-line.fresh::after { content:""; position:absolute; inset:0 -30px; background:linear-gradient(90deg, transparent, color-mix(in srgb, var(--brand-bright) 16%, transparent), transparent); transform:translateX(-100%); animation:kasa-sweep 900ms var(--k-ease) 120ms; pointer-events:none; }
@keyframes kasa-sweep { to { transform:translateX(100%);} }
.kasa-line__head { display:grid; grid-template-columns:30px 1fr auto; align-items:baseline; gap:14px; }
.kasa-line__nr { font-family:var(--k-mono); font-size:14px; color:var(--ink-faint); font-variant-numeric:tabular-nums; }
.kasa-line__name { font-weight:600; font-size:20px; letter-spacing:-0.01em; color:var(--ink); display:flex; align-items:center; gap:10px; min-width:0; }
.kasa-line__name .txt { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.kasa-line__badge { flex-shrink:0; display:inline-flex; align-items:center; gap:5px; padding:3px 10px 3px 8px; border-radius:999px; background:linear-gradient(180deg, var(--accent-light), var(--accent)); color:var(--brand-deep); font-size:12px; font-weight:700; letter-spacing:0.03em; text-transform:uppercase; box-shadow:0 4px 10px -4px color-mix(in srgb, var(--accent) 70%, transparent); }
.kasa-line__badge::before { content:"★"; font-size:11px; }
.kasa-line__total { font-family:var(--k-mono); font-weight:600; font-size:20px; font-variant-numeric:tabular-nums; white-space:nowrap; color:var(--ink); }
.kasa-line__total.struck { text-decoration:line-through; text-decoration-color:var(--ink-faint); color:var(--ink-faint); font-weight:500; font-size:17px; }
.kasa-line__sub { margin:9px 0 0 44px; display:flex; flex-direction:column; gap:5px; }
.kasa-line__row { display:grid; grid-template-columns:1fr auto; align-items:baseline; font-size:15px; color:var(--ink-soft); }
.kasa-line__row .v { font-family:var(--k-mono); font-variant-numeric:tabular-nums; }
.kasa-line__row.discount, .kasa-line__row.discount .v { color:var(--brick); font-weight:600; }
.kasa-line__row.final { color:var(--ink); font-weight:700; font-size:17px; padding-top:4px; }
.kasa-line__row.final .v { font-size:18px; }

.kasa-foot { flex-shrink:0; border-top:1.5px dashed var(--rule-strong); background:linear-gradient(180deg, rgba(255,255,255,0.5), color-mix(in srgb, var(--paper-warm) 70%, transparent)); padding:18px 38px 26px; display:none; flex-direction:column; gap:14px; }
.kasa-foot[data-visible="true"] { display:flex; }
.kasa-foot__pre { display:grid; grid-template-columns:1fr auto; align-items:baseline; font-size:17px; color:var(--ink-soft); }
.kasa-foot__pre[data-visible="false"] { display:none; }
.kasa-foot__pre .v { font-family:var(--k-mono); font-variant-numeric:tabular-nums; font-size:19px; }

.kasa-savings { display:none; align-items:center; gap:18px; padding:16px 18px; border-radius:18px;
  background:radial-gradient(120% 160% at 12% 0%, color-mix(in srgb, var(--accent-light) 22%, transparent), transparent 60%), linear-gradient(180deg, color-mix(in srgb, var(--brand) 10%, transparent), color-mix(in srgb, var(--brand) 5%, transparent));
  border:1px solid color-mix(in srgb, var(--brand) 22%, transparent); position:relative; overflow:hidden; }
.kasa-savings[data-visible="true"] { display:flex; animation:kasa-pop-in 520ms var(--k-ease); }
@keyframes kasa-pop-in { from { opacity:0; transform:translateY(10px) scale(0.97);} to { opacity:1; transform:none;} }
.kasa-wallet { position:relative; width:76px; height:70px; flex-shrink:0; }
.kasa-wallet svg { position:absolute; inset:0; width:100%; height:100%; overflow:visible; }
/* Wallet-glyph fills shared by .kasa-wallet and .kasa-card__savings .big-wallet
   (both render the _kasa_wallet partial). Scoped at .kasa so one rule retints
   every wallet on screen. */
.kasa .w-back   { fill:var(--brand-deep); }
.kasa .w-flap   { fill:var(--brand); }
.kasa .w-pocket { fill:color-mix(in srgb, var(--brand-deep) 76%, #000); }
.kasa .w-coin   { fill:var(--accent); }
.kasa .w-coin-in{ fill:var(--accent-deep); }
.kasa-wallet.bump { animation:kasa-wallet-bump 360ms var(--k-ease); }
@keyframes kasa-wallet-bump { 0% { transform:translateY(0);} 30% { transform:translateY(4px) scale(0.97,1.03);} 100% { transform:translateY(0);} }
.kasa-coin { position:absolute; left:50%; top:-34px; width:26px; height:26px; margin-left:-13px; border-radius:50%; background:radial-gradient(circle at 35% 30%, var(--accent-light), var(--accent) 60%, var(--accent-deep)); box-shadow:inset 0 -2px 3px rgba(0,0,0,0.25), 0 3px 6px rgba(0,0,0,0.25); display:grid; place-items:center; font-family:var(--k-display); font-weight:700; font-size:12px; color:var(--coin-ink); z-index:3; animation:kasa-coin-drop 760ms cubic-bezier(0.5,0,0.7,1) forwards; }
.kasa-coin::after { content:"zł"; }
@keyframes kasa-coin-drop { 0% { opacity:0; transform:translateY(-12px) scale(0.6) rotate(-20deg);} 25% { opacity:1;} 70% { opacity:1; transform:translateY(26px) scale(1) rotate(8deg);} 100% { opacity:0; transform:translateY(34px) scale(0.7) rotate(14deg);} }
.kasa-savings__body { flex:1; min-width:0; }
.kasa-savings__label { font-family:var(--k-display); font-style:italic; font-weight:500; font-size:19px; color:var(--brand-deep); letter-spacing:-0.01em; }
.kasa-savings__amount { font-family:var(--k-mono); font-weight:700; font-variant-numeric:tabular-nums; font-size:34px; line-height:1.05; color:var(--brand); letter-spacing:-0.01em; }
.kasa-savings__amount .cur { font-size:0.55em; color:var(--brand-deep); margin-left:4px; }

.kasa-pay { display:grid; grid-template-columns:1fr auto auto; align-items:center; gap:16px; margin-top:2px; padding:20px 26px; border-radius:20px;
  background:linear-gradient(165deg, var(--brand-bright) 0%, var(--brand) 45%, var(--brand-deep) 100%); color:#fff; box-shadow:0 18px 38px -16px color-mix(in srgb, var(--brand) 75%, transparent), inset 0 1px 0 rgba(255,255,255,0.25); position:relative; overflow:hidden; }
.kasa-pay::before { content:""; position:absolute; inset:0; background:linear-gradient(105deg, transparent 35%, rgba(255,255,255,0.22) 50%, transparent 65%); transform:translateX(-120%); animation:kasa-shine 4.2s ease-in-out infinite; }
@keyframes kasa-shine { 0%,55% { transform:translateX(-120%);} 80%,100% { transform:translateX(120%);} }
.kasa-pay__label { font-size:17px; font-weight:600; opacity:0.92; }
.kasa-pay__amount { font-family:var(--k-mono); font-weight:700; font-variant-numeric:tabular-nums; font-size:40px; line-height:1; letter-spacing:-0.02em; }
.kasa-pay__amount .cur { font-size:0.5em; opacity:0.85; margin-left:4px; }
.kasa-pay__arrow { width:44px; height:44px; display:grid; place-items:center; border-radius:50%; background:rgba(255,255,255,0.18); }
.kasa-pay__arrow svg { width:22px; height:22px; }

/* ---- summary ---- */
.kasa-summary { position:absolute; inset:0; z-index:30; display:none; align-items:center; justify-content:center; padding:24px; background:radial-gradient(80% 70% at 50% 30%, var(--panel-glow), transparent 70%), linear-gradient(160deg, var(--panel-c) 0%, var(--panel-d) 100%); }
.kasa-summary[data-visible="true"] { display:flex; animation:kasa-fade 280ms ease-out; }
@keyframes kasa-fade { from { opacity:0;} to { opacity:1;} }
.kasa-confetti { position:absolute; inset:0; overflow:hidden; pointer-events:none; }
.kasa-confetti-piece { position:absolute; top:-20px; width:10px; height:14px; opacity:0; animation:kasa-confetti-fall linear forwards; }
@keyframes kasa-confetti-fall { 0% { opacity:0; transform:translateY(-20px) rotate(0);} 10% { opacity:1;} 100% { opacity:0.9; transform:translateY(calc(105 * var(--k-vh))) rotate(540deg);} }
/* margin:auto centres the card when it fits; when it's TALLER than the pane it
   top-pins (flex auto margins never overflow the start edge). kasa(_live)_controller.
   fitSummaryCard() then measures the natural height and sets the `scale` individual
   property so the WHOLE card — logo through the trailing welcome line — shrinks to
   fit with no clip. transform-origin:center top makes it shrink DOWN from that
   pinned top (a centre origin would scale about a point below the pane centre and
   push the bottom off-screen). `scale` is independent of the `transform` the
   kasa-card-in entry animation drives, so the two compose cleanly. */
.kasa-card { position:relative; margin:auto; transform-origin:center top; width:min(560px,94%); background:radial-gradient(rgba(32,32,26,0.035) 1px, transparent 1.5px), linear-gradient(180deg, var(--card), var(--paper-warm)); background-size:8px 8px, 100% 100%; border-radius:22px; padding:32px 48px 28px; text-align:center; box-shadow:0 40px 90px -30px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.8); display:flex; flex-direction:column; gap:16px; animation:kasa-card-in 520ms var(--k-ease) both; }
@keyframes kasa-card-in { from { opacity:0; transform:translateY(26px) scale(0.96);} to { opacity:1; transform:none;} }
.kasa-card::before { content:""; position:absolute; top:-9px; left:22px; right:22px; height:18px; background:radial-gradient(circle at 9px 9px, transparent 8px, var(--card) 8.5px) 0 0 / 18px 18px repeat-x; filter:drop-shadow(0 -3px 4px rgba(0,0,0,0.12)); }
.kasa-card__thanks { font-family:var(--k-display); font-style:italic; font-weight:500; font-size:clamp(40px,calc(6 * var(--k-vw)),58px); line-height:0.96; letter-spacing:-0.02em; color:var(--ink); animation:kasa-rise 460ms var(--k-ease) 120ms both; }
.kasa-card__sub { font-size:14px; text-transform:uppercase; letter-spacing:0.28em; color:var(--ink-faint); animation:kasa-rise 460ms var(--k-ease) 200ms both; }
.kasa-card__savings { display:none; flex-direction:column; align-items:center; gap:10px; margin:4px auto; padding:26px 28px 22px; border-radius:20px; background:radial-gradient(120% 150% at 50% 0%, color-mix(in srgb, var(--accent-light) 30%, transparent), transparent 65%), linear-gradient(180deg, color-mix(in srgb, var(--brand) 12%, transparent), color-mix(in srgb, var(--brand) 6%, transparent)); border:1px solid color-mix(in srgb, var(--brand) 28%, transparent); position:relative; overflow:hidden; animation:kasa-rise 500ms var(--k-ease) 320ms both; }
.kasa-card__savings[data-visible="true"] { display:flex; }
.kasa-card__savings .big-wallet { position:relative; width:96px; height:88px; margin-bottom:4px; }
.kasa-card__savings .big-wallet svg { width:100%; height:100%; overflow:visible; }
.kasa-card__savings-label { font-family:var(--k-display); font-style:italic; font-weight:500; font-size:22px; color:var(--brand-deep); }
.kasa-card__savings-amount { font-family:var(--k-mono); font-weight:700; font-variant-numeric:tabular-nums; font-size:clamp(46px,calc(8 * var(--k-vw)),64px); line-height:1; letter-spacing:-0.02em; color:var(--brand); }
.kasa-card__savings-amount .cur { font-size:0.45em; color:var(--brand-deep); margin-left:6px; }
.kasa-card__total-label { font-family:var(--k-display); font-variant:small-caps; letter-spacing:0.16em; font-size:15px; color:var(--ink-soft); }
.kasa-card__total { font-family:var(--k-mono); font-weight:700; font-variant-numeric:tabular-nums; font-size:clamp(54px,calc(10 * var(--k-vw)),80px); line-height:1; letter-spacing:-0.03em; color:var(--ink); animation:kasa-rise 500ms var(--k-ease) 420ms both; }
.kasa-card__total .cur { font-size:0.36em; color:var(--ink-soft); margin-left:6px; }
.kasa-card__rows { display:flex; flex-direction:column; gap:12px; padding-top:20px; border-top:1.5px dashed var(--rule-strong); margin:0; }
.kasa-card__row { display:grid; grid-template-columns:max-content 1fr max-content; align-items:baseline; animation:kasa-rise 420ms var(--k-ease) both; }
.kasa-card__row:nth-child(1) { animation-delay:520ms; } .kasa-card__row:nth-child(2) { animation-delay:580ms; } .kasa-card__row:nth-child(3) { animation-delay:640ms; }
.kasa-card__row dt { font-family:var(--k-display); font-variant:small-caps; letter-spacing:0.12em; font-size:16px; color:var(--ink-soft); white-space:nowrap; margin:0; }
.kasa-card__row::after { content:""; grid-column:2; align-self:center; height:2px; margin:0 14px; background-image:radial-gradient(var(--rule-strong) 1px, transparent 1.4px); background-size:8px 2px; background-repeat:repeat-x; transform:translateY(0.2em); }
.kasa-card__row dd { font-family:var(--k-mono); font-weight:600; font-variant-numeric:tabular-nums; font-size:19px; color:var(--ink); margin:0; justify-self:end; }
.kasa-card__welcome { padding-top:18px; border-top:1.5px dashed var(--rule-strong); font-family:var(--k-display); font-style:italic; font-size:22px; color:var(--ink-soft); animation:kasa-rise 460ms var(--k-ease) 720ms both; }
@keyframes kasa-rise { from { opacity:0; transform:translateY(12px);} to { opacity:1; transform:none;} }

/* ============================================================
   KARTA LAYOUT — "Lewiatan z Kartą" (theme lewiatan_karta)
   The left column splits into three rows: a loyalty-card window (top), the
   campaign ad (middle, flex), and the promo stack (bottom). The promo stack is
   content-sized and the ad (flex:1) absorbs the rest, shrinking as promos pile
   up — so every card stays full-size and on-screen. The window swaps
   regret↔reward and a rich↔compact body; kasa(_live)_controller fills it via
   lib/kasa_karta.js. Scoped to the new classes so the shared `.kasa` themes are
   untouched.
   ============================================================ */
.kasa-zone { position:relative; min-height:0; overflow:hidden;
  transition:padding 560ms var(--k-ease), opacity 360ms var(--k-ease); }
/* Horizontal padding is 40px (was 30/34) as a safe inset: on a customer panel
   whose Windows scaling overhangs the left edge, the extra gutter keeps the
   loyalty-card column off the bezel. Belt-and-suspenders to the launcher
   --force-device-scale-factor fix; harmless on a correctly-scaled screen. */
.kasa-zone--loyalty { flex:0 0 auto; padding:20px 40px 4px; display:flex; align-items:flex-start; }
.kasa-zone--media   { flex:1 1 auto; min-height:0; padding:0; display:grid; place-items:stretch; }
/* Content-driven: the promo zone takes the height its cards need (flex:0 1 auto so
   it can still shrink under pressure); the ad zone above is flex:1 and absorbs the
   rest, shrinking as the stack grows. The inner stack is capped to the zone and
   scrolls only as a last resort, so a tall stack never clips below the screen. */
.kasa-zone--promos  { flex:0 1 auto; padding:8px 40px 20px; }
/* height:0 guarantees a full collapse when there are no promos (the inline 0%
   height an older build set used to do this); the ad zone reclaims the space. */
.kasa-zone--promos[data-empty="true"] { height:0; padding-top:0; padding-bottom:0; opacity:0; }
/* max-height:100% DOES bound the scroll here (unlike the classic .kasa-promos at
   the top of this file, which must use 54vh): this zone is a flex item in a
   fixed-height, overflow:hidden flex column, so its resolved height is definite
   and the percentage resolves against it. Cards scroll only as a last resort. */
.kasa-zone--promos .kasa-promos { height:auto; max-height:100%; overflow-y:auto; }
/* Idle (no customer): only the fullscreen ad — collapse the card + promo rows. */
.kasa[data-mode="idle"] .kasa-zone--loyalty,
.kasa[data-mode="idle"] .kasa-zone--promos { display:none; }
/* Serving: float the ad into a framed window so the three rows read as one scene. */
.kasa[data-mode="serving"] .kasa-stage--karta .kasa-zone--media { padding:6px 40px 12px; }
.kasa[data-mode="serving"] .kasa-stage--karta .kasa-media { border-radius:18px;
  border:1px solid rgba(255,255,255,0.18); box-shadow:0 30px 60px -28px rgba(0,0,0,0.8); }

/* ---- the loyalty-card window ---- */
.kasa-loyalty--karta { display:block; width:100%; max-height:100%; align-self:stretch;
  gap:0; padding:16px 22px; border-radius:22px; overflow:hidden;
  box-shadow:0 22px 46px -24px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.12);
  color:#eaf1fb; animation:kasa-zone-in 520ms var(--k-ease) both; }
@keyframes kasa-zone-in { from { opacity:0; transform:translateY(-10px);} to { opacity:1; transform:none;} }
.kasa-loyalty--karta .kasa-loyalty__head { display:flex; align-items:center; gap:11px; }
.kasa-loyalty--karta .kasa-loyalty__eyebrow { margin-left:auto; text-transform:uppercase; letter-spacing:0.16em;
  font-size:10px; font-weight:600; padding:5px 11px; border-radius:999px;
  color:#ffe9d8; background:rgba(255,138,91,0.16); border:1px solid rgba(255,138,91,0.4); }
.kasa-loyalty--karta .kasa-loyalty__eyebrow.on { color:#0e2a60;
  background:linear-gradient(135deg,var(--accent-light),var(--accent)); border-color:rgba(255,255,255,0.3); box-shadow:0 4px 12px -4px var(--accent-deep); }
.kasa-logo--loyalty .kasa-logo__art { height:30px; }

.kasa-loyalty--karta .kasa-loyalty__grid { display:grid; grid-template-columns:auto 1px 1fr; gap:18px; align-items:start; margin-top:13px; }
.kasa-loyalty--karta .kasa-loyalty__rule { align-self:stretch; background:linear-gradient(180deg, transparent, rgba(255,255,255,0.18), transparent); }
.kasa-loyalty--karta .kasa-loyalty__title { font-family:var(--k-display); font-weight:500; font-size:22px; line-height:1.08; letter-spacing:-0.01em; color:#fff; font-style:normal; }
.kasa-loyalty--karta[data-state="regret"] .kasa-loyalty__title { font-style:italic; }
.kasa-loyalty--karta .kasa-loyalty__title b { color:var(--accent-light); font-weight:700; font-style:normal; }
.kasa-loyalty--karta .kasa-loyalty__sub { font-size:13.5px; color:rgba(234,241,251,0.72); margin-top:8px; line-height:1.35; }
.kasa-loyalty--karta .kasa-loyalty__sub b { color:#fff; }

/* card visual */
.kasa-cardvis { position:relative; width:138px; flex-shrink:0; display:flex; flex-direction:column; gap:10px; align-items:center; }
.kasa-cardvis__plastic { position:relative; width:138px; height:88px; border-radius:13px; overflow:hidden;
  background:linear-gradient(135deg, var(--brand-bright) 0%, var(--brand-deep) 100%);
  box-shadow:0 16px 30px -12px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.25); }
.kasa-cardvis__plastic[data-on="true"] { background:linear-gradient(135deg, var(--brand) 0%, var(--brand-deep) 100%);
  box-shadow:0 16px 34px -10px rgba(46,123,214,0.6), inset 0 1px 0 rgba(255,255,255,0.3), 0 0 0 2px color-mix(in srgb,var(--accent) 60%,transparent); }
.kasa-cardvis__chip { position:absolute; left:13px; top:32px; width:25px; height:18px; border-radius:4px;
  background:linear-gradient(135deg,var(--accent-light),var(--accent-deep)); box-shadow:inset 0 0 0 1px rgba(0,0,0,0.15); }
.kasa-cardvis__brand { position:absolute; left:13px; top:9px; font-family:var(--k-display); font-style:italic; font-weight:600; font-size:14px; color:#fff; }
.kasa-cardvis__beam { position:absolute; inset:0; background:linear-gradient(100deg, transparent 40%, rgba(255,255,255,0.5) 50%, transparent 60%);
  transform:translateX(-120%); animation:kasa-karta-beam 2.6s ease-in-out infinite; }
@keyframes kasa-karta-beam { 0%,60% { transform:translateX(-120%);} 85%,100% { transform:translateX(120%);} }
.kasa-cardvis__check { position:absolute; right:-7px; top:-7px; width:30px; height:30px; border-radius:50%;
  background:linear-gradient(135deg,var(--accent-light),var(--accent)); color:var(--brand-deep); display:grid; place-items:center; font-size:16px; font-weight:800;
  box-shadow:0 6px 14px -4px var(--accent-deep); animation:kasa-stamp 520ms var(--k-ease) 160ms both; z-index:3; }
@keyframes kasa-stamp { 0% { opacity:0; transform:scale(0.2) rotate(-30deg);} 60% { transform:scale(1.18);} 100% { opacity:1; transform:scale(1) rotate(0);} }
.kasa-scan { display:inline-flex; align-items:center; gap:8px; font-size:13px; font-weight:600; color:#eaf1fb; }
.kasa-scan__ok { color:var(--accent-light); }
.kasa-scan__arrow { font-size:17px; color:var(--accent-light); animation:kasa-karta-nudge 1.4s ease-in-out infinite; }
@keyframes kasa-karta-nudge { 0%,100% { transform:translateY(0);} 50% { transform:translateY(4px);} }

/* elki tokens */
.elki-row { display:flex; align-items:center; gap:9px; margin-top:13px; flex-wrap:wrap; }
.elki { position:relative; width:40px; height:40px; border-radius:50%; flex-shrink:0; display:grid; place-items:center;
  background:radial-gradient(circle at 36% 30%, var(--accent-light), var(--accent) 58%, var(--accent-deep));
  box-shadow:inset 0 -2px 3px rgba(0,0,0,0.22), 0 6px 14px -6px rgba(214,164,0,0.7); }
.elki svg { width:22px; height:22px; }
.elki .blade { fill:var(--brand-deep); } .elki .vein { stroke:var(--accent-light); stroke-width:2.4; fill:none; stroke-linecap:round; }
.elki--ghost { background:rgba(255,255,255,0.06); box-shadow:inset 0 0 0 1.5px rgba(255,210,0,0.4); }
.elki--ghost .blade { fill:rgba(255,210,0,0.5); } .elki--ghost .vein { stroke:rgba(255,210,0,0.5); }
.elki--ghost::after { content:""; position:absolute; left:50%; top:-7px; bottom:-7px; width:2.5px; margin-left:-1px;
  background:var(--loss, #ff8a5b); transform:rotate(34deg); border-radius:2px; box-shadow:0 0 8px var(--loss, #ff8a5b); }
.elki-count { display:inline-flex; align-items:baseline; gap:6px; margin-left:4px; }
.elki-count b { font-family:var(--k-mono); font-weight:700; font-size:32px; line-height:1; letter-spacing:-0.02em; font-variant-numeric:tabular-nums; }
.elki-count span { font-size:13.5px; }
.kasa-loyalty--karta[data-state="regret"] .elki-count b { color:var(--loss, #ff8a5b); }
.kasa-loyalty--karta[data-state="reward"] .elki-count b { color:var(--accent-light); text-shadow:0 0 22px color-mix(in srgb,var(--accent) 55%,transparent); }

/* regret ledger */
.kasa-ledger { margin-top:12px; display:flex; flex-direction:column; gap:8px; }
.kasa-ledger__row { display:grid; grid-template-columns:30px 1fr auto; align-items:center; gap:11px; padding:10px 13px; border-radius:13px;
  background:rgba(255,138,91,0.08); border:1px solid rgba(255,138,91,0.2); }
.kasa-ledger__ico { width:30px; height:30px; display:grid; place-items:center; border-radius:9px; font-size:15px; background:rgba(255,138,91,0.16); }
.kasa-ledger__ico--leaf svg { width:17px; height:17px; }
.kasa-ledger__ico--leaf .blade { fill:var(--loss, #ff8a5b); } .kasa-ledger__ico--leaf .vein { stroke:none; }
.kasa-ledger__txt { font-size:13px; color:rgba(255,233,216,0.92); line-height:1.25; }
.kasa-ledger__txt .muted { opacity:0.7; }
.kasa-ledger__amt { font-family:var(--k-mono); font-weight:700; font-size:17px; color:var(--loss, #ff8a5b); white-space:nowrap; font-variant-numeric:tabular-nums; }
.kasa-ledger__amt .u { font-size:0.7em; } .kasa-ledger__amt .cur { font-size:0.62em; opacity:0.8; margin-left:2px; }

/* reward progress */
.kasa-reward { margin-top:12px; padding:13px 15px; border-radius:15px;
  background:radial-gradient(120% 150% at 12% 0%, color-mix(in srgb,var(--accent-light) 16%,transparent), transparent 60%), linear-gradient(180deg, rgba(255,255,255,0.07), rgba(255,255,255,0.03));
  border:1px solid rgba(255,236,77,0.28); }
.kasa-reward__top { display:flex; align-items:baseline; justify-content:space-between; gap:12px; }
.kasa-reward__lead { font-size:13px; color:rgba(234,241,251,0.85); }
.kasa-reward__lead b { color:var(--accent-light); font-weight:700; }
.kasa-reward__val { font-family:var(--k-mono); font-size:11.5px; color:rgba(234,241,251,0.6); }
.kasa-loyalty--karta .kasa-meter { margin-top:9px; height:10px; border-radius:999px; background:rgba(0,0,0,0.3); overflow:hidden; }
.kasa-loyalty--karta .kasa-meter__fill { height:100%; border-radius:999px; background:linear-gradient(90deg, var(--accent), var(--accent-light));
  box-shadow:0 0 14px color-mix(in srgb,var(--accent) 60%,transparent); transition:width 640ms var(--k-ease); }
.kasa-loyalty--karta .kasa-meter__row { display:flex; justify-content:space-between; align-items:baseline; margin-top:7px; font-size:12.5px; color:rgba(234,241,251,0.78); }
.kasa-loyalty--karta .kasa-meter__row b { font-family:var(--k-mono); font-weight:700; color:#fff; }

/* ---- summary medal (card-active checkout) ---- */
/* The transaction is paid: no progress meter, just a ceremonial gold medallion
   carrying the elki earned. Centered, full-width (no card/grid split). */
.kasa-loyalty--karta[data-size="summary"] { padding:18px 22px 20px; text-align:center; }
.kasa-medal { display:flex; flex-direction:column; align-items:center; }
.kasa-medal__lead { font-family:var(--k-display); font-style:italic; font-weight:500; font-size:23px; line-height:1.1;
  color:#fff; margin-top:14px; animation:kasa-zone-in 460ms var(--k-ease) both; }
.kasa-medal__stage { position:relative; width:172px; height:172px; margin:10px auto 2px; display:grid; place-items:center; }
.kasa-medal__rays { position:absolute; inset:0; border-radius:50%;
  background:conic-gradient(from 0deg, rgba(255,236,77,0) 0deg, rgba(255,236,77,0.22) 6deg, rgba(255,236,77,0) 12deg);
  -webkit-mask:radial-gradient(circle, transparent 54px, #000 56px, #000 84px, transparent 86px);
          mask:radial-gradient(circle, transparent 54px, #000 56px, #000 84px, transparent 86px);
  animation:kasa-medal-spin 20s linear infinite; }
@keyframes kasa-medal-spin { to { transform:rotate(360deg); } }
.kasa-medal__disc { position:relative; width:116px; height:116px; border-radius:50%; display:grid; place-items:center;
  background:radial-gradient(circle at 38% 28%, var(--accent-light), var(--accent) 56%, var(--accent-deep));
  box-shadow:inset 0 -4px 8px rgba(0,0,0,0.25), inset 0 2px 4px rgba(255,255,255,0.6), 0 16px 34px -10px rgba(214,164,0,0.8);
  animation:kasa-medal-pop 560ms var(--k-ease) 140ms both; }
.kasa-medal__disc::before { content:""; position:absolute; inset:9px; border-radius:50%; border:2px dashed rgba(110,84,0,0.45); }
@keyframes kasa-medal-pop { 0% { opacity:0; transform:scale(0.2) rotate(-24deg); } 60% { transform:scale(1.14); } 100% { opacity:1; transform:scale(1) rotate(0); } }
.kasa-medal__num { font-family:var(--k-mono); font-weight:700; font-size:52px; line-height:0.8; letter-spacing:-0.03em;
  color:var(--coin-ink, #6b5400); font-variant-numeric:tabular-nums; }
.kasa-medal__disc svg { position:absolute; top:9px; right:11px; width:28px; height:28px; }
.kasa-medal__disc svg .blade { fill:var(--coin-ink, #6b5400); }
.kasa-medal__disc svg .vein { stroke:#fff; stroke-width:2.2; fill:none; stroke-linecap:round; }
.kasa-medal__unit { font-family:var(--k-display); font-style:italic; font-weight:500; font-size:23px; color:var(--accent-light);
  text-shadow:0 0 22px color-mix(in srgb,var(--accent) 50%,transparent); margin-top:8px; }
.kasa-medal__done { display:inline-flex; align-items:center; gap:6px; margin-top:14px; font-size:12.5px; font-weight:600;
  color:#cdebd2; background:rgba(74,201,122,0.14); border:1px solid rgba(74,201,122,0.42); padding:6px 13px; border-radius:999px;
  animation:kasa-zone-in 460ms var(--k-ease) 220ms both; }
.kasa-medal--zero { padding:8px 0; }
.kasa-medal--zero .kasa-medal__lead { margin-bottom:4px; }
.kasa-medal__thanks { margin-top:8px; font-size:14px; color:rgba(234,241,251,0.82);
  animation:kasa-zone-in 460ms var(--k-ease) 160ms both; }

/* compact banner */
.kasa-loyalty--karta[data-size="compact"] { padding:13px 18px; }
.kasa-loyalty__compact { display:flex; align-items:center; gap:15px; }
.kasa-compact__card { position:relative; width:78px; height:50px; flex-shrink:0; border-radius:9px; overflow:hidden;
  background:linear-gradient(135deg,var(--brand-bright) 0%,var(--brand-deep) 100%); box-shadow:0 9px 18px -9px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.25); }
.kasa-compact__card .b { position:absolute; left:8px; top:6px; font-family:var(--k-display); font-style:italic; font-weight:600; font-size:10px; color:#fff; }
.kasa-compact__card .chip { position:absolute; left:8px; bottom:7px; width:16px; height:11px; border-radius:3px; background:linear-gradient(135deg,var(--accent-light),var(--accent-deep)); }
.kasa-compact__card .beam { position:absolute; inset:0; background:linear-gradient(100deg, transparent 40%, rgba(255,255,255,0.5) 50%, transparent 60%); transform:translateX(-120%); animation:kasa-karta-beam 2.6s ease-in-out infinite; }
.kasa-compact__card[data-on="true"] { background:linear-gradient(135deg,var(--brand) 0%,var(--brand-deep) 100%); box-shadow:0 9px 18px -8px rgba(46,123,214,0.6), 0 0 0 2px color-mix(in srgb,var(--accent) 60%,transparent); }
.kasa-compact__check { position:absolute; right:-6px; top:-6px; width:22px; height:22px; border-radius:50%; display:grid; place-items:center; font-size:12px; font-weight:800;
  background:linear-gradient(135deg,var(--accent-light),var(--accent)); color:var(--brand-deep); box-shadow:0 5px 12px -4px var(--accent-deep); z-index:2; }
.kasa-compact__body { flex:1; min-width:0; }
.kasa-compact__eyebrow { text-transform:uppercase; letter-spacing:0.16em; font-size:9.5px; font-weight:600; margin-bottom:4px;
  display:inline-block; padding:3px 9px; border-radius:999px; color:#ffe9d8; background:rgba(255,138,91,0.16); border:1px solid rgba(255,138,91,0.4); }
.kasa-compact__eyebrow.on { color:#0e2a60; background:linear-gradient(135deg,var(--accent-light),var(--accent)); border-color:rgba(255,255,255,0.3); }
.kasa-compact__title { font-family:var(--k-display); font-style:italic; font-weight:500; font-size:18px; line-height:1.12; color:#fff; }
.kasa-compact__title b { font-style:normal; font-weight:700; }
.kasa-loyalty--karta[data-state="regret"] .kasa-compact__title b { color:var(--loss, #ff8a5b); }
.kasa-loyalty--karta[data-state="reward"] .kasa-compact__title b { color:var(--accent-light); }
.kasa-compact__scan { display:inline-flex; align-items:center; gap:7px; flex-shrink:0; font-size:13px; font-weight:600; color:#eaf1fb; white-space:nowrap; }
.kasa-compact__scan .arr { font-size:18px; color:var(--accent-light); }
.kasa-compact__earned { display:inline-flex; align-items:center; gap:8px; flex-shrink:0; }
.kasa-compact__earned .elki { width:34px; height:34px; }
.kasa-compact__earned .elki svg { width:19px; height:19px; }
.kasa-compact__earned b { font-family:var(--k-mono); font-weight:700; font-size:24px; color:var(--accent-light); text-shadow:0 0 18px color-mix(in srgb,var(--accent) 55%,transparent); }

/* ============================================================
   COMPACT DENSITY — small kiosks (≤900 wide OR ≤700 tall; e.g. 800×600), used
   most visibly by "Lewiatan z Kartą".
   The base `.kasa` scale is tuned for the ≥1024×768 minimum. On an 800×600
   panel the fixed px sizes overflow: the receipt header clips its loyalty
   caption, the regret window's sub-copy is cut by overflow:hidden, and the
   card + ad + promo rows fight for the 600px column. This block recompresses
   paddings, type and the card visuals so the SAME scene fits the shorter
   viewport with nothing dropped. Activated by `.kasa--compact`, set from the
   kiosk-REPORTED screen resolution (Device#small_screen?) AND reconciled from
   the live viewport by kasa(_live)_controller — so it is correct on a real
   800×600 kiosk even before client_info has been reported. Scoped entirely to
   the marker class so the full layout is untouched. The `.kasa--tiny` block
   below layers a second, tighter pass on top of this for very short panels
   (≤540 tall, e.g. 800×480).
   ============================================================ */
.kasa--compact { font-size:15px; }

/* left: karta zones */
.kasa--compact .kasa-zone--loyalty { padding:10px 22px 2px; }
.kasa--compact .kasa-zone--promos  { padding:4px 22px 12px; }
.kasa[data-mode="serving"].kasa--compact .kasa-stage--karta .kasa-zone--media { padding:4px 22px 8px; }
.kasa--compact .kasa-loyalty--karta { padding:11px 15px; border-radius:16px; }
.kasa--compact .kasa-logo--loyalty .kasa-logo__art { height:24px; }
.kasa--compact .kasa-loyalty--karta .kasa-loyalty__eyebrow { font-size:9px; padding:4px 9px; }
.kasa--compact .kasa-loyalty--karta .kasa-loyalty__grid { gap:13px; margin-top:9px; }
.kasa--compact .kasa-loyalty--karta .kasa-loyalty__title { font-size:17px; }
.kasa--compact .kasa-loyalty--karta .kasa-loyalty__sub { font-size:12px; margin-top:5px; line-height:1.3; }
/* card visual */
.kasa--compact .kasa-cardvis { width:104px; gap:7px; }
.kasa--compact .kasa-cardvis__plastic { width:104px; height:66px; border-radius:11px; }
.kasa--compact .kasa-cardvis__chip { left:11px; top:24px; width:21px; height:15px; }
.kasa--compact .kasa-cardvis__brand { left:11px; top:8px; font-size:12px; }
.kasa--compact .kasa-cardvis__check { width:24px; height:24px; font-size:13px; }
.kasa--compact .kasa-scan { font-size:12px; }
/* elki tokens */
.kasa--compact .elki-row { gap:7px; margin-top:9px; }
.kasa--compact .elki { width:31px; height:31px; }
.kasa--compact .elki svg { width:17px; height:17px; }
.kasa--compact .elki-count b { font-size:24px; }
.kasa--compact .elki-count span { font-size:12px; }
/* regret ledger */
.kasa--compact .kasa-ledger { margin-top:9px; gap:6px; }
.kasa--compact .kasa-ledger__row { grid-template-columns:26px 1fr auto; gap:9px; padding:7px 10px; border-radius:11px; }
.kasa--compact .kasa-ledger__ico { width:26px; height:26px; font-size:13px; }
.kasa--compact .kasa-ledger__ico--leaf svg { width:15px; height:15px; }
.kasa--compact .kasa-ledger__txt { font-size:11.5px; line-height:1.2; }
.kasa--compact .kasa-ledger__amt { font-size:15px; }
/* reward progress */
.kasa--compact .kasa-reward { margin-top:9px; padding:10px 13px; }
.kasa--compact .kasa-reward__lead { font-size:12px; }
.kasa--compact .kasa-loyalty--karta .kasa-meter { margin-top:7px; height:8px; }
.kasa--compact .kasa-loyalty--karta .kasa-meter__row { margin-top:5px; font-size:11.5px; }
/* compact banner (promos piled up) */
.kasa--compact .kasa-loyalty--karta[data-size="compact"] { padding:10px 14px; }
.kasa--compact .kasa-loyalty__compact { gap:12px; }
.kasa--compact .kasa-compact__card { width:66px; height:42px; }
.kasa--compact .kasa-compact__title { font-size:16px; }
.kasa--compact .kasa-compact__earned b { font-size:21px; }
/* summary medal (card checkout) */
.kasa--compact .kasa-loyalty--karta[data-size="summary"] { padding:12px 18px 14px; }
.kasa--compact .kasa-medal__lead { font-size:19px; margin-top:8px; }
.kasa--compact .kasa-medal__stage { width:124px; height:124px; margin:6px auto 0; }
.kasa--compact .kasa-medal__disc { width:86px; height:86px; }
.kasa--compact .kasa-medal__num { font-size:38px; }
.kasa--compact .kasa-medal__disc svg { width:22px; height:22px; top:7px; right:8px; }
.kasa--compact .kasa-medal__unit { font-size:18px; margin-top:5px; }
.kasa--compact .kasa-medal__done { margin-top:9px; font-size:11.5px; padding:5px 11px; }
.kasa--compact .kasa-medal__rays { -webkit-mask:radial-gradient(circle, transparent 38px, #000 40px, #000 60px, transparent 62px); mask:radial-gradient(circle, transparent 38px, #000 40px, #000 60px, transparent 62px); }

/* promo cards */
.kasa--compact .kasa-promos { gap:7px; }
.kasa--compact .kasa-promo { gap:11px; padding:8px 12px; border-radius:13px; }
.kasa--compact .kasa-promo__icon { width:36px; height:36px; font-size:19px; border-radius:11px; }
.kasa--compact .kasa-promo__title { font-size:15px; }
.kasa--compact .kasa-promo--strip .kasa-promo__title { font-size:14px; }
.kasa--compact .kasa-promo__note { font-size:11px; margin-top:3px; }
.kasa--compact .kasa-promo__reward-amt { font-size:20px; }
.kasa--compact .kasa-price__now b { font-size:24px; }

/* right: receipt panel (shared by all .kasa themes) */
.kasa--compact .kasa-cart { gap:12px; padding:16px 22px 12px; }
.kasa--compact .kasa-cart__icon { width:42px; height:42px; border-radius:12px; }
.kasa--compact .kasa-cart__icon svg { width:23px; height:23px; }
.kasa--compact .kasa-cart__count b { font-size:32px; }
.kasa--compact .kasa-cart__count span { font-size:14px; }
.kasa--compact .kasa-cart__loyalty { gap:4px; }
.kasa--compact .kasa-cart__loyalty-caption { font-size:9px; letter-spacing:0.1em; }
.kasa--compact .kasa-logo--receipt .kasa-logo__art { height:30px; }
.kasa--compact .kasa-logo--receipt .kasa-logo__word { font-size:17px; }
.kasa--compact .kasa-lines { padding:9px 20px 6px; }
.kasa--compact .kasa-line { padding:11px 0 10px; }
.kasa--compact .kasa-line__head { grid-template-columns:24px 1fr auto; gap:10px; }
.kasa--compact .kasa-line__name { font-size:16px; }
.kasa--compact .kasa-line__total { font-size:16px; }
.kasa--compact .kasa-line__total.struck { font-size:14px; }
.kasa--compact .kasa-line__sub { margin:6px 0 0 34px; gap:4px; }
.kasa--compact .kasa-line__row { font-size:13px; }
.kasa--compact .kasa-line__row.final { font-size:14px; }
.kasa--compact .kasa-foot { padding:12px 22px 16px; gap:9px; }
.kasa--compact .kasa-foot__pre { font-size:14px; }
.kasa--compact .kasa-foot__pre .v { font-size:15px; }
.kasa--compact .kasa-savings { gap:12px; padding:11px 14px; border-radius:15px; }
.kasa--compact .kasa-wallet { width:50px; height:46px; }
.kasa--compact .kasa-savings__label { font-size:15px; }
.kasa--compact .kasa-savings__amount { font-size:25px; }
.kasa--compact .kasa-pay { gap:11px; padding:13px 18px; border-radius:16px; }
.kasa--compact .kasa-pay__label { font-size:14px; }
.kasa--compact .kasa-pay__amount { font-size:28px; }
.kasa--compact .kasa-pay__arrow { width:36px; height:36px; }
.kasa--compact .kasa-pay__arrow svg { width:18px; height:18px; }

/* summary card ("Dziękujemy") */
.kasa--compact .kasa-summary { padding:14px; }
.kasa--compact .kasa-card { width:min(440px,94%); padding:20px 30px 18px; gap:10px; border-radius:18px; }
.kasa--compact .kasa-card__thanks { font-size:clamp(30px,calc(7 * var(--k-vw)),44px); }
.kasa--compact .kasa-card__sub { font-size:12px; letter-spacing:0.2em; }
.kasa--compact .kasa-card__savings { margin:2px auto; padding:16px 22px 14px; border-radius:16px; gap:7px; }
.kasa--compact .kasa-card__savings .big-wallet { width:64px; height:58px; }
.kasa--compact .kasa-card__savings-label { font-size:17px; }
.kasa--compact .kasa-card__savings-amount { font-size:clamp(34px,calc(8 * var(--k-vw)),48px); }
.kasa--compact .kasa-card__total-label { font-size:13px; }
.kasa--compact .kasa-card__total { font-size:clamp(40px,calc(11 * var(--k-vw)),60px); }
.kasa--compact .kasa-card__rows { gap:8px; padding-top:14px; }
.kasa--compact .kasa-card__row dt { font-size:13px; }
.kasa--compact .kasa-card__row dd { font-size:15px; }
.kasa--compact .kasa-card__welcome { padding-top:12px; font-size:17px; }
.kasa--compact .kasa-logo--summary .kasa-logo__art { height:clamp(32px,calc(7 * var(--k-vw)),44px); }

/* ============================================================
   EXTRA-COMPACT DENSITY — very short kiosks (≤540 tall; e.g. 800×480). LAYERS
   on `.kasa--compact` (the live kiosk + lab apply both marks together), so it
   only needs to re-tighten what still overflows at 480 — every property it
   doesn't restate keeps its compact value. Each rule mirrors a `.kasa--compact`
   selector with `--tiny` swapped in: equal specificity, so it wins purely by
   coming later in the file (this block must stay AFTER the compact one). Set
   from Device#tiny_screen? and reconciled live by kasa(_live)_controller.
   ============================================================ */
.kasa--tiny { font-size:13.5px; }

/* left: karta zones */
.kasa--tiny .kasa-zone--loyalty { padding:7px 18px 2px; }
.kasa--tiny .kasa-zone--promos  { padding:3px 18px 8px; }
.kasa[data-mode="serving"].kasa--tiny .kasa-stage--karta .kasa-zone--media { padding:3px 18px 6px; }
.kasa--tiny .kasa-loyalty--karta { padding:8px 13px; border-radius:13px; }
.kasa--tiny .kasa-logo--loyalty .kasa-logo__art { height:20px; }
.kasa--tiny .kasa-loyalty--karta .kasa-loyalty__eyebrow { font-size:8px; padding:3px 7px; }
.kasa--tiny .kasa-loyalty--karta .kasa-loyalty__grid { gap:10px; margin-top:6px; }
.kasa--tiny .kasa-loyalty--karta .kasa-loyalty__title { font-size:15px; }
.kasa--tiny .kasa-loyalty--karta .kasa-loyalty__sub { font-size:11px; margin-top:3px; line-height:1.25; }
/* card visual */
.kasa--tiny .kasa-cardvis { width:88px; gap:5px; }
.kasa--tiny .kasa-cardvis__plastic { width:88px; height:56px; border-radius:9px; }
.kasa--tiny .kasa-cardvis__chip { left:9px; top:20px; width:18px; height:13px; }
.kasa--tiny .kasa-cardvis__brand { left:9px; top:7px; font-size:10px; }
.kasa--tiny .kasa-cardvis__check { width:20px; height:20px; font-size:11px; }
.kasa--tiny .kasa-scan { font-size:11px; }
/* elki tokens */
.kasa--tiny .elki-row { gap:5px; margin-top:6px; }
.kasa--tiny .elki { width:26px; height:26px; }
.kasa--tiny .elki svg { width:14px; height:14px; }
.kasa--tiny .elki-count b { font-size:20px; }
.kasa--tiny .elki-count span { font-size:11px; }
/* regret ledger */
.kasa--tiny .kasa-ledger { margin-top:6px; gap:4px; }
.kasa--tiny .kasa-ledger__row { grid-template-columns:22px 1fr auto; gap:7px; padding:5px 8px; border-radius:9px; }
.kasa--tiny .kasa-ledger__ico { width:22px; height:22px; font-size:11px; }
.kasa--tiny .kasa-ledger__ico--leaf svg { width:13px; height:13px; }
.kasa--tiny .kasa-ledger__txt { font-size:10.5px; line-height:1.2; }
.kasa--tiny .kasa-ledger__amt { font-size:13px; }
/* reward progress */
.kasa--tiny .kasa-reward { margin-top:6px; padding:8px 10px; }
.kasa--tiny .kasa-reward__lead { font-size:11px; }
.kasa--tiny .kasa-loyalty--karta .kasa-meter { margin-top:5px; height:7px; }
.kasa--tiny .kasa-loyalty--karta .kasa-meter__row { margin-top:4px; font-size:10.5px; }
/* compact banner (promos piled up) */
.kasa--tiny .kasa-loyalty--karta[data-size="compact"] { padding:8px 12px; }
.kasa--tiny .kasa-loyalty__compact { gap:9px; }
.kasa--tiny .kasa-compact__card { width:56px; height:36px; }
.kasa--tiny .kasa-compact__title { font-size:14px; }
.kasa--tiny .kasa-compact__earned b { font-size:18px; }
/* summary medal (card checkout) */
.kasa--tiny .kasa-loyalty--karta[data-size="summary"] { padding:10px 14px 11px; }
.kasa--tiny .kasa-medal__lead { font-size:16px; margin-top:6px; }
.kasa--tiny .kasa-medal__stage { width:100px; height:100px; margin:4px auto 0; }
.kasa--tiny .kasa-medal__disc { width:70px; height:70px; }
.kasa--tiny .kasa-medal__num { font-size:31px; }
.kasa--tiny .kasa-medal__disc svg { width:18px; height:18px; top:6px; right:7px; }
.kasa--tiny .kasa-medal__unit { font-size:15px; margin-top:4px; }
.kasa--tiny .kasa-medal__done { margin-top:7px; font-size:10.5px; padding:4px 9px; }
.kasa--tiny .kasa-medal__rays { -webkit-mask:radial-gradient(circle, transparent 31px, #000 33px, #000 50px, transparent 52px); mask:radial-gradient(circle, transparent 31px, #000 33px, #000 50px, transparent 52px); }

/* promo cards */
.kasa--tiny .kasa-promos { gap:5px; }
.kasa--tiny .kasa-promo { gap:9px; padding:6px 10px; border-radius:11px; }
.kasa--tiny .kasa-promo__icon { width:30px; height:30px; font-size:16px; border-radius:9px; }
.kasa--tiny .kasa-promo__title { font-size:13.5px; }
.kasa--tiny .kasa-promo--strip .kasa-promo__title { font-size:12.5px; }
.kasa--tiny .kasa-promo__note { font-size:10px; margin-top:2px; }
.kasa--tiny .kasa-promo__reward-amt { font-size:17px; }
.kasa--tiny .kasa-price__now b { font-size:20px; }

/* right: receipt panel (shared by all .kasa themes) */
.kasa--tiny .kasa-cart { gap:10px; padding:11px 18px 9px; }
.kasa--tiny .kasa-cart__icon { width:36px; height:36px; border-radius:10px; }
.kasa--tiny .kasa-cart__icon svg { width:20px; height:20px; }
.kasa--tiny .kasa-cart__count b { font-size:27px; }
.kasa--tiny .kasa-cart__count span { font-size:12px; }
.kasa--tiny .kasa-cart__loyalty-caption { font-size:8px; }
.kasa--tiny .kasa-logo--receipt .kasa-logo__art { height:25px; }
.kasa--tiny .kasa-logo--receipt .kasa-logo__word { font-size:15px; }
.kasa--tiny .kasa-lines { padding:6px 16px 4px; }
.kasa--tiny .kasa-line { padding:8px 0 7px; }
.kasa--tiny .kasa-line__head { grid-template-columns:20px 1fr auto; gap:8px; }
.kasa--tiny .kasa-line__name { font-size:14px; }
.kasa--tiny .kasa-line__total { font-size:14px; }
.kasa--tiny .kasa-line__total.struck { font-size:12.5px; }
.kasa--tiny .kasa-line__sub { margin:4px 0 0 28px; gap:3px; }
.kasa--tiny .kasa-line__row { font-size:12px; }
.kasa--tiny .kasa-line__row.final { font-size:12.5px; }
.kasa--tiny .kasa-foot { padding:9px 18px 11px; gap:6px; }
.kasa--tiny .kasa-foot__pre { font-size:12.5px; }
.kasa--tiny .kasa-foot__pre .v { font-size:13px; }
.kasa--tiny .kasa-savings { gap:9px; padding:8px 11px; border-radius:12px; }
.kasa--tiny .kasa-wallet { width:42px; height:39px; }
.kasa--tiny .kasa-savings__label { font-size:13px; }
.kasa--tiny .kasa-savings__amount { font-size:21px; }
.kasa--tiny .kasa-pay { gap:9px; padding:10px 14px; border-radius:13px; }
.kasa--tiny .kasa-pay__label { font-size:12.5px; }
.kasa--tiny .kasa-pay__amount { font-size:23px; }
.kasa--tiny .kasa-pay__arrow { width:30px; height:30px; }
.kasa--tiny .kasa-pay__arrow svg { width:15px; height:15px; }

/* summary card ("Dziękujemy") */
.kasa--tiny .kasa-summary { padding:10px; }
.kasa--tiny .kasa-card { width:min(380px,94%); padding:15px 24px 14px; gap:7px; border-radius:15px; }
.kasa--tiny .kasa-card__thanks { font-size:clamp(24px,calc(6 * var(--k-vw)),36px); }
.kasa--tiny .kasa-card__sub { font-size:11px; letter-spacing:0.18em; }
.kasa--tiny .kasa-card__savings { margin:2px auto; padding:12px 18px 11px; border-radius:13px; gap:5px; }
.kasa--tiny .kasa-card__savings .big-wallet { width:52px; height:47px; }
.kasa--tiny .kasa-card__savings-label { font-size:14px; }
.kasa--tiny .kasa-card__savings-amount { font-size:clamp(28px,calc(7 * var(--k-vw)),40px); }
.kasa--tiny .kasa-card__total-label { font-size:12px; }
.kasa--tiny .kasa-card__total { font-size:clamp(32px,calc(9 * var(--k-vw)),48px); }
.kasa--tiny .kasa-card__rows { gap:6px; padding-top:10px; }
.kasa--tiny .kasa-card__row dt { font-size:12px; }
.kasa--tiny .kasa-card__row dd { font-size:13px; }
.kasa--tiny .kasa-card__welcome { padding-top:9px; font-size:14px; }
.kasa--tiny .kasa-logo--summary .kasa-logo__art { height:clamp(26px,calc(6 * var(--k-vw)),36px); }

/* ============================================================
   DISTANCE DENSITY — `.kasa--distance`, for a panel pushed back from the till.

   The two tiers above shrink the scene to survive a small panel. This one goes
   the other way, and it is NOT a fourth step on that ladder: Device#receipt_
   density_tier returns :distance INSTEAD of compact/tiny, never alongside, so
   nothing here layers on their overrides. It sits last only so its plain
   `.kasa--distance X` selectors beat the equal-specificity `.kasa--compact X`
   ones if a stale marker ever survives on the root.

   WHY A DIFFERENT SCENE AND NOT JUST BIGGER TEXT. On a 13" 1280x800 panel the
   item name subtends 10.5' from a metre against a 16' floor, and scaling alone
   tops out around 12' before the canvas gets too small (see ReceiptLegibility).
   Reaching the comfort band needs roughly 2x the glyph — which simply does not
   fit fifteen fully itemised lines. So the receipt drops to the last few lines
   in large type with a dominant total, the way supermarket customer displays
   have always done it, and kasa_live_controller measures how many actually fit
   rather than any number being hardcoded here.

   Sizes are `calc(NNpx * var(--k-type))` so receipt_text_size (normal 1.0 /
   large 1.2 / xlarge 1.4) tunes the whole mode from one variable instead of
   three copies of this block. The 30px name is the reference glyph mirrored in
   ReceiptLegibility::REFERENCE_FONT_PX — change one, change the other.
   ============================================================ */

/* ---- receipt pane ---- */
.kasa--distance .kasa-cart { gap:14px; padding:22px 30px 18px; }
.kasa--distance .kasa-cart__icon { width:calc(46px * var(--k-type)); height:calc(46px * var(--k-type)); border-radius:14px; }
.kasa--distance .kasa-cart__icon svg { width:calc(26px * var(--k-type)); height:calc(26px * var(--k-type)); }
.kasa--distance .kasa-cart__count b { font-size:calc(44px * var(--k-type)); }
.kasa--distance .kasa-cart__count span { font-size:calc(19px * var(--k-type)); }
/* The loyalty caption is a secondary cue; at this size it costs a whole line of
   product for nothing, so only the value survives. */
.kasa--distance .kasa-cart__loyalty-caption { display:none; }
.kasa--distance .kasa-cart__loyalty b { font-size:calc(20px * var(--k-type)); }

.kasa--distance .kasa-lines { padding:8px 30px 4px; }
.kasa--distance .kasa-line { padding:12px 0 11px; }
/* Two columns, not three: the position number is scaffolding for someone
   reading a full receipt up close, and the head total carries the price. */
.kasa--distance .kasa-line__nr { display:none; }
.kasa--distance .kasa-line__head { grid-template-columns:1fr auto; gap:16px; }
.kasa--distance .kasa-line__name { font-size:calc(30px * var(--k-type)); gap:8px; }
.kasa--distance .kasa-line__total { font-size:calc(30px * var(--k-type)); }
.kasa--distance .kasa-line__total.struck { font-size:calc(23px * var(--k-type)); }
.kasa--distance .kasa-line__badge { font-size:calc(13px * var(--k-type)); padding:3px 8px; }
/* Quantity folded into the name ("2 ×" / "0,5 kg") instead of the counter mode's
   separate breakdown row — same information, no second line of height. */
.kasa--distance .kasa-line__qty { flex-shrink:0; font-family:var(--k-mono); font-weight:600;
  font-size:calc(22px * var(--k-type)); color:var(--ink-soft); }
/* The pre-promotion price, struck, beside the one actually charged. */
.kasa--distance .kasa-line__total .was { margin-right:0.45em; font-size:calc(20px * var(--k-type));
  color:var(--ink-faint); text-decoration-thickness:2px; }
/* kasa_live_controller emits at most ONE sub-row in this mode (the saving), so
   the indent that lined breakdown rows up under a name is just lost width. */
.kasa--distance .kasa-line__sub { margin:6px 0 0; gap:4px; }
.kasa--distance .kasa-line__row { font-size:calc(20px * var(--k-type)); }
.kasa--distance .kasa-line__row.final { font-size:calc(21px * var(--k-type)); }
.kasa--distance .kasa-line__row .v { font-size:calc(21px * var(--k-type)); }

/* The elision row for lines scrolled out of the window — rendered by
   kasa_live_controller only when it actually dropped something. */
.kasa-lines__more { flex-shrink:0; padding:8px 0 10px; text-align:center;
  font-size:calc(17px * var(--k-type)); color:var(--ink-faint);
  letter-spacing:0.04em; border-bottom:1px dashed var(--rule); }

.kasa--distance .kasa-foot { padding:14px 30px 20px; gap:11px; }
.kasa--distance .kasa-foot__pre { font-size:calc(21px * var(--k-type)); }
.kasa--distance .kasa-foot__pre .v { font-size:calc(23px * var(--k-type)); }
.kasa--distance .kasa-savings { gap:14px; padding:13px 16px; }
.kasa--distance .kasa-wallet { width:calc(62px * var(--k-type)); height:calc(57px * var(--k-type)); }
.kasa--distance .kasa-savings__label { font-size:calc(22px * var(--k-type)); }
.kasa--distance .kasa-savings__amount { font-size:calc(36px * var(--k-type)); }
/* The total is what a customer at a distance is actually looking for, so it is
   the one element allowed to grow faster than the rest of the pane. */
.kasa--distance .kasa-pay { gap:14px; padding:18px 22px; }
.kasa--distance .kasa-pay__label { font-size:calc(22px * var(--k-type)); }
.kasa--distance .kasa-pay__amount { font-size:calc(54px * var(--k-type)); }
.kasa--distance .kasa-pay__arrow { width:calc(40px * var(--k-type)); height:calc(40px * var(--k-type)); }

/* ---- left stage: same treatment, less of it ----
   The stage has no scroll of its own, so growing its type without trimming its
   chrome is how the promo zone gets pushed off a short panel. Copy grows; the
   decorative card art and beams shrink to pay for it. */
.kasa--distance .kasa-zone--loyalty { padding:16px 30px 2px; }
.kasa--distance .kasa-zone--promos { padding:6px 30px 14px; }
.kasa--distance .kasa-loyalty--karta { padding:14px 18px; border-radius:18px; }
.kasa--distance .kasa-loyalty--karta .kasa-loyalty__grid { gap:14px; margin-top:10px; }
.kasa--distance .kasa-loyalty--karta .kasa-loyalty__title { font-size:calc(28px * var(--k-type)); }
.kasa--distance .kasa-loyalty--karta .kasa-loyalty__sub { font-size:calc(18px * var(--k-type)); margin-top:6px; }
.kasa--distance .kasa-loyalty--karta .kasa-loyalty__eyebrow { font-size:calc(12px * var(--k-type)); }
.kasa--distance .kasa-loyalty__title { font-size:calc(28px * var(--k-type)); }
.kasa--distance .kasa-loyalty__sub { font-size:calc(18px * var(--k-type)); }
.kasa--distance .kasa-cardvis { width:104px; gap:7px; }
.kasa--distance .kasa-cardvis__plastic { width:104px; height:66px; }
.kasa--distance .kasa-cardvis__chip { left:11px; top:24px; width:21px; height:15px; }
.kasa--distance .kasa-cardvis__brand { left:11px; top:8px; font-size:12px; }
.kasa--distance .kasa-loyalty__card { width:74px; height:48px; }
.kasa--distance .kasa-scan { font-size:calc(15px * var(--k-type)); }
.kasa--distance .elki { width:34px; height:34px; }
.kasa--distance .elki svg { width:19px; height:19px; }
.kasa--distance .elki-count b { font-size:calc(26px * var(--k-type)); }
.kasa--distance .elki-count span { font-size:calc(14px * var(--k-type)); }
.kasa--distance .kasa-ledger__txt { font-size:calc(15px * var(--k-type)); }
.kasa--distance .kasa-ledger__amt { font-size:calc(19px * var(--k-type)); }
.kasa--distance .kasa-reward__lead { font-size:calc(16px * var(--k-type)); }
.kasa--distance .kasa-promo { padding:11px 15px; gap:12px; }
.kasa--distance .kasa-promo__title { font-size:calc(21px * var(--k-type)); }
.kasa--distance .kasa-promo__sub { font-size:calc(17px * var(--k-type)); }
.kasa--distance .kasa-meter { height:12px; }
.kasa--distance .kasa-meter__row { font-size:calc(16px * var(--k-type)); }

/* Short canvas in distance mode. A media query cannot be used here: it measures
   the real viewport, while everything in this scene is laid out on the LOGICAL
   canvas (viewport / --k-scale), so above 100% scale it would under-trigger.
   kasa_live_controller#applyDensity stamps this marker from the same logical
   height it uses for every other tier decision. */
.kasa--distance-short .kasa-zone--loyalty { padding:8px 24px 0; }
.kasa--distance-short .kasa-zone--promos { padding:4px 24px 8px; }
.kasa--distance-short .kasa-loyalty--karta { padding:10px 14px; }
.kasa--distance-short .kasa-loyalty--karta .kasa-loyalty__grid { gap:10px; margin-top:7px; }
.kasa--distance-short .kasa-loyalty--karta .kasa-loyalty__sub { display:none; }
.kasa--distance-short .kasa-cardvis { width:78px; }
.kasa--distance-short .kasa-cardvis__plastic { width:78px; height:50px; }
.kasa--distance-short .kasa-cart { padding:14px 24px 12px; }
.kasa--distance-short .kasa-lines { padding:6px 24px 2px; }
.kasa--distance-short .kasa-foot { padding:10px 24px 14px; gap:8px; }

/* ============================================================
   RCFG — the customer-screen legibility configurator
   (/devices/:identifier/receipt_design). A control rail beside a live preview
   of the real scene. Its own namespace because it renders in the minimal
   receipt_lab layout, with none of the admin shell's surrounding chrome.

   The preview is the actual `.kasa` renderer at a SIMULATED resolution, scaled
   down by receipt_config_controller#fitFrame to fit the page. Two nested
   transforms are in play and they mean different things: `.rcfg__canvas` is the
   operator's zoom, `.kasa-scene` is the device's own setting. Keeping them on
   separate elements is what stops the preview from misreporting glyph size.
   ============================================================ */
/* `position:fixed` rather than a 100vh block. The receipt_lab layout
   deliberately sets no global html/body style (that used to leak back into the
   admin pages through Turbo), so a normal-flow root can still make the DOCUMENT
   scroll — and it did: Selenium scrolling a visually-hidden radio into view
   dragged the whole control rail off screen. A fixed root cannot contribute to
   document height at all, which removes the entire failure mode instead of
   chasing whichever child overflowed. */
.rcfg { position:fixed; inset:0; display:flex; overflow:hidden;
  background:#0c0d0a; color:#f3f1e8;
  font-family:"Bricolage Grotesque", system-ui, sans-serif; }

.rcfg__rail { flex:0 0 400px; max-width:400px; height:100%; overflow-y:auto;
  padding:22px 24px 40px; background:#14150f; border-right:1px solid rgba(255,255,255,0.08); }
.rcfg__head { display:flex; align-items:flex-start; gap:12px; margin-bottom:22px; }
.rcfg__eyebrow { font-size:11px; letter-spacing:0.18em; text-transform:uppercase; color:rgba(243,241,232,0.45); }
.rcfg__title { margin:4px 0 2px; font-size:20px; font-weight:700; line-height:1.2; }
.rcfg__sub { font-size:13px; color:rgba(243,241,232,0.55); }
.rcfg__close { margin-left:auto; flex-shrink:0; width:36px; height:36px; display:grid; place-items:center;
  border-radius:50%; background:rgba(255,255,255,0.08); color:#f3f1e8; text-decoration:none; }
.rcfg__close:hover { background:rgba(255,255,255,0.16); }

.rcfg__section { padding:16px 0; border-top:1px solid rgba(255,255,255,0.07); }
.rcfg__h { margin:0 0 4px; font-size:14px; font-weight:700; letter-spacing:0.01em; }
.rcfg__hint { margin:6px 0 0; font-size:12px; line-height:1.45; color:rgba(243,241,232,0.5); }
.rcfg__note { margin:8px 0 0; font-size:12px; line-height:1.45; color:var(--accent, #ffd200); }
.rcfg__row { margin-top:12px; }
.rcfg__label { display:flex; align-items:baseline; justify-content:space-between; gap:10px;
  margin-bottom:6px; font-size:13px; font-weight:600; color:rgba(243,241,232,0.85); }
.rcfg__label output { font-family:"Spline Sans Mono", monospace; font-size:13px; color:var(--accent, #ffd200); }
/* The shell's base `label { color: var(--text) }` is a dark ink meant for the
   light admin chrome, and it beats the inherited colour on every label in this
   dark rail — which rendered the mode titles near-invisible. Restate it. */
.rcfg label { color:inherit; font-size:inherit; font-weight:inherit; }
/* The rail's own control skin — the admin form styles live on the app shell,
   which this layout deliberately does not load. */
.rcfg select, .rcfg input[type="number"] { width:100%; max-width:none; padding:9px 10px; border-radius:9px;
  background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.14); color:#f3f1e8; font-size:14px; }
.rcfg input[type="range"] { width:100%; }

/* ---- the verdict ---- */
.rcfg__section--readout { border-top-color:rgba(255,255,255,0.14); }
.rcfg__readout { padding:14px 16px; border-radius:14px; background:rgba(255,255,255,0.05);
  border-left:4px solid var(--rcfg-verdict, rgba(255,255,255,0.3)); }
.rcfg__readout[data-verdict="comfort"] { --rcfg-verdict:#4ac07a; }
.rcfg__readout[data-verdict="minimum"] { --rcfg-verdict:#e5b524; }
.rcfg__readout[data-verdict="poor"]    { --rcfg-verdict:#d6402b; }
.rcfg__readout[data-verdict="error"]   { --rcfg-verdict:rgba(255,255,255,0.3); }
.rcfg__verdict { display:flex; align-items:center; gap:9px; font-size:15px; line-height:1.35; }
.rcfg__verdict b { font-weight:700; color:#fff; }
.rcfg__verdict-dot { flex-shrink:0; width:10px; height:10px; border-radius:50%; background:var(--rcfg-verdict); }
.rcfg__facts { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:6px 14px; margin:12px 0 0; }
.rcfg__facts div { display:flex; align-items:baseline; justify-content:space-between; gap:8px; min-width:0; }
.rcfg__facts dt { font-size:11px; color:rgba(243,241,232,0.45); }
.rcfg__facts dd { margin:0; font-family:"Spline Sans Mono", monospace; font-size:12px; color:rgba(243,241,232,0.9); }
.rcfg__warnings { margin-top:10px; display:flex; flex-direction:column; gap:6px; }
.rcfg__warn { margin:0; padding:9px 11px; border-radius:9px; font-size:12px; line-height:1.45;
  background:rgba(214,64,43,0.14); border:1px solid rgba(214,64,43,0.35); color:#ffd9d2; }
.rcfg__rec { display:flex; align-items:center; gap:12px; margin-top:12px; padding:11px 13px;
  border-radius:11px; background:rgba(255,255,255,0.05); }
.rcfg__rec[data-state="applied"] { opacity:0.6; }
.rcfg__rec-body { min-width:0; }
.rcfg__rec-label { display:block; font-size:11px; letter-spacing:0.14em; text-transform:uppercase; color:rgba(243,241,232,0.45); }
.rcfg__rec-value { display:block; margin-top:3px; font-size:13px; font-weight:600; }
.rcfg__rec .btn { margin-left:auto; flex-shrink:0; }

/* ---- choices ---- */
.rcfg__choices { display:flex; flex-direction:column; gap:8px; margin-top:10px; }
.rcfg__choice { display:flex; align-items:flex-start; gap:10px; padding:11px 13px; border-radius:11px;
  background:rgba(255,255,255,0.04); border:1px solid rgba(255,255,255,0.10); cursor:pointer; }
.rcfg__choice:has(input:checked) { background:rgba(255,255,255,0.09); border-color:var(--accent, #ffd200); }
.rcfg__choice input { margin-top:3px; flex-shrink:0; }
.rcfg__choice-body { display:flex; flex-direction:column; gap:3px; min-width:0; }
.rcfg__choice-title { font-size:14px; font-weight:600; }
.rcfg__choice-sub { font-size:12px; line-height:1.4; color:rgba(243,241,232,0.5); }
.rcfg__segmented { display:flex; gap:6px; margin-top:10px; }
.rcfg__seg { flex:1; text-align:center; padding:10px 6px; border-radius:10px; cursor:pointer;
  background:rgba(255,255,255,0.05); border:1px solid rgba(255,255,255,0.10); font-size:13px; font-weight:600; }
.rcfg__seg:has(input:checked) { background:var(--accent, #ffd200); border-color:var(--accent, #ffd200); color:#14150f; }
.rcfg__seg input { position:absolute; opacity:0; pointer-events:none; }

.rcfg__advanced { margin-top:16px; padding-top:14px; border-top:1px solid rgba(255,255,255,0.07); }
.rcfg__advanced > summary { cursor:pointer; font-size:13px; font-weight:600; color:rgba(243,241,232,0.75); }
.rcfg__row--slider { margin-top:14px; }
.rcfg__live { display:flex; align-items:flex-start; gap:10px; cursor:pointer; }
.rcfg__live input { margin-top:3px; flex-shrink:0; }
.rcfg__live-title { display:block; font-size:14px; font-weight:600; }
.rcfg__actions { margin-top:18px; display:flex; gap:10px; }
.rcfg__actions .btn, .rcfg__actions input[type="submit"] { width:100%; }

/* Save confirmation. Above the fixed configurator root (z-index 80 clears the
   operator dock at 60) so it is visible whichever pane has focus. */
.rcfg-flash { position:fixed; top:16px; left:50%; transform:translateX(-50%); z-index:80;
  max-width:min(560px, calc(100vw - 32px)); padding:12px 20px; border-radius:12px;
  font-family:"Bricolage Grotesque", system-ui, sans-serif; font-size:14px; font-weight:600;
  box-shadow:0 18px 40px -16px rgba(0,0,0,0.7); }
.rcfg-flash--notice { background:#1f7a4d; color:#f2fff8; }
.rcfg-flash--alert  { background:#a8271a; color:#fff1ee; }

/* ---- preview stage ---- */
.rcfg__stage { position:relative; flex:1 1 auto; min-width:0; display:flex; align-items:center;
  justify-content:center; padding:28px 28px 88px; overflow:hidden; }
.rcfg__frame { position:relative; width:100%; height:100%; overflow:hidden; }
/* Sized in JS to the SIMULATED resolution, then scaled to fit. It must be taken
   OUT OF FLOW to do that: a transform is a paint-time effect, so a canvas laid
   out at 1280x800 keeps reserving 1280x800 however small it is drawn — which
   pushed the whole page taller than the viewport and scrolled the control rail
   off screen. Absolute + centre-translate makes the frame's size the only thing
   that decides layout. */
.rcfg__canvas { position:absolute; top:50%; left:50%; overflow:hidden; border-radius:6px;
  box-shadow:0 30px 80px -30px rgba(0,0,0,0.9), 0 0 0 1px rgba(255,255,255,0.08);
  transform-origin:center center; }

@media (max-width: 880px) {
  /* Preview first, controls under it — an operator on a phone is checking a
     setting against a screen they can see, not discovering the tool. The rail
     keeps scrolling INTERNALLY rather than letting the document grow, for the
     same reason the root is fixed. */
  .rcfg { flex-direction:column; }
  .rcfg__stage { order:1; flex:0 0 auto; width:100%; height:min(52vh, 420px); padding:14px 14px 76px; }
  .rcfg__rail { order:2; flex:1 1 auto; min-height:0; max-width:none; width:100%; height:auto;
    border-right:0; border-top:1px solid rgba(255,255,255,0.08); padding:18px 16px 40px; }
  .rcfg__facts { grid-template-columns:1fr; }
}

@media (hover: none) and (pointer: coarse) {
  .rcfg__choice, .rcfg__seg, .rcfg__live { min-height:44px; }
  .rcfg select, .rcfg input[type="submit"], .rcfg .btn { min-height:44px; }
  .rcfg__advanced > summary { min-height:44px; display:flex; align-items:center; }
  .rcfg__close { width:44px; height:44px; }
}
/* flex-wrap + a viewport-bounded max-width keep every control reachable (and the
   page free of horizontal overflow) when the row is wider than a phone viewport. */
.kasa-dock { position:fixed; left:50%; bottom:18px; transform:translateX(-50%); z-index:60; display:flex; flex-wrap:wrap; justify-content:center; gap:8px; max-width:calc(100vw - 24px); padding:8px; border-radius:16px; background:rgba(12,13,10,0.82); backdrop-filter:blur(10px); box-shadow:0 12px 30px -10px rgba(0,0,0,0.6); }
.kasa-dock button { font-family:var(--k-ui); font-size:13px; font-weight:600; color:#f3f1e8; background:rgba(255,255,255,0.08); border:1px solid rgba(255,255,255,0.12); padding:9px 16px; border-radius:10px; cursor:pointer; }
.kasa-dock button:hover { background:rgba(255,255,255,0.16); }
/* --brand/--brand-bright are declared on `.kasa`, and the configurator renders
   this dock OUTSIDE the scene (so it is not scaled with it) — where those two
   resolve to nothing, leaving a transparent button. Fallbacks keep the accent
   readable in both arrangements. */
.kasa-dock button.accent { background:var(--brand, #1f62b0); border-color:var(--brand-bright, #2e7bd6); }
.kasa-dock__label { color:rgba(243,241,232,0.5); font-size:11px; align-self:center; padding:0 8px 0 4px; letter-spacing:0.04em; }
.kasa-dock__sep { width:1px; background:rgba(255,255,255,0.15); margin:4px 2px; }
/* Touch operators: the dock controls (density / theme / preview) need a ~44px
   tap target on coarse pointers — the default 13px/9px sizing is ~36px tall. */
@media (hover: none) and (pointer: coarse) {
  .kasa-dock button { min-height:44px; padding-top:11px; padding-bottom:11px; }
}

/* ============================================================
   Inventory ("Inwentarz") — fleet hardware & telemetry stats.
   Custom CSS/SVG charts (no chart library); reuses the dashboard
   palette + breakpoints (880 / 640). See app/views/inventory/.
   ============================================================ */

/* Small annotation next to a section header / under a chart */
.inv-note { font-size: 12.5px; color: var(--text-secondary); }
.inv-note--block { display: block; margin-top: 14px; max-width: 70ch; line-height: 1.5; }

.inv-empty {
  margin: 0;
  padding: 22px 0;
  color: var(--text-secondary);
  font-size: 13.5px;
}

/* ---- Horizontal bar list (resolutions, scale model/transport/state) ---- */
.inv-bars { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.inv-bar {
  display: grid;
  grid-template-columns: 140px 1fr 48px 56px;
  align-items: center;
  gap: 12px;
}
.inv-bar__label { font-size: 13px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.inv-bar__track { position: relative; height: 10px; border-radius: 999px; background: var(--gray-100); overflow: hidden; }
.inv-bar__fill {
  position: absolute; inset: 0 auto 0 0;
  height: 100%;
  min-width: 3px;
  border-radius: 999px;
  background: var(--blue);
  transition: width 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.inv-bar__value { font-size: 13.5px; font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }
.inv-bar__pct { font-size: 12.5px; color: var(--text-secondary); text-align: right; font-variant-numeric: tabular-nums; }

/* ---- Card grid for OS donuts + scale breakdowns ---- */
.inv-os-grid {
  margin-top: 18px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
}
.inv-card {
  border: 1px solid var(--border);
  border-radius: 14px;
  background: #fff;
  padding: 18px 20px 20px;
}
.inv-card__title { font-size: 13.5px; font-weight: 600; color: var(--text); margin-bottom: 6px; }

/* ---- SVG donut + legend ---- */
.inv-donut-wrap { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; margin-top: 10px; }
.inv-donut { width: 120px; height: 120px; flex-shrink: 0; }
.inv-donut circle { transition: stroke-dasharray 0.6s cubic-bezier(0.22, 1, 0.36, 1); }
.inv-donut__center { font-size: 26px; font-weight: 600; fill: var(--text); font-variant-numeric: tabular-nums; }

.inv-legend { list-style: none; margin: 0; padding: 0; flex: 1 1 200px; min-width: 180px; display: flex; flex-direction: column; gap: 8px; }
.inv-legend__item { display: grid; grid-template-columns: 12px 1fr 40px 52px; align-items: center; gap: 10px; }
.inv-legend__dot { width: 12px; height: 12px; border-radius: 4px; }
.inv-legend__label { font-size: 13px; color: var(--text); }
.inv-legend__value { font-size: 13px; font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }
.inv-legend__pct { font-size: 12.5px; color: var(--text-secondary); text-align: right; font-variant-numeric: tabular-nums; }

@media (max-width: 880px) {
  .inv-os-grid { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .inv-bar { grid-template-columns: 110px 1fr 40px 48px; gap: 8px; }
  .inv-bar__label { font-size: 12px; }
  .inv-donut-wrap { gap: 14px; }
  .inv-donut { width: 104px; height: 104px; }
}

/* ===== Location editor / preview (tabbed) ===== */

/* The editor (6 tabs) and preview (up to 8) outgrow a phone width, so the tab
   strip scrolls horizontally rather than wrapping or pushing the page wide. */
.tabs--scroll {
  overflow-x: auto;
  flex-wrap: nowrap;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.tabs--scroll::-webkit-scrollbar { display: none; }
.tabs--scroll .tabs__tab { flex: 0 0 auto; white-space: nowrap; }

/* First heading in a panel shouldn't inherit the big inter-section gap that
   dash-section / form-section carry when stacked down a page. */
.tabs__panel > .form-section__title:first-child,
.tabs__panel > .dash-section:first-child,
.tabs__panel > section:first-child { margin-top: 0; }

/* A divider between two groups inside one tab panel (device photos under the
   location photo; modules under the device table). */
.form-subsection {
  margin-top: 28px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}

/* Two-column field grid for the Dane tab; collapses on a phone. */
.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px 24px;
}
.form-grid > p { margin: 8px 0; }
.form-grid__wide { grid-column: 1 / -1; }
@media (max-width: 640px) {
  .form-grid { grid-template-columns: 1fr; }
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 24px;
}

/* Map tab: an address echo bar above the map (the "repeated address"). */
.map-tab__address {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 4px;
  font-size: 14px;
}
.map-tab__address > svg { color: var(--brand-red); flex: 0 0 auto; }
.map-tab__address > span { flex: 1 1 200px; min-width: 0; }
.map-tab__address .btn { flex: 0 0 auto; }

/* ===== Instant photo widget (location + per-device) ===== */
.photo-single { max-width: 440px; }

.photo-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--white);
}
.photo-tile--error { border-color: var(--brand-red); }

.photo-tile__frame {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border: 1.5px dashed var(--gray-200);
  border-radius: var(--radius-sm);
  background: var(--gray-50);
  color: var(--text-secondary);
}
.photo-tile__frame--filled { border-style: solid; background: var(--white); }
.photo-tile__frame img { width: 100%; height: 100%; object-fit: cover; display: block; }

.photo-tile__placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  font-size: 12px;
}

.photo-tile__spinner {
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border: 3px solid rgba(35, 31, 32, 0.15);
  border-top-color: var(--brand-red);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
.photo-tile--busy .photo-tile__frame { opacity: 0.5; }
.photo-tile--busy .photo-tile__spinner { display: block; }

/* ===== Gallery thumbnails (several photos per record) ===== */
.photo-tile__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 8px;
}
@media (max-width: 640px) {
  .photo-tile__grid { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); }
}

.photo-thumb {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--white);
}
.photo-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-thumb--cover { outline: 2px solid var(--brand-red); outline-offset: -2px; }

.photo-thumb__badge {
  position: absolute;
  left: 4px;
  bottom: 4px;
  padding: 1px 6px;
  border-radius: var(--radius-full);
  background: var(--brand-red);
  color: var(--white);
  font-size: 10px;
  font-weight: 600;
}

.photo-thumb__actions {
  position: absolute;
  top: 4px;
  right: 4px;
  display: flex;
  gap: 4px;
  opacity: 0;
  transition: opacity var(--transition);
}
.photo-thumb:hover .photo-thumb__actions,
.photo-thumb:focus-within .photo-thumb__actions { opacity: 1; }

.photo-thumb__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: rgba(11, 11, 14, 0.62);
  color: var(--white);
  cursor: pointer;
}
.photo-thumb__btn:hover { background: rgba(11, 11, 14, 0.82); }
.photo-thumb__btn--danger:hover { background: var(--brand-red); }

/* Touch has no hover, so the per-photo actions must always be reachable. */
@media (hover: none) and (pointer: coarse) {
  .photo-thumb__actions { opacity: 1; }
  .photo-thumb__btn { width: 44px; height: 44px; }
  /* Two 44px buttons would blanket a ~90px thumbnail, leaving nowhere to tap
     the photo itself to enlarge it — give the thumbnails room to be tapped. */
  .photo-tile__grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}

.photo-tile__count { font-size: 12px; color: var(--text-secondary); }
.photo-tile__hint { margin: 0; font-size: 12px; color: var(--text-secondary); }

.photo-tile__meta { display: flex; flex-direction: column; gap: 2px; }
.photo-tile__title { font-size: 13px; font-weight: 600; line-height: 1.3; }
.photo-tile__subtitle { font-size: 12px; color: var(--text-secondary); }
.photo-tile__error { margin: 0; font-size: 12px; color: var(--brand-red); }
.photo-tile__actions { display: flex; flex-wrap: wrap; gap: 6px; }

/* ===== Read-only preview media + pills + module status (show page) ===== */
.preview-photo {
  display: block;
  max-width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.preview-photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 14px;
  margin-top: 12px;
}
.preview-photo-grid__item { margin: 0; }
.preview-photo-grid__item img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.preview-photo-grid__item figcaption {
  margin-top: 6px;
  font-size: 12px;
  color: var(--text-secondary);
}

/* ===== Full-screen photo viewer (photo_lightbox_controller) ===== */
/* Any thumbnail carrying data-lightbox-src is a trigger — the tile widget, the
   read-only previews and the device grid alike. The <img> is the button, so
   nothing here touches the sizing/cropping the surrounding layouts do. */
[data-lightbox-src] { cursor: zoom-in; }
[data-lightbox-src]:focus-visible { outline: 2px solid var(--brand-red); outline-offset: 2px; }

.photo-lightbox {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  padding: 24px;
  border: 0;
  /* The dim lives on the dialog, not ::backdrop, so the non-modal fallback
     (a browser without showModal) still renders against a dark ground. */
  background: rgba(0, 0, 0, 0.86);
}
.photo-lightbox[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}

.photo-lightbox__image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--radius-sm);
}

.photo-lightbox__close {
  position: absolute;
  top: 16px;
  right: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: var(--white);
  cursor: pointer;
}
.photo-lightbox__close:hover { background: rgba(255, 255, 255, 0.26); }

@media (max-width: 640px) {
  .photo-lightbox { padding: 12px; }
  .photo-lightbox__close { top: 10px; right: 10px; }
}

@media (hover: none) and (pointer: coarse) {
  .photo-lightbox__close { width: 44px; height: 44px; }
}

.pill-row { display: flex; flex-wrap: wrap; gap: 8px; }

.module-status-list { display: flex; flex-direction: column; gap: 10px; }
.module-status {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--white);
}
.module-status__icon { display: flex; color: var(--text-secondary); }
.module-status__name { flex: 1 1 auto; font-weight: 600; }
/* Configure / activate buttons in the preview module list — sit at the row end,
   wrap below the name on narrow (phone) widths instead of overflowing. */
.module-status__actions { margin-left: auto; }

/* Per-device curation cards (preview): photo + categories in one place. */
.device-curation-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 16px;
  margin-top: 12px;
}
.device-curation-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--white);
}
/* The photo tile drops its own chrome inside the card — the card is the frame. */
.device-curation-card .photo-tile {
  padding: 0;
  border: none;
  background: transparent;
}
.device-cat-fields {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.device-cat-widget--busy {
  opacity: 0.5;
  pointer-events: none;
}
.device-cat-widget__error {
  display: none;
  margin: 8px 0 0;
  color: var(--danger, #c0392b);
  font-size: 0.85rem;
}
.device-cat-widget--error .device-cat-widget__error {
  display: block;
}

/* ---- Ekrany tab: the TVs on a player's HDMI splitter -------------------
   Filled in on a phone, standing in the shop with the TV in front of you, so
   the single-column stack IS the primary layout; the two-column grid is what
   the desktop gets as a bonus. Cards never sit narrower than a readable form,
   which is why the minmax floor is 280px rather than the tighter floors used
   by the read-only cards elsewhere. */
.screens-count {
  margin: 0 0 14px;
  font-size: 0.9rem;
  color: var(--text-secondary);
}
.screens-count__hint { display: block; margin-top: 2px; }
.screen-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}
.screen-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--white);
  /* min-width: 0 keeps a long serial number from widening the grid track and
     pushing the page into horizontal overflow on a phone. */
  min-width: 0;
}
.screen-card--new { border-style: dashed; }
.screen-card__head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.screen-card__title { margin: 0; font-size: 1rem; flex: 1 1 auto; }
.screen-card__head .button_to { margin-left: auto; }
.screen-card__hint { margin: 0; font-size: 0.85rem; color: var(--text-secondary); }
.screen-card .form-grid { gap: 10px; }
.screen-card .form-actions { margin-top: 0; }
/* The gallery drops its own frame inside the card — the card is the frame,
   same as the per-device curation cards on the location preview. Hiding the
   border by COLOUR and only when there is no error keeps .photo-tile--error's
   red frame visible: `border: none` here would outrank it (0,2,0 vs 0,1,0) and
   a rejected upload would lose the frame the other galleries show. */
.screen-card .photo-tile {
  padding: 0;
  background: transparent;
}
.screen-card .photo-tile:not(.photo-tile--error) { border-color: transparent; }
.screen-card .dev-field__value { overflow-wrap: anywhere; }
@media (max-width: 640px) {
  .screen-cards { grid-template-columns: 1fr; }
}
/* Touch: the save/delete buttons on these cards are hit with a thumb, and so
   are the install-code buttons on the location-module page — integrators copy
   those standing at the back-office PC with a phone in hand. Base .btn is
   34px, which is below the 44px CLAUDE.md requires on touch. */
@media (hover: none) and (pointer: coarse) {
  .screen-card .btn,
  .screen-card input[type="submit"],
  .install-codes .btn { min-height: 44px; }
}

/* ============================================================
   KNOWLEDGE BASE — in-app documentation (/knowledge).
   Reading surfaces, not an operating UI: a long measure, generous leading, and
   tables that stay tabular. Reuses the shell's page-hero / badge / table-wrap;
   everything below is only what those do not already cover.
   ============================================================ */
.kb-section { margin-bottom: 34px; }
.kb-section__title {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 12px;
}
.kb-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.kb-card { margin: 0; }
.kb-card__link {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--white);
  text-decoration: none;
  color: inherit;
}
.kb-card__link:hover { border-color: var(--brand-red); }
.kb-card__title { font-size: 17px; font-weight: 650; }
.kb-card__summary { font-size: 14px; color: var(--text-secondary); line-height: 1.5; }
.kb-card__meta { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.kb-tag {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--gray-50);
  color: var(--text-secondary);
}

/* ---- article ---- */
.kb-hero__back { text-decoration: none; }
.kb-hero__actions { display: flex; align-items: flex-start; }
.kb-article { max-width: 78ch; }
.kb-block { margin-bottom: 40px; }
.kb-block h2 { font-size: 21px; margin: 0 0 12px; letter-spacing: -0.01em; }
.kb-block p { line-height: 1.65; margin: 0 0 14px; }
.kb-lede { font-size: 18px; line-height: 1.6; color: var(--text-secondary); }
.kb-note { font-size: 14.5px; color: var(--text-secondary); line-height: 1.6; }
.kb-callout {
  margin: 18px 0;
  padding: 16px 20px;
  border-radius: 10px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--brand-red);
  background: var(--white);
}
.kb-callout--warn { border-left-color: var(--red); }
.kb-callout p:last-child { margin-bottom: 0; }
.kb-callout__tail { margin-top: 10px; }
.kb-block--method { font-size: 14.5px; color: var(--text-secondary); }
/* Prose lists and a captioned screenshot inside an article. Every article image
   is fluid (max-width, auto height) — at the article level, not the figure's,
   so a screenshot dropped into a bare <p> cannot push a 390px viewport sideways
   either; width/height attributes on the <img> reserve the box. */
.kb-block ol,
.kb-block ul { margin: 0 0 14px; padding-left: 22px; line-height: 1.65; }
.kb-block li { margin-bottom: 6px; }
.kb-article img { display: block; max-width: 100%; height: auto; }
.kb-figure { margin: 18px 0 20px; }
.kb-figure img { border: 1px solid var(--border); border-radius: var(--radius-sm); }
.kb-figure figcaption { margin-top: 8px; font-size: 13.5px; line-height: 1.5; color: var(--text-secondary); }

/* The article body is capped at a prose measure, but its tables need the full
   column back rather than being trapped in 78ch. */
.kb-article .table-wrap { max-width: none; margin: 16px 0 8px; }
.kb-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.kb-table th,
.kb-table td { padding: 8px 12px; text-align: left; white-space: nowrap; }
.kb-table thead th {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--border);
  vertical-align: bottom;
}
.kb-table tbody td { border-bottom: 1px solid var(--gray-100); }
.kb-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.kb-table .muted { color: var(--text-secondary); }
/* nowrap above is for the numeric matrices. A prose column (the "why" of a
   settings table) has to wrap, or between the 880px scroll breakpoint and
   ~940px the table overruns its wrap and the page scrolls sideways. Opt-in
   class, so .muted stays a colour and the matrices' muted numbers keep nowrap. */
.kb-table .wrap { white-space: normal; }
.kb-table__name { font-weight: 650; }
/* A rule where the resolution changes, so a 15-row matrix reads as five groups
   of panels instead of one undifferentiated block. */
.kb-table__band td { border-top: 1px solid var(--border); }

/* ---- calculator ---- */
.kb-calc {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--white);
  overflow: hidden;
  margin: 18px 0;
}
.kb-calc__inputs {
  padding: 18px 20px;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.kb-field { display: flex; flex-direction: column; gap: 5px; }
.kb-field > span {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.kb-field select { width: 100%; max-width: none; }
.kb-calc__out { padding: 18px 20px; }
.kb-calc__verdict { font-size: 21px; font-weight: 700; margin: 0 0 4px; letter-spacing: -0.01em; }
.kb-calc__sub { font-size: 14.5px; color: var(--text-secondary); margin: 0 0 14px; }
.kb-calc[data-state="pass"] .kb-calc__verdict { color: #1c6b45; }
.kb-calc[data-state="warn"] .kb-calc__verdict { color: #8c6110; }
.kb-calc[data-state="fail"] .kb-calc__verdict { color: #a22e1c; }
.kb-calc[data-state="unknown"] .kb-calc__verdict { color: var(--text-secondary); }
.kb-calc__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 12px;
  margin: 0;
}
.kb-calc__facts dt {
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 2px;
}
.kb-calc__facts dd { margin: 0; font-size: 16px; font-variant-numeric: tabular-nums; }
.kb-calc__rec { margin: 14px 0 0; font-size: 14px; color: var(--text-secondary); }

@media (max-width: 880px) {
  .kb-calc { grid-template-columns: 1fr; }
  .kb-calc__inputs { border-right: 0; border-bottom: 1px solid var(--border); }
  .kb-article { max-width: none; }
}

@media (hover: none) and (pointer: coarse) {
  .kb-field select { min-height: 44px; }
  .kb-card__link { min-height: 44px; }
  .kb-hero__actions .btn { min-height: 44px; }
}

/* ---- print ----
   Printing IS the delivery path for these articles: they are not shared by
   link, they get printed to PDF and attached to an offer. So this block is a
   requirement rather than a nicety — drop the app shell, force a light ground
   whatever theme the reader is on, and never break a reach table across pages.

   EVERY selector is scoped to `body.kb-printable` (set by knowledge/show via
   content_for :body_class). Unscoped, this is the app's only @media print block
   and would silently rewrite how every other page prints — most damagingly
   `.table-wrap { break-inside: avoid; overflow: visible }`, which CLAUDE.md
   mandates as the wrapper for ALL wide tables, so a long devices list would be
   forced onto one page and clipped instead of paginating. */
@media print {
  body.kb-printable { background: #fff !important; color: #000 !important; }
  body.kb-printable .sidebar,
  body.kb-printable .topbar,
  body.kb-printable .nav-scrim,
  body.kb-printable .kb-hero__actions,
  body.kb-printable .flash { display: none !important; }
  body.kb-printable .kb-article { max-width: none; }
  body.kb-printable .kb-calc,
  body.kb-printable .kb-callout,
  body.kb-printable .kb-table,
  body.kb-printable .kb-figure,
  body.kb-printable .kb-block ol,
  body.kb-printable .kb-block ul,
  body.kb-printable .table-wrap { break-inside: avoid; }
  /* No avoid-page on the whole .kb-block: a block with a list, a table and a
     screenshot is taller than a page, and the rule then pushed it whole onto the
     next sheet (leaving the first ~80% blank) before splitting it anyway. The
     per-child guards above plus the h2 rule keep the un-splittable units whole. */
  body.kb-printable .kb-block h2 { break-after: avoid; }
  body.kb-printable .table-wrap { overflow: visible !important; }
  body.kb-printable .kb-table { font-size: 11px; }
  body.kb-printable .kb-table th,
  body.kb-printable .kb-table td { padding: 4px 8px; }
  /* The calculator is interactive; on paper it should read as the case that was
     stated, not as a row of dead dropdowns. */
  body.kb-printable .kb-calc__inputs { display: none !important; }
  body.kb-printable .kb-calc { grid-template-columns: 1fr; border-color: #999; }
  body.kb-printable a[href]::after { content: ""; }
}
