/*
 * Design tokens ported verbatim from the React app's src/index.css, so the
 * server-rendered pages look pixel-identical to the current app. These are
 * plain CSS custom properties - Tailwind (loaded via CDN in
 * partials/header.inc.php) maps its color/radius/font utilities onto them,
 * exactly like tailwind.config.js did for the Vite build.
 */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

:root {
  --background: 210 20% 98%;
  --foreground: 220 20% 15%;
  --card: 0 0% 100%;
  --card-foreground: 220 20% 15%;
  --popover: 0 0% 100%;
  --popover-foreground: 220 20% 15%;
  --primary: 210 70% 45%;
  --primary-foreground: 0 0% 100%;
  --secondary: 0 0% 75%;
  --secondary-foreground: 220 20% 15%;
  --muted: 210 15% 95%;
  --muted-foreground: 220 10% 50%;
  --accent: 210 60% 92%;
  --accent-foreground: 210 70% 35%;
  --destructive: 0 72% 51%;
  --destructive-foreground: 0 0% 98%;
  --border: 210 15% 90%;
  --input: 210 15% 90%;
  --ring: 210 70% 45%;
  --chart-1: 210 70% 45%;
  --chart-2: 0 0% 60%;
  --chart-3: 220 20% 25%;
  --chart-4: 43 74% 66%;
  --chart-5: 210 50% 65%;
  --radius: 0.5rem;
  --font-heading: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --font-body: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --font-display: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  --sidebar-background: 220 20% 18%;
  --sidebar-foreground: 210 15% 85%;
  --sidebar-primary: 210 70% 50%;
  --sidebar-primary-foreground: 0 0% 100%;
  --sidebar-accent: 220 18% 24%;
  --sidebar-accent-foreground: 0 0% 100%;
  --sidebar-border: 220 15% 25%;
  --sidebar-ring: 210 70% 50%;
}

.dark {
  --background: 220 20% 10%;
  --foreground: 0 0% 95%;
  --card: 220 20% 14%;
  --card-foreground: 0 0% 95%;
  --popover: 220 20% 14%;
  --popover-foreground: 0 0% 95%;
  --primary: 210 70% 50%;
  --primary-foreground: 0 0% 100%;
  --secondary: 0 0% 30%;
  --secondary-foreground: 0 0% 95%;
  --muted: 220 15% 18%;
  --muted-foreground: 210 10% 55%;
  --accent: 220 18% 24%;
  --accent-foreground: 0 0% 95%;
  --destructive: 0 62.8% 30.6%;
  --destructive-foreground: 0 0% 98%;
  --border: 220 15% 22%;
  --input: 220 15% 22%;
  --ring: 210 70% 50%;
  --chart-1: 210 70% 50%;
  --chart-2: 0 0% 60%;
  --chart-3: 30 80% 55%;
  --chart-4: 280 65% 60%;
  --chart-5: 340 75% 55%;
  --sidebar-background: 220 25% 10%;
  --sidebar-foreground: 210 15% 85%;
  --sidebar-primary: 210 70% 50%;
  --sidebar-primary-foreground: 0 0% 100%;
  --sidebar-accent: 220 18% 16%;
  --sidebar-accent-foreground: 0 0% 95%;
  --sidebar-border: 220 15% 18%;
  --sidebar-ring: 210 70% 50%;
}

body {
  background-color: hsl(var(--background));
  color: hsl(var(--foreground));
  font-family: var(--font-body);
}

* {
  border-color: hsl(var(--border));
}

/*
 * Vanilla-JS modal/dropdown idiom, mirrors CTS Booking's
 * assets/js/custom_modals.js (.cc-overlay/.cc-active toggle+transition) -
 * see static/js/ui.js for the JS side.
 */
.ui-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
  z-index: 9000;
}
.ui-overlay.ui-active {
  opacity: 1;
  pointer-events: auto;
}
.ui-dialog {
  background: #ffffff !important;
  background-color: #ffffff !important;
  color: #0f172a !important;
  border-radius: 1rem;
  border: 1px solid #e2e8f0;
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5), 0 10px 15px -3px rgba(0, 0, 0, 0.2);
  transform: scale(0.96) translateY(8px);
  transition: transform 0.15s ease;
  max-height: 90vh;
  overflow-y: auto;
  position: relative;
  z-index: 9001;
}
.dark .ui-dialog {
  background: #0f172a !important;
  background-color: #0f172a !important;
  color: #f8fafc !important;
  border-color: #334155;
}
.ui-overlay.ui-active .ui-dialog {
  transform: scale(1) translateY(0);
}
/* The cross added to a dialog that has none of its own (ui.js, limsEnsureDialogClose). */
.ui-dialog > .lims-dialog-x {
  position: absolute;
  top: .6rem;
  right: .6rem;
  z-index: 2;
  width: 2rem;
  height: 2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 9999px;
  color: hsl(var(--muted-foreground));
}
.ui-dialog > .lims-dialog-x:hover {
  background: hsl(var(--muted));
  color: hsl(var(--foreground));
}

.ui-dropdown-menu {
  position: absolute;
  background: hsl(var(--popover));
  color: hsl(var(--popover-foreground));
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius);
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
  display: none;
  z-index: 90;
  min-width: 10rem;
  padding: 4px;
}
.ui-dropdown-menu.ui-open {
  display: block;
}
.ui-dropdown-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 10px;
  border-radius: calc(var(--radius) - 2px);
  font-size: 14px;
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: none;
  color: inherit;
}
.ui-dropdown-item:hover {
  background: hsl(var(--muted));
}

/* Hover help ("?" icons). The bubble is a body-level, position:fixed element
   placed by static/js/tooltip.js - it cannot be a child of the trigger
   because .ui-dialog is overflow-y:auto and would clip it. */
.ui-help-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  flex-shrink: 0;
  color: #9ca3af;
  cursor: help;
}
.ui-help-icon svg {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}
.ui-help-icon:hover,
.ui-help-icon:focus-visible {
  color: #6b7280;
  outline: none;
}
.ui-help-bubble {
  position: fixed;
  z-index: 2147483647;
  max-width: 24rem;
  width: max-content;
  min-width: 14rem;
  padding: 8px 12px;
  border-radius: 6px;
  background: #1e293b;
  color: #f8fafc;
  font-size: 12px;
  line-height: 1.45;
  box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4), 0 8px 10px -6px rgba(0, 0, 0, 0.2);
  pointer-events: none;
  border: 1px solid rgba(255, 255, 255, 0.15);
  animation: limsHelpFadeIn 0.15s ease-out;
}
@keyframes limsHelpFadeIn {
  from { opacity: 0; transform: translateY(-3px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Row a notification points at (issue #19): when a test/request list is
   opened from a notification link the target row flashes so it stands out
   among many. Applied by the ?open= / ?phase_prompt= deep-link handlers. */
/* Amber - the same colour the metrology registers use for a calibration that
   is close to expiry (issue #19, Denisa). */
@keyframes lims-row-flash {
  0%, 100% { background-color: transparent; }
  50%      { background-color: rgba(245, 158, 11, 0.32); }
}
/* Blink for ~4s (issue #19, Denisa: "rozblikanie nechaj po dobu 4 sekúnd"). */
tr.lims-row-flash > td {
  animation: lims-row-flash 1s ease-in-out 4;
}
/* The left marker stripe is not time-limited - it stays until the user clicks
   anywhere on the page or reloads (issue #19, Denisa). Cleared by detail_row.js. */
tr.lims-row-marked > td:first-child {
  box-shadow: inset 3px 0 0 0 #f59e0b;
}
@media (prefers-reduced-motion: reduce) {
  tr.lims-row-flash > td { animation: none; background-color: rgba(245, 158, 11, 0.22); }
}

/* Notification read/unread left bar (issue #19). A plain inset box-shadow, not
   Tailwind side-colour utilities - `border-l-<color>` did not render on the
   Play CDN build, so the distinction never showed. */
.lims-notif-unread { box-shadow: inset 3px 0 0 0 var(--primary, #2563eb); }
.lims-notif-read   { box-shadow: inset 3px 0 0 0 rgba(100, 116, 139, 0.55); }

/* Sticky table header, all data-resizable="..." register/list tables
   (Peter, 11. 9. 2026 - first noticed on equipment_register.php, then asked
   for it "on almost every table"). Every one of them sits in the same
   `.flex-1.overflow-auto` wrapper, so `position: sticky; top: 0` pins the
   header to that wrapper's own scroll, not the page's. Full opacity (not a
   row's own bg-muted/70) is intentional - a sticky header needs to be
   opaque or scrolled rows show through underneath it.
   Column-filter tables get it too: the samples table drops data-resizable in
   its compact view ("Zobraziť všetky údaje" off), and the header then scrolled
   away with the rows (Peter, 9. 10. 2026, #151). */
table[data-resizable] thead th,
table[data-col-filter] thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background-color: hsl(var(--muted));
}

table:not(.kpi-table):not(.kpi-heatmap-table):not(.capacity-table) thead th {
  text-align: center;
  vertical-align: middle;
}

table.kpi-table thead th,
table.kpi-table thead th:first-child,
table.kpi-heatmap-table thead th,
table.kpi-heatmap-table thead th.kpi-heat-col-name,
table.capacity-table thead th,
table.capacity-table thead th.sticky-col {
  text-align: center;
  vertical-align: middle;
}

table:not(.kpi-table):not(.kpi-heatmap-table):not(.capacity-table) tbody td {
  text-align: center;
  vertical-align: middle;
}

table.kpi-table tbody td,
table.kpi-table tbody td:first-child,
table.kpi-heatmap-table tbody td,
table.kpi-heatmap-table tbody td.kpi-heat-col-name,
table.capacity-table tbody td,
table.capacity-table tbody td.sticky-col {
  text-align: center;
  vertical-align: middle;
}

table tbody th[scope="row"] {
  text-align: center;
  vertical-align: middle;
}

table tbody td > .flex:not(.flex-col) {
  justify-content: center;
}

table tbody td > .flex.flex-col {
  align-items: center;
}

table tbody th[scope="row"] > .flex:not(.flex-col) {
  justify-content: center;
}

table tbody th[scope="row"] > .flex.flex-col {
  align-items: center;
}

table.plan-tabula tbody th.plan-zariadenie-info .plan-zariadenie-nazov {
  text-align: center;
}

table.plan-tabula tbody th.plan-zariadenie-info .plan-telem-text-badge {
  align-items: center;
}

table.plan-tabula tbody th.plan-zariadenie-info > div.text-xs {
  padding-left: 0;
}

table.capacity-table .cap-th-label-row {
  position: relative;
  justify-content: center;
}

table.capacity-table .cap-th-filter-btn {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
}

table.capacity-table td .cap-people-chips {
  justify-content: center;
}

#rez-meradla-wrap {
  max-height: 10rem;
}

.rez-meter-group {
  grid-column: 1 / -1;
  padding: 6px 8px;
  border: 1px solid hsl(var(--border) / 0.7);
  border-radius: 8px;
  background: hsl(var(--background) / 0.55);
}

.rez-meter-group-title {
  margin: 0 0 4px;
  color: hsl(var(--foreground));
  font-size: 11px;
  font-weight: 600;
}

.rez-meter-group-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 4px;
}

@media (max-width: 640px) {
  #rez-meradla-wrap .rez-meter-group-list {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Invalid-field highlight, porting the React forms' inp(errors.x) helper.
   Set/cleared by static/js/form_validate.js. */
.ui-invalid {
  border-color: #ef4444 !important;
  background-color: #fef2f2;
}
.ui-invalid:focus {
  border-color: #ef4444 !important;
  outline: none;
}

/* Column resize grips, added by static/js/table_resize.js. Sits above the
   full-bleed sort <a> inside each header cell so the drag wins over the
   sort click. */
.ui-col-resize {
  position: absolute;
  top: 0;
  right: 0;
  width: 6px;
  height: 100%;
  cursor: col-resize;
  user-select: none;
  z-index: 5;
}
.ui-col-resize:hover,
.ui-col-resize.is-dragging {
  background: hsl(var(--primary) / 0.45);
}
body.ui-resizing {
  cursor: col-resize;
  user-select: none;
}

/* Per-column filter row, added below the header label by
   static/js/table_col_filter.js. The <th>'s own content is moved into this
   wrapper, which stacks it into 2 rows - the existing sort link / label on
   top, the filter row underneath - so the funnel never crowds the label
   text, however narrow the column gets. The <th> itself is left alone
   (still `display: table-cell`); overriding that broke the whole <tr> into
   one column of stacked cells instead of a row. */
.ui-col-filter-wrap {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  box-sizing: border-box;
  text-align: center;
}
table[data-col-filter] thead th {
  text-align: center;
}
.ui-col-filter-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  width: 100%;
  box-sizing: border-box;
  gap: 3px;
  min-height: 22px;
  padding: 2px 10px 5px;
  cursor: pointer;
  font-weight: 400;
}
/* Center the idle filter hint beneath each header; once filters are active,
   the chips start at the left and the flexing chip group pushes clear-all
   to the right. */
.ui-col-filter-toggle {
  display: flex;
  align-items: center;
  gap: 5px;
  color: hsl(var(--muted-foreground));
  opacity: 0.6;
}
.ui-col-filter-row:hover .ui-col-filter-toggle,
.ui-col-filter-row.is-open .ui-col-filter-toggle {
  opacity: 1;
  color: hsl(var(--primary));
}
.ui-col-filter-row.is-active .ui-col-filter-toggle {
  opacity: 1;
  color: hsl(var(--primary));
}
.ui-col-filter-toggle svg {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
}
.ui-col-filter-toggle span {
  font-size: 11px;
  white-space: nowrap;
}
.ui-col-filter-chips {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 3px;
  flex: 1;
  min-width: 0;
}
.ui-col-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  max-width: 110px;
  padding: 1px 4px 1px 7px;
  border-radius: 999px;
  background: hsl(var(--primary) / 0.14);
  color: hsl(var(--primary));
  font-size: 11px;
  font-weight: 500;
  white-space: nowrap;
}
.ui-col-filter-chip span {
  overflow: hidden;
  text-overflow: ellipsis;
}
.ui-col-filter-chip-x {
  border: none;
  background: transparent;
  color: inherit;
  opacity: 0.6;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 0 2px;
}
.ui-col-filter-chip-x:hover {
  opacity: 1;
}
.ui-col-filter-more {
  font-size: 11px;
  font-weight: 500;
  color: hsl(var(--primary));
  white-space: nowrap;
}
.ui-col-filter-clearall {
  border: none;
  background: transparent;
  color: hsl(var(--muted-foreground));
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 1px 2px;
  flex-shrink: 0;
}
.ui-col-filter-clearall:hover {
  color: hsl(var(--destructive, 0 70% 50%));
}

.ui-col-filter-pop {
  position: absolute;
  z-index: 50;
  display: flex;
  flex-direction: column;
  width: 220px;
  max-width: calc(100vw - 8px);
  background: hsl(var(--popover));
  border: 1px solid hsl(var(--border));
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  padding: 8px;
  gap: 6px;
  cursor: default;
}
.ui-col-filter-input {
  width: 100%;
  box-sizing: border-box;
  font-size: 12px;
  padding: 5px 8px;
  border: 1px solid hsl(var(--border));
  border-radius: 6px;
  background: hsl(var(--card));
  color: hsl(var(--foreground));
  outline: none;
}
.ui-col-filter-input:focus {
  border-color: hsl(var(--primary));
}
.ui-col-filter-options {
  max-height: 190px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  border-top: 1px solid hsl(var(--border));
  border-bottom: 1px solid hsl(var(--border));
  padding: 4px 0;
}
.ui-col-filter-option {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 4px;
  border-radius: 4px;
  font-size: 12px;
  color: hsl(var(--foreground));
  cursor: pointer;
}
.ui-col-filter-option:hover {
  background: hsl(var(--muted));
}
.ui-col-filter-option input[type="checkbox"] {
  flex-shrink: 0;
  accent-color: hsl(var(--primary));
}
.ui-col-filter-option span.ui-col-filter-optlabel {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ui-col-filter-option span.ui-col-filter-optcount {
  color: hsl(var(--muted-foreground));
  font-size: 11px;
  flex-shrink: 0;
}
.ui-col-filter-options-empty {
  padding: 6px 4px;
  font-size: 12px;
  color: hsl(var(--muted-foreground));
}
.ui-col-filter-pop-footer {
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
.ui-col-filter-clearall-pop,
.ui-col-filter-selectall-pop {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 12px;
  padding: 2px 4px;
}
.ui-col-filter-clearall-pop {
  color: hsl(var(--muted-foreground));
}
.ui-col-filter-selectall-pop {
  color: hsl(var(--primary));
}
.ui-col-filter-clearall-pop:hover,
.ui-col-filter-selectall-pop:hover {
  color: hsl(var(--foreground));
  text-decoration: underline;
}

html, body {
  overflow-x: hidden;
  max-width: 100vw;
}

/* Paint the theme background on <html> too, not just <body>. Otherwise every
   full-page navigation (e.g. clicking a sort-column header) shows a white
   flash while the browser has unloaded the old page but not yet painted the
   new <body>. */
html {
  background-color: hsl(var(--background));
}

/* Same-origin navigations (sort-column clicks, pagination, menu links reload
   the page) cross-fade instead of flashing. Progressive enhancement - browsers
   without the View Transitions API just navigate normally. */
@view-transition {
  navigation: auto;
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* Shared frame of the logged-in app. On a larger screen the sidebar can be
   hidden with the topbar button; on mobile it becomes a slide-out panel. */
.lims-app-shell {
  position: relative;
  min-width: 0;
  max-width: 100vw;
  overflow-x: hidden;
}
.lims-main {
  min-width: 0;
  min-height: 0;
}
.lims-sidebar {
  width: 16rem;
  min-width: 16rem;
  overflow-x: hidden !important;
  transition: width .2s ease, min-width .2s ease, transform .2s ease, border-color .2s ease;
}
.lims-nav-backdrop { display: none; }

/* Desktop: the panel hides with its own button in its header (the topbar
   hamburger is for phones), and while it is hidden a strip at the left edge
   shows it again. The strip is invisible until the pointer comes within ~140px
   of the edge, then grows with the pointer (ui.js sets --peek from 0 to 1) and
   carries one arrow; a click anywhere on it opens the panel (#150). */
.lims-nav-peek { display: none; }
@media (min-width: 768px) {
  .lims-nav-hide { display: flex !important; }
  .lims-nav-toggle { display: none; }
  .lims-app-shell.lims-nav-collapsed .lims-nav-peek {
    display: block;
    position: fixed;
    left: 0;
    top: 4rem;
    bottom: 0;
    width: 2.25rem;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    z-index: 60;
  }
  .lims-nav-peek-bar {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: calc(var(--peek, 0) * 1.75rem);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    background: hsl(var(--sidebar-background));
    color: hsl(var(--sidebar-foreground));
    box-shadow: 4px 0 12px rgba(15, 23, 42, calc(var(--peek, 0) * .25));
    transition: width .08s linear;
  }
  .lims-nav-peek-bar > svg { opacity: calc((var(--peek, 0) - .35) * 1.6); flex-shrink: 0; }
  .lims-nav-peek:hover .lims-nav-peek-bar,
  .lims-nav-peek:focus-visible .lims-nav-peek-bar { width: 1.75rem; }
  .lims-nav-peek:hover .lims-nav-peek-bar > svg,
  .lims-nav-peek:focus-visible .lims-nav-peek-bar > svg { opacity: 1; }
}
/* A touch screen has no pointer to approach with: keep a thin strip to tap. */
@media (min-width: 768px) and (hover: none) {
  .lims-app-shell.lims-nav-collapsed .lims-nav-peek-bar { width: 1.25rem; }
  .lims-app-shell.lims-nav-collapsed .lims-nav-peek-bar > svg { opacity: 1; }
}
.lims-app-shell.lims-nav-collapsed .lims-sidebar {
  width: 0 !important;
  min-width: 0 !important;
  overflow: hidden;
  border-right-color: transparent;
  transform: translateX(-100%);
}
.lims-app-shell.lims-nav-collapsed .lims-sidebar > * {
  visibility: hidden;
}

/* Every existing overflow-auto table can be swiped sideways on a phone. We
   do not shrink tables into unreadable cards or break their fixed columns
   or the resize handles. */
.lims-main .overflow-auto {
  max-width: 100%;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}

/* Time-tracker: the pill lives in the topbar; its mirror lives in the profile
   dropdown. Only one shows at a time - the pill on wide viewports, the menu
   item once the topbar runs out of room (~900px). */
.lims-tt-menu-item,
.lims-tt-menu-sep { display: none; }

@media (max-width: 900px) {
  .lims-tt-pill { display: none; }
  .lims-tt-menu-item { display: flex; }
  .lims-tt-menu-sep { display: block; }
}

@media (max-width: 767px) {
  /* The off-canvas sidebar sits at a negative x (translateX(-105%)); its box
     still counts toward the document's scroll area, letting the page drift
     sideways. Clip it at the root - nothing on mobile needs page-level
     horizontal scroll (wide tables scroll inside their own box). */
  html, body { overflow-x: hidden; }

  /* `100vh` on a phone often includes the area behind the address bar. The
     dynamic height keeps the planning workspace exactly between the topbar
     and the bottom of the window. */
  .lims-app-shell { height: calc(100dvh - 4rem) !important; }
  .lims-topbar { padding-left: .5rem; padding-right: .5rem; }
  .lims-topbar-brand,
  .lims-topbar-actions { min-width: 0; gap: .25rem; }
  .lims-topbar-logo { height: 2rem; max-width: 72px; }
  .lims-topbar-title { font-size: 1rem; white-space: nowrap; }
  .lims-language-code { display: none; }
  .lims-topbar-actions > button { padding: .45rem; }

  .lims-sidebar {
    position: fixed;
    top: 4rem;
    bottom: 0;
    left: 0;
    z-index: 80;
    width: min(18rem, calc(100vw - 3.25rem)) !important;
    min-width: min(18rem, calc(100vw - 3.25rem)) !important;
    box-shadow: 12px 0 30px rgba(15, 23, 42, .24);
    transform: translateX(-105%);
  }
  .lims-sidebar > * { visibility: visible !important; }
  .lims-app-shell.lims-nav-collapsed .lims-sidebar { transform: translateX(-105%); }
  .lims-app-shell.lims-nav-open .lims-sidebar { transform: translateX(0); }
  .lims-nav-close { display: block !important; }
  .lims-nav-backdrop {
    position: fixed;
    inset: 4rem 0 0;
    z-index: 70;
    display: block;
    border: 0;
    background: rgba(15, 23, 42, .42);
    opacity: 0;
    pointer-events: none;
    transition: opacity .2s ease;
  }
  .lims-app-shell.lims-nav-open .lims-nav-backdrop {
    opacity: 1;
    pointer-events: auto;
  }
  .lims-main {
    overflow-x: hidden;
  }
  /* Planning has its own scrolling calendar. Without this rule the whole
     document scrolled on mobile and the grid had no height left after the
     filters and the bottom bar. */
  #lims-main:has(#plan-main) { overflow: hidden; }
  #plan-main { width: 100%; min-width: 0; overflow: hidden; }
  .lims-main > .p-6 { padding: 1rem; }
  .lims-main table { max-width: none; }

  /* Page-header toolbars (title + filter dropdown + search + action button)
     are a non-wrapping flex row - on a phone that forced #lims-main wider
     than the screen, and overflow-x:hidden then clipped the search box and
     the action button out of reach. Let them wrap and let the inputs shrink. */
  .lims-main > .p-6 > [class*="mb-"] .flex.items-center,
  .lims-main > .p-6 form.flex {
    flex-wrap: wrap;
    row-gap: .5rem;
  }
  .lims-main > .p-6 form.flex > *,
  .lims-main > .p-6 form.flex input {
    min-width: 0;
  }
  .lims-main > .p-6 form.flex input:not([type="checkbox"]):not([type="radio"]) {
    flex: 1 1 9rem;
    width: auto;
  }
}

/* Toast notification host - must always float in foreground above any active modal overlay or dialog */
#lims-toast-host {
  position: fixed !important;
  top: 20px !important;
  left: 50% !important;
  transform: translateX(-50%) !important;
  z-index: 2147483647 !important;
  pointer-events: none !important;
  display: flex !important;
  flex-direction: column !important;
  gap: 8px !important;
  align-items: center !important;
}
#lims-toast-host > div {
  pointer-events: auto !important;
}
@keyframes limsToastIn {
  from { opacity: 0; transform: translateY(-8px) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* Red asterisks for mandatory form fields */
.req-star, .form-asterisk-required {
  color: #ef4444 !important;
  font-weight: 700 !important;
  margin-left: 2px !important;
  display: inline-block !important;
}

/* Searchable Categorized Dropdown (Zariadenia / Meradlá) */
.sc-native-select {
  display: none !important;
}
.sc-dropdown-container {
  position: relative;
  width: 100%;
}
.sc-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background-color: #ffffff;
  border: 1px solid #d1d5db;
  border-radius: 0.375rem;
  padding: 0.375rem 0.625rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: #1f2937;
  text-align: left;
  cursor: pointer;
  min-height: 34px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.sc-trigger:hover {
  border-color: #9ca3af;
  background-color: #f9fafb;
}
.sc-trigger:focus-visible, .sc-trigger.sc-focused {
  outline: none;
  border-color: #16a34a;
  box-shadow: 0 0 0 2px rgba(22, 163, 74, 0.2);
}
.sc-trigger.ui-invalid {
  border-color: #ef4444 !important;
  box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.25) !important;
  background-color: #fef2f2 !important;
}
.sc-popover {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 4px);
  z-index: 100;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 0.5rem;
  box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15), 0 8px 10px -6px rgba(0, 0, 0, 0.08);
  min-width: 280px;
  max-height: 340px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: scPopoverFadeIn 0.15s ease-out;
}
/* Tailwind's `.hidden` (display: none) and this rule's own `display: flex`
   above are both single-class selectors - equal specificity, so whichever
   stylesheet loads second wins regardless of which element actually has
   `.hidden` applied. Since tailwind-built.css loads before this file, its
   `.hidden` always lost: every equipment/fixture/instrument picker popover
   in create_test_dialog.inc.php rendered permanently open, `hidden` class or
   not (found 19.9.2026 - "prečo je tu rozbalené okno?"). Two-class selector
   here outranks a one-class Tailwind utility regardless of load order. */
.sc-popover.hidden {
  display: none;
}
.sc-popover.sc-popover-top {
  top: auto;
  bottom: calc(100% + 4px);
}
@keyframes scPopoverFadeIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}
.sc-search-wrap {
  padding: 0.5rem;
  background-color: #f8fafc;
  border-bottom: 1px solid #e2e8f0;
  position: sticky;
  top: 0;
  z-index: 2;
}
.sc-search-input {
  width: 100%;
  padding: 0.375rem 1.75rem 0.375rem 1.85rem;
  font-size: 0.8125rem;
  border: 1px solid #cbd5e1;
  border-radius: 0.375rem;
  background-color: #ffffff;
  color: #0f172a;
}
.sc-search-input:focus {
  outline: none;
  border-color: #16a34a;
  box-shadow: 0 0 0 1px #16a34a;
}
.sc-search-icon {
  position: absolute;
  left: 0.95rem;
  top: 50%;
  transform: translateY(-50%);
  color: #94a3b8;
  pointer-events: none;
}
.sc-category-header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.375rem 0.625rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: #334155;
  background-color: #f1f5f9;
  border: none;
  border-radius: 0.25rem;
  cursor: pointer;
  user-select: none;
  transition: background-color 0.12s ease;
}
.sc-category-header:hover {
  background-color: #e2e8f0;
}
.sc-cat-arrow {
  transition: transform 0.2s ease;
}
.sc-cat-expanded .sc-cat-arrow {
  transform: rotate(90deg);
}
/* A station group (e.g. "IDAS stanica 3") nested inside its family group
 * ("IDAS stanice") - lighter header + left indent so the two levels read as
 * a hierarchy, not a flat list (issue #82, 23. 9. 2026). */
.sc-cat-items-nested {
  padding-left: 0.5rem;
}
.sc-category-group-nested {
  border-color: transparent;
}
.sc-category-group-nested > .sc-category-header {
  background-color: transparent;
  font-weight: 500;
}
.sc-category-group-nested > .sc-category-header:hover {
  background-color: #f1f5f9;
}
.sc-item {
  padding: 0.4rem 0.625rem;
  font-size: 0.8125rem;
  cursor: pointer;
  border-radius: 0.25rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  transition: background-color 0.1s ease;
}
/* Same Tailwind-`.hidden`-vs-own-`display` collision as .sc-popover above:
   create_test.js's search filter toggles `.hidden` on non-matching rows, but
   without this override that class was a no-op and every row stayed visible
   regardless of the search text (found 19.9.2026 alongside the popover bug). */
.sc-item.hidden {
  display: none;
}
.sc-item:hover, .sc-item.sc-highlighted {
  background-color: #f0fdf4;
  color: #166534;
}
.sc-item.sc-selected {
  background-color: #ecfdf5;
  color: #065f46;
  font-weight: 500;
}
.sc-code-badge {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-weight: 600;
  font-size: 0.75rem;
  padding: 0.125rem 0.375rem;
  border-radius: 0.25rem;
  background-color: #f1f5f9;
  border: 1px solid #e2e8f0;
  color: #1e293b;
  flex-shrink: 0;
}

.ped-attach-kind-picker {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.25rem;
  padding: 0.25rem;
  border: 1px solid hsl(var(--border));
  border-radius: 0.625rem;
  background: hsl(var(--muted) / 0.35);
}
.ped-attach-kind {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-width: 0;
  min-height: 2.5rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid transparent;
  border-radius: 0.45rem;
  background: transparent;
  color: hsl(var(--muted-foreground));
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.ped-attach-kind:hover {
  background: hsl(var(--card));
  color: hsl(var(--foreground));
}
.ped-attach-kind.is-active {
  border-color: hsl(var(--border));
  background: hsl(var(--card));
  color: hsl(var(--primary));
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.08);
}
.ped-attach-kind:focus-visible {
  outline: 2px solid hsl(var(--ring));
  outline-offset: 2px;
}
.ped-attach-panel {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
  min-width: 0;
}
.ped-attach-panel.hidden {
  display: none;
}
@media (min-width: 640px) {
  .ped-attach-panel {
    flex-direction: row;
    align-items: center;
  }
}

.ui-table-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.65rem 0.85rem;
  border: 1px solid hsl(var(--border));
  border-top: 0;
  border-radius: 0 0 0.75rem 0.75rem;
  background: hsl(var(--card));
  color: hsl(var(--muted-foreground));
  font-size: 0.75rem;
}
.ui-table-pagination-controls {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.ui-table-pagination-size,
.ui-table-pagination-button {
  min-height: 2rem;
  padding: 0.25rem 0.55rem;
  border: 1px solid hsl(var(--border));
  border-radius: 0.4rem;
  background: hsl(var(--background));
  color: hsl(var(--foreground));
  font: inherit;
}
.ui-table-pagination-button:not(:disabled) { cursor: pointer; }
.ui-table-pagination-button:not(:disabled):hover { background: hsl(var(--muted)); }
.ui-table-pagination-button:disabled { opacity: 0.45; cursor: not-allowed; }
@media (max-width: 520px) {
  .ui-table-pagination { align-items: flex-start; flex-direction: column; }
  .ui-table-pagination-controls { width: 100%; flex-wrap: wrap; }
}


/* Row the user marked by clicking it (issue #103, Denisa point 16). Stays until
   clicked again or Esc. !important: the row hover utility and per-category
   cell backgrounds would otherwise win over the mark. */
tr.lims-row-picked > td {
  background-color: rgba(59, 130, 246, 0.14) !important;
}
tr.lims-row-picked > td:first-child {
  box-shadow: inset 4px 0 0 0 #3b82f6;
}

/* Group heading inside a column-filter popover (e.g. Owner / Test support /
   Equipment in the "Priradené" column). */
.ui-col-filter-group {
  padding: 6px 4px 2px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: hsl(var(--muted-foreground));
}

/* Searchable select (static/js/searchable_select.js, #103 point 27). The button
   takes the original select's classes; the panel floats under it. */
.lims-ss { position: relative; display: block; width: 100%; }
.lims-ss-btn { width: 100%; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 8px center; padding-right: 26px; }
.lims-ss-panel { position: absolute; z-index: 60; left: 0; right: 0; top: calc(100% + 2px); background: hsl(var(--card));
  border: 1px solid hsl(var(--border)); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.16); padding: 6px; min-width: 12rem; }
.lims-ss-search { width: 100%; height: 32px; padding: 0 8px; margin-bottom: 4px; border: 1px solid hsl(var(--input)); border-radius: 6px;
  background: hsl(var(--background)); font-size: 13px; outline: none; }
.lims-ss-search:focus { border-color: hsl(var(--primary)); }
.lims-ss-list { max-height: 240px; overflow-y: auto; margin: 0; padding: 0; list-style: none; }
.lims-ss-list li { padding: 5px 8px; font-size: 13px; border-radius: 5px; cursor: pointer; }
.lims-ss-list li.lims-ss-active, .lims-ss-list li:hover { background: hsl(var(--primary) / 0.12); }
.lims-ss-list li.lims-ss-selected { font-weight: 600; }
.lims-ss-list li.lims-ss-disabled { opacity: .45; cursor: not-allowed; }

/* ── Components added 2-3 Oct 2026 (plain CSS, so no Tailwind rebuild is needed) ── */

/* Icons from static/js/icons.js - icon() in PHP, limsIcon() in JS (issue #109). The size comes
   from the width/height attributes (16 px) or a Tailwind class such as w-5 h-5, which wins. */
.lims-icon { display: inline-block; flex-shrink: 0; vertical-align: -0.125em; }

/* "Akcie" column (issue #113 point 3): one look for the row buttons of every table, helpers
   actionsHeaderCell() / actionsCell() / actionButton() in partials/ui_helpers.inc.php. The buttons
   reproduce the ones in the samples table, which was the first to follow the rule. The column is
   pinned to the right edge so the buttons stay in view on tables wider than the screen. */
/* Opaque grey, not the card white: on white the pinned column blended into the cells scrolling under
   it and read as overlapping text, like a rendering bug (Denisa, #103 point 42). */
.lims-act-col { position: sticky; right: 0; z-index: 3; background: color-mix(in srgb, hsl(var(--muted)) 75%, hsl(var(--card))); box-shadow: -8px 0 10px -8px hsl(var(--foreground) / .22); border-left: 1px solid hsl(var(--border)); }
thead .lims-act-col { background: hsl(var(--muted)); z-index: 4; }
/* The sticky-header rule "table[data-resizable] thead th" (z-index 3) is more specific than the line
   above, so on resizable tables the row buttons scrolled up over the "Akcie" header, not under it. */
table thead th.lims-act-col { z-index: 4; }
tbody tr:hover > .lims-act-col { background: hsl(var(--muted)); }
/* The same grey, the left border and the soft edge, painted by the scroll frame itself so the pinned column
   is grey down to the bottom of the frame and not only beside the rows (act_col_band.js sets the class and
   --lims-act-w, the column's width). Without a scroll the gradients stay put, so the band is exactly where
   the pinned cells are. Under a vertical scrollbar it sits a scrollbar's width to the right, behind the
   cells, which are opaque - the band only shows where there is no row. */
.lims-act-band {
  background-image:
    linear-gradient(hsl(var(--border)), hsl(var(--border))),
    linear-gradient(to left, hsl(var(--foreground) / .12), transparent),
    linear-gradient(color-mix(in srgb, hsl(var(--muted)) 75%, hsl(var(--card))), color-mix(in srgb, hsl(var(--muted)) 75%, hsl(var(--card))));
  background-repeat: no-repeat;
  background-size: 1px 100%, 8px 100%, var(--lims-act-w) 100%;
  background-position: right calc(var(--lims-act-w) - 1px) top, right var(--lims-act-w) top, right top;
}
/* A row marked by a click (row_mark.js) tints its cells with a translucent blue; the pinned cell has to
   stay opaque or the columns scrolling underneath would show through it. */
tr.lims-row-picked > td.lims-act-col { background: linear-gradient(rgba(59, 130, 246, .14), rgba(59, 130, 246, .14)), hsl(var(--card)) !important; }
/* Tables whose other header cells are half-transparent (bg-muted/50, the samples table): same shade, but opaque. */
thead .lims-act-col--soft { background: color-mix(in srgb, hsl(var(--muted)) 50%, hsl(var(--card))); }
.lims-act-cell { display: flex; align-items: center; justify-content: center; gap: .375rem; }
.lims-act-btn, .lims-act-slot { width: 2rem; height: 2rem; flex-shrink: 0; }
.lims-act-btn { display: inline-flex; align-items: center; justify-content: center; border: 1px solid transparent; border-radius: .375rem; cursor: pointer; transition: background-color .15s, color .15s, border-color .15s, opacity .15s; }
.lims-act-btn--primary { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); box-shadow: 0 1px 2px rgba(15, 23, 42, .12); }
.lims-act-btn--primary:hover { opacity: .9; }
.lims-act-btn--secondary { border-color: hsl(var(--primary) / .4); background: hsl(var(--primary) / .05); color: hsl(var(--primary)); }
.lims-act-btn--secondary:hover { border-color: hsl(var(--primary)); background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
.lims-act-btn--ghost { background: hsl(var(--primary) / .1); color: hsl(var(--primary)); }
.lims-act-btn--ghost:hover { background: hsl(var(--primary) / .2); }
.lims-act-btn--danger { background: hsl(var(--destructive) / .08); color: hsl(var(--destructive)); }
.lims-act-btn--danger:hover { background: hsl(var(--destructive)); color: #fff; }
.lims-act-btn[disabled], .lims-act-btn.is-disabled { border-color: hsl(var(--border)); background: hsl(var(--muted) / .4); color: hsl(var(--muted-foreground) / .5); box-shadow: none; cursor: not-allowed; opacity: 1; }

/* Admin → Údržba systému → Zálohy databázy (issue #88), partials/admin_backups_card.inc.php */
.lims-bk-state { border: 1px solid; border-radius: .5rem; padding: .5rem .75rem; margin-bottom: .75rem; font-size: .875rem; }
.lims-bk-state--ok { background: hsl(142 70% 45% / .1); border-color: hsl(142 70% 38% / .4); color: hsl(142 70% 24%); }
.lims-bk-state--warn { background: hsl(38 92% 50% / .12); border-color: hsl(38 92% 45% / .5); color: hsl(30 80% 28%); }
.lims-bk-state--bad { background: hsl(var(--destructive) / .1); border-color: hsl(var(--destructive) / .4); color: hsl(var(--destructive)); }
.lims-bk-grid { display: grid; grid-template-columns: max-content 1fr; gap: .25rem 1rem; font-size: .8125rem; }
.lims-bk-grid dt { color: hsl(var(--muted-foreground)); }
.lims-bk-grid dd { margin: 0; overflow-wrap: anywhere; }
.lims-bk-table { width: 100%; border-collapse: collapse; font-size: .75rem; }
/* !important: the global "table ... thead th / tbody td { text-align: center }" rules are more specific. */
.lims-bk-table th, .lims-bk-table td { padding: .25rem .5rem; border-bottom: 1px solid hsl(var(--border)); text-align: left !important; }
.lims-bk-table th { color: hsl(var(--muted-foreground)); font-weight: 600; position: static; }
.lims-bk-table .num { text-align: right !important; white-space: nowrap; }

/* Empty table row - partials/ui_helpers.inc.php emptyStateRow().
   The row spans every column, so on a table wider than the screen the text is pinned to the
   visible part of the scroll container (sticky, at the cell's own 1.25rem padding) and sized to
   it: the viewport minus the 16rem sidebar, the page padding and that cell padding on both sides.
   Collapsed sidebar and phones (sidebar is an overlay there) drop the 16rem. */
.lims-empty-inner { position: sticky; left: 1.25rem; width: min(100%, calc(100vw - 21.75rem)); }
@media (min-width: 768px) { .lims-app-shell.lims-nav-collapsed .lims-empty-inner { width: min(100%, calc(100vw - 5.75rem)); } }
@media (max-width: 767px) { .lims-empty-inner { width: min(100%, calc(100vw - 4.75rem)); } }
.lims-empty-hint { max-width: 32rem; margin: .35rem auto 0; }
.lims-empty-link { display: inline-block; margin-top: .75rem; }

/* Banner of the sample being stored on the warehouse page - static/js/sample_placement.js */
.lims-place-banner {
  position: sticky; top: 0; z-index: 60; margin: 0 0 1rem; padding: .75rem 1rem;
  border-radius: .5rem; background: #eff6ff; border: 2px solid #2563eb; color: #1e3a8a;
  box-shadow: 0 4px 14px rgba(37, 99, 235, .18);
}
.lims-place-row { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }
.lims-place-text { flex: 1; min-width: 14rem; }
.lims-place-title { font-weight: 700; }
.lims-place-sub { font-size: .8rem; opacity: .85; margin-top: .15rem; }
.lims-place-error { display: none; margin-top: .35rem; font-size: .8rem; font-weight: 600; color: #b91c1c; }
.lims-place-error.is-visible { display: block; }
.lims-place-btn {
  padding: .4rem .9rem; border-radius: .375rem; font-weight: 600; font-size: .85rem;
  border: 1px solid #cbd5e1; background: #fff; color: #334155; cursor: pointer;
}
.lims-place-btn.is-primary { border: 0; background: #2563eb; color: #fff; }
.lims-place-btn:disabled { opacity: .45; cursor: not-allowed; }
.lims-place-selected { outline: 3px solid #2563eb; outline-offset: 2px; }

/* Rack map (static/js/rack_map.js): column numbers header and floor plan */
.lims-rack-cols { display: flex; align-items: stretch; background: #334155; color: #cbd5e1; font: 600 9px ui-monospace, monospace; }
.lims-rack-cols-label { width: 2.5rem; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
.lims-rack-cols-grid { flex: 1; display: grid; gap: .25rem; padding: .125rem .25rem; text-align: center; }
.lims-rack-free { margin-left: auto; font-weight: 600; opacity: .8; }
.lims-rack-num { font-size: 10px; font-weight: 700; color: #0f172a; text-shadow: 0 0 2px rgba(255, 255, 255, .55); }
.lims-rack-plan { width: 100%; max-width: 300px; display: block; margin: 0 auto; }
.lims-rack-plan-hint { text-align: center; font-size: 10px; color: #64748b; margin: -2px 0 4px; }
.lims-rack-plan g[data-plan-rack] { cursor: pointer; }
.lims-rack-flash { outline: 3px solid #2563eb; outline-offset: 2px; }

/* Expandable request detail row (static/js/detail_row*.js). It inherited the
   centring of the table rules above, so its labels, headings and the
   "Stav testu" phases sat ragged around the middle of their columns (Peter,
   4.10.2026). detail_row.js sizes it to the visible part of the table;
   sticky keeps it there when the table is scrolled sideways. Its sections
   stay side by side in one row and the grid scrolls sideways when they do
   not fit: wrapping pushed "Výsledky testu" under "Prevzatie žiadosti" on a
   zoomed-in screen (Denisa, #103 point 43). */
.detail-content { position: sticky; left: 1.5rem; text-align: left; }
.detail-grid { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(18rem, 1fr); gap: 1.5rem; overflow-x: auto; padding-bottom: .25rem; }
.detail-info { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: 1rem; row-gap: .25rem; }
