@layer zemo.tokens, zemo.reset, zemo.base, zemo.components, zemo.responsive;
@import url('./zemo-theme.css') layer(zemo.tokens);
@import url('./zemo-responsive.css') layer(zemo.responsive);

@layer zemo.reset {
  *, *::before, *::after { box-sizing: border-box; }
  html { min-height: 100%; }
  body { margin: 0; min-height: 100%; }
  button, input, select, textarea { font: inherit; }
  /* Values read character by character -- addresses, hashes, ports -- in the
     fixed pitch the token already described but nothing outside one table
     cell could reach. */
  code, kbd, samp, pre, .z-mono { font-family: var(--z-font-mono); }
  button, [role="button"] { -webkit-tap-highlight-color: transparent; }
  img, svg { max-width: 100%; }
  [hidden] { display: none !important; }
}

@layer zemo.base {
  html, body {
    background: radial-gradient(circle at 20% 0%, var(--z-surface-2) 0, var(--z-bg) 44%, var(--z-bg-deep) 100%);
    color: var(--z-text);
    font-family: var(--z-font);
    line-height: var(--z-leading-normal);
  }

  /* Velicinu postavlja samo body. Na html bi je pomerila pod sobom: rem racuna
     od korena, pa bi html na 0.875rem postao 14px, tim 1rem postao 14px, i body
     na 0.875rem ispao 12.25px umesto 14. Koren ostaje na onome sto je citalac
     podesio u pregledacu -- to je cela poenta prelaska na rem. */
  body { font-size: var(--z-font-md); }

  body.z-scroll-lock { overflow: hidden; }

  /* The ring is drawn as an outline, not a shadow. Written with :where() this
     rule carries no weight, so any component with a box-shadow of its own —
     every .z-btn has one — used to win the shadow and keep only the
     "outline: 0", leaving keyboard users with no ring at all. An outline
     cannot be defeated that way, and components that want a softer ring still
     override it explicitly. */
  :where(button, input, select, textarea, [tabindex]):focus-visible {
    outline: 2px solid var(--z-primary);
    outline-offset: 2px;
  }

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

  .z-subtle { color: var(--z-muted); }
  .z-spacer { flex: 1 1 auto; }
  .z-stack { display: grid; gap: var(--z-layout-gap); }
  .z-inline { display: flex; align-items: center; gap: var(--z-space-3); flex-wrap: wrap; }
  .z-grid-full { grid-column: 1 / -1; }
  .z-gap-2 { gap: var(--z-space-2); }
  .z-m-0 { margin: 0; }
  .z-mt-2 { margin-top: var(--z-space-2); }
  .z-search-field { min-width: 210px; }
  .z-search-field-wide { min-width: min(260px, 100%); }
  .z-symbol-sprite { position: absolute; overflow: hidden; }
}

@layer zemo.components {
  .z-shell {
    min-height: 100vh;
    display: grid;
    grid-template-columns: var(--z-sidebar-w) minmax(0, 1fr);
  }

  .z-sidebar {
    height: 100vh;
    position: sticky;
    top: 0;
    display: flex;
    flex-direction: column;
    padding: 18px 14px;
    overflow-y: auto;
    background: linear-gradient(180deg, var(--z-sidebar) 0%, var(--z-sidebar-deep) 100%);
    color: var(--z-sidebar-text);
    box-shadow: 5px 0 18px rgba(24, 32, 40, .14);
  }

  /* Works in a sidebar and in a top bar; the sidebar adds its own padding. */
  .z-brand {
    min-height: 36px;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--z-space-3);
    font-weight: 750;
    letter-spacing: .2px;
  }
  .z-sidebar .z-brand { padding: 0 8px 18px; }

  .z-brand-mark {
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    border-radius: var(--z-radius-sm);
    display: grid;
    place-items: center;
    background: linear-gradient(145deg, var(--z-primary), var(--z-primary-deep));
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .3), var(--z-elevation-panel);
  }

  /* A name with a second line under it, for a status or a timestamp. */
  .z-brand strong { display: block; line-height: var(--z-leading-tight); }
  .z-brand-sub { display: block; font-size: var(--z-font-xs); }
  /* The page needs exactly one <h1> for anyone navigating by headings, and in
     these applications that is the name in the bar — which must still look like
     a name, not a page title. */
  .z-brand-title { margin: 0; font-size: inherit; font-weight: 700; line-height: inherit; }

  .z-nav { display: grid; gap: 5px; margin-top: 8px; }

  .z-nav-item {
    width: 100%;
    min-height: 38px;
    border: 1px solid transparent;
    border-radius: 8px;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 11px;
    background: transparent;
    color: #dbe3e8;
    text-decoration: none;
    text-align: left;
    cursor: pointer;
  }

  .z-nav-item:hover { background: rgba(255, 255, 255, .07); border-color: rgba(255, 255, 255, .08); }
  .z-nav-item.is-active { background: linear-gradient(180deg, rgba(135, 164, 196, .38), rgba(91, 122, 155, .30)); border-color: rgba(181, 203, 224, .22); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .10); }
  .z-list-nav .z-nav-item { color: var(--z-text); }
  .z-list-nav .z-nav-item.is-active { background: var(--z-selected); border-color: var(--z-border-soft); }
  .z-nav-icon { width: 18px; height: 18px; flex: 0 0 auto; display: inline-grid; place-items: center; }
  /* The sidebar is dark whatever the page's theme is, so a control placed in
     it cannot take its colour from the page: a ghost button did, came out in
     the light theme's near-black, and disappeared into the background. Nav
     items already knew this; buttons did not. */
  .z-sidebar .z-btn-ghost { color: var(--z-sidebar-text); border-color: transparent; }
  .z-sidebar .z-btn-ghost:hover,
  .z-sidebar .z-btn-ghost:focus-visible { background: rgba(255, 255, 255, .09); border-color: rgba(255, 255, 255, .10); color: #fff; }
  .z-sidebar-footer { margin-top: auto; padding: 14px 8px 4px; font-size: var(--z-font-sm); color: #b9c6cf; }

  .z-sidebar-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 90;
    opacity: 0;
    pointer-events: none;
    background: var(--z-overlay);
    transition: opacity var(--z-motion-slow) var(--z-ease-standard);
  }

  .z-page {
    min-width: 0;
    width: min(var(--z-page-w), 100%);
    min-height: 100dvh;
    margin-inline: auto;
    display: grid;
    align-content: start;
    gap: var(--z-layout-gap);
    padding:
      max(var(--z-space-3), env(safe-area-inset-top))
      max(var(--z-space-5), env(safe-area-inset-right))
      max(var(--z-space-6), env(safe-area-inset-bottom))
      max(var(--z-space-5), env(safe-area-inset-left));
  }

  /* A grid item's automatic minimum size is its min-content width, so one wide
     child — a table, a long unbroken string — widens the whole column and drags
     every sibling with it, and the page scrolls sideways on a phone. Items must
     be allowed to be narrower than what they contain; the ones that scroll
     their own overflow then do so instead. */
  .z-page > * { min-width: 0; }

  .z-page > .z-topbar { margin: 0; border-radius: var(--z-radius-md); }

  .z-main { min-width: 0; width: 100%; padding: var(--z-space-5); }
  /* Pinned below the status bar, not under it: an installed application draws
     into the whole screen, so a sticky bar at top:0 slides beneath the clock
     and the battery. */
  .z-topbar > * { min-width: 0; }
  .z-topbar { min-height: 58px; position: sticky; z-index: 60; top: env(safe-area-inset-top, 0px); display: flex; align-items: center; justify-content: space-between; gap: var(--z-layout-gap); margin: calc(-1 * var(--z-space-5)) calc(-1 * var(--z-space-5)) var(--z-space-5); padding: var(--z-space-3) var(--z-space-5); border-bottom: 1px solid color-mix(in srgb, var(--z-border) 76%, transparent); background: color-mix(in srgb, var(--z-surface-overlay) 88%, transparent); box-shadow: var(--z-elevation-panel); backdrop-filter: blur(14px) saturate(1.15); }
  .z-title { margin: 0; font-size: clamp(20px, 2vw, var(--z-font-2xl)); line-height: var(--z-leading-tight); letter-spacing: -.3px; }

  .z-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--z-space-2);
  }

  .z-toolbar-surface {
    padding: 7px;
    border: 1px solid var(--z-border);
    border-radius: var(--z-radius-sm);
    background: var(--z-surface-raised);
    box-shadow: var(--z-elevation-panel), var(--z-shadow-inset);
  }

  .z-separator { width: 1px; height: 24px; flex: 0 0 auto; background: var(--z-border); margin: 0 2px; }
  .z-btn.z-mobile-menu { display: none; }
  .z-btn.z-sidebar-close { display: none; margin-left: auto; }

  .z-card {
    min-width: 0;
    padding: var(--z-card-pad);
    border: 1px solid var(--z-border);
    border-radius: var(--z-radius-md);
    background: linear-gradient(180deg, var(--z-surface-raised), var(--z-surface));
    box-shadow: var(--z-elevation-panel), var(--z-shadow-inset);
    transition: border-color var(--z-motion-normal) var(--z-ease-standard), box-shadow var(--z-motion-normal) var(--z-ease-standard);
  }

  .z-card-inset { padding: var(--z-space-3); background: var(--z-surface-2); box-shadow: var(--z-elevation-flat), var(--z-shadow-inset); }

  .z-card h2, .z-card h3 { margin: 0 0 14px; }
  /* A paragraph in a card carried the browser default margin, so prose in one card
     started lower than the table in the card beside it -- the same mismatch the
     heading rule above already fixes. Every other component that holds running text
     (notice, section bar, feature card, prose card) normalises it; the plainest card,
     the one a new page reaches for first, was the place left out. */
  .z-card p { margin: 0 0 var(--z-space-3); line-height: 1.6; }
  .z-card p:last-child { margin-bottom: 0; }
  .z-card-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
  /* A control in the header is a control, not the header. Left to grow, a
     search field took most of the row and folded the title onto two lines
     with the subtitle stranded underneath it. */
  .z-card-header > :is(.z-input, .z-input-group, .z-combo, select) { flex: 0 1 clamp(170px, 34%, 320px); }
  /* The text block, not simply the first child: card headers often start
     with an icon, and letting that grow pushed the title across the card
     and left the icon stranded on its own. */
  .z-card-header > div { min-width: 0; flex: 1 1 auto; }
  .z-card-header :is(h2, h3) { margin: 0; }
  /* An icon ahead of the heading block; the auto margin keeps any trailing
     toolbar pinned right, which the header's space-between alone would not. */
  .z-card-icon { width: 20px; height: 20px; flex: 0 0 auto; align-self: start; margin-top: 2px; color: var(--z-muted); }
  .z-card-icon + * { margin-right: auto; }
  .z-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--z-layout-gap); }
  .z-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--z-layout-gap); }
  .z-grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--z-layout-gap); }
  .z-content-grid { display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(300px, .75fr); gap: var(--z-layout-gap); }

  .z-btn {
    --z-btn-bg: var(--z-control-bg);
    --z-btn-bg-hover: var(--z-control-bg-hover);
    --z-btn-bg-active: var(--z-control-bg-active);
    min-height: var(--z-control-h);
    min-width: var(--z-button-min-w);
    border: 1px solid var(--z-border-strong);
    border-radius: var(--z-radius-sm);
    padding: 0 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    background: var(--z-btn-bg);
    color: var(--z-text);
    box-shadow: 0 2px 3px rgba(27, 39, 51, .12), inset 0 1px 0 rgba(255, 255, 255, .72), inset 0 -1px 0 rgba(27, 39, 51, .10);
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    transition: transform var(--z-motion-fast) var(--z-ease-emphasized), border-color var(--z-motion-normal) var(--z-ease-standard), box-shadow var(--z-motion-normal) var(--z-ease-standard), filter var(--z-motion-normal) var(--z-ease-standard);
  }

  .z-btn:hover:not(:disabled) { transform: translateY(-1px); background: var(--z-btn-bg-hover); box-shadow: 0 4px 8px rgba(27, 39, 51, .16), inset 0 1px 0 rgba(255, 255, 255, .78), inset 0 -1px 0 rgba(27, 39, 51, .12); }
  .z-btn:active:not(:disabled), .z-btn.is-pressed { transform: translateY(1px); background: var(--z-btn-bg-active); box-shadow: inset 0 2px 5px rgba(27, 39, 51, .24), inset 0 -1px 0 rgba(255, 255, 255, .18); }
  .z-btn-primary { --z-btn-bg: var(--z-button-primary); --z-btn-bg-hover: var(--z-button-primary-hover); --z-btn-bg-active: var(--z-button-primary-active); color: var(--z-on-accent); border-color: var(--z-primary-deep); text-shadow: 0 1px 1px rgba(0, 0, 0, .28); }
  .z-btn-danger { --z-btn-bg: var(--z-button-danger); --z-btn-bg-hover: var(--z-button-danger-hover); --z-btn-bg-active: var(--z-button-danger-active); color: var(--z-on-accent); border-color: var(--z-danger-strong); text-shadow: 0 1px 1px rgba(0, 0, 0, .24); }
  .z-btn-success { --z-btn-bg: var(--z-button-success); --z-btn-bg-hover: var(--z-button-success-hover); --z-btn-bg-active: var(--z-button-success-active); color: var(--z-on-accent); border-color: var(--z-success-strong); text-shadow: 0 1px 1px rgba(0, 0, 0, .24); }
  .z-btn-outline-danger { --z-btn-bg: transparent; --z-btn-bg-hover: var(--z-danger-soft); --z-btn-bg-active: color-mix(in srgb, var(--z-danger-soft) 72%, var(--z-danger)); color: var(--z-danger-strong); border-color: var(--z-danger); box-shadow: none; }
  .z-btn-ghost { --z-btn-bg: transparent; --z-btn-bg-hover: var(--z-hover); --z-btn-bg-active: var(--z-selected); background: var(--z-btn-bg); border-color: transparent; box-shadow: none; }
  .z-btn-uniform { width: var(--z-button-uniform-w); }
  .z-btn-sm { min-width: 28px; min-height: 28px; padding-inline: 9px; font-size: var(--z-font-sm); }
  .z-btn-lg { min-width: 112px; min-height: 42px; padding-inline: 16px; }
  .z-btn:disabled, .z-btn[aria-disabled="true"] { opacity: .48; cursor: not-allowed; transform: none; filter: saturate(.5); }
  .z-icon-btn { width: var(--z-control-h); min-width: var(--z-control-h); padding: 0; }
  /* Phosphor duotone: dva puta po ikoni, oba u currentColor, gde bledji nosi opacity .2
     iz samog sprite-a. Zato popuna, ne linija -- stroke bi obrisao oba sloja u jednu kasu. */
  .z-icon { width: 16px; height: 16px; flex: 0 0 auto; fill: currentColor; stroke: none; }
  .z-icon-warning-lg { width: 22px; height: 22px; color: var(--z-warning); }

  .z-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    width: max-content;
    border: 1px solid var(--z-border);
    border-radius: 999px;
    padding: 4px 8px;
    background: var(--z-surface-2);
    font-size: var(--z-font-sm);
  }

  .z-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--z-muted); box-shadow: 0 0 0 3px color-mix(in srgb, var(--z-muted) 12%, transparent); }
  .z-badge.ok { color: var(--z-success-strong); border-color: color-mix(in srgb, var(--z-success) 38%, var(--z-border)); background: var(--z-success-soft); }
  .z-badge.ok .z-dot { background: var(--z-success); }
  .z-badge.warn { color: var(--z-warning-strong); border-color: color-mix(in srgb, var(--z-warning) 42%, var(--z-border)); background: var(--z-warning-soft); }
  .z-badge.warn .z-dot { background: var(--z-warning); }
  .z-badge.bad { color: var(--z-danger-strong); border-color: color-mix(in srgb, var(--z-danger) 38%, var(--z-border)); background: var(--z-danger-soft); }
  .z-badge.bad .z-dot { background: var(--z-danger); }
  .z-status-badge {
    --z-status-accent: var(--z-muted);
    --z-status-ink: color-mix(in srgb, var(--z-muted) 82%, var(--z-text));
    --z-status-fill: color-mix(in srgb, var(--z-muted) 14%, var(--z-surface-raised));
    width: var(--z-status-badge-w);
    min-height: 26px;
    flex: 0 0 var(--z-status-badge-w);
    justify-content: flex-start;
    color: var(--z-status-ink);
    border-color: color-mix(in srgb, var(--z-status-accent) 44%, var(--z-border));
    background: linear-gradient(180deg, color-mix(in srgb, var(--z-status-fill) 66%, var(--z-surface-raised)), var(--z-status-fill));
    box-shadow: inset 0 1px 0 color-mix(in srgb, var(--z-surface-raised) 72%, transparent), 0 1px 2px rgba(31, 47, 60, .10);
    font-weight: 650;
    line-height: 1;
    white-space: nowrap;
  }
  .z-status-badge.ok { --z-status-accent: var(--z-success); --z-status-ink: var(--z-success-strong); --z-status-fill: var(--z-success-soft); }
  .z-status-badge.warn { --z-status-accent: var(--z-warning); --z-status-ink: var(--z-warning-strong); --z-status-fill: var(--z-warning-soft); }
  .z-status-badge.bad { --z-status-accent: var(--z-danger); --z-status-ink: var(--z-danger-strong); --z-status-fill: var(--z-danger-soft); }
  .z-status-badge .z-dot { background: var(--z-status-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--z-status-accent) 14%, transparent), inset 0 1px 0 color-mix(in srgb, var(--z-surface-raised) 56%, transparent); }

  .z-field { display: grid; gap: var(--z-field-gap); }
  /* Related fields, side by side while there is room and stacked when there is not. Settings
     and forms are read in groups -- server and port belong together -- and a column of full
     width inputs on a wide screen makes the reader travel for no reason. */
  .z-field-grid { display: grid; gap: var(--z-space-3) var(--z-space-4); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); margin-bottom: var(--z-space-4); }
  .z-label { font-size: var(--z-font-sm); font-weight: 700; color: var(--z-muted); }
  .z-help { font-size: var(--z-font-sm); color: var(--z-muted); }
  .z-input, .z-select, .z-textarea {
    width: 100%;
    min-height: var(--z-control-h);
    border: 1px solid var(--z-border-strong);
    border-radius: var(--z-radius-sm);
    padding: 6px 10px;
    background: var(--z-surface-raised);
    color: var(--z-text);
    box-shadow: inset 0 1px 2px rgba(27, 39, 51, .07);
  }
  .z-textarea { min-height: 88px; resize: vertical; }
  .z-input::placeholder, .z-textarea::placeholder { color: var(--z-disabled); }
  .z-input:disabled, .z-select:disabled, .z-textarea:disabled { opacity: .58; cursor: not-allowed; }
  .z-field.has-error :is(.z-input, .z-select, .z-textarea) { border-color: var(--z-danger); }
  .z-field.has-error .z-help { color: var(--z-danger-strong); }
  .z-check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
  .z-check input { width: 16px; height: 16px; accent-color: var(--z-primary-strong); }

  .z-input-group { position: relative; min-width: 0; display: flex; align-items: stretch; }
  .z-password { width: 100%; }
  .z-input-group > .z-input { padding-right: 38px; }
  .z-input-action {
    width: 34px;
    min-width: 34px;
    position: absolute;
    inset: 1px 1px 1px auto;
    border: 0;
    border-left: 1px solid var(--z-border-soft);
    border-radius: 0 calc(var(--z-radius-sm) - 1px) calc(var(--z-radius-sm) - 1px) 0;
    display: inline-grid;
    place-items: center;
    background: linear-gradient(180deg, color-mix(in srgb, var(--z-surface-raised) 88%, transparent), var(--z-surface-2));
    color: var(--z-muted);
    cursor: pointer;
  }
  .z-input-action:hover:not(:disabled) { color: var(--z-text); background: var(--z-hover); }
  .z-input-action:active:not(:disabled), .z-input-action[aria-pressed="true"] { box-shadow: inset 0 2px 4px rgba(27, 39, 51, .18); color: var(--z-primary-strong); }
  .z-input-action:disabled { opacity: .45; cursor: not-allowed; }

  .z-combo { position: relative; min-width: 0; }
  .z-combo-list {
    width: 100%;
    max-height: 220px;
    position: absolute;
    z-index: 300;
    top: calc(100% + 5px);
    left: 0;
    overflow-y: auto;
    padding: 5px;
    border: 1px solid var(--z-border);
    border-radius: var(--z-radius-sm);
    background: var(--z-surface-overlay);
    box-shadow: var(--z-elevation-overlay);
    backdrop-filter: blur(10px);
  }
  .z-combo:not(.is-open) .z-combo-list { display: none; }
  .z-combo-option { width: 100%; min-height: 32px; border: 0; border-radius: 5px; padding: 5px 9px; display: flex; align-items: center; gap: 8px; background: transparent; color: var(--z-text); text-align: left; cursor: pointer; }
  .z-combo-option:hover, .z-combo-option.is-active { background: var(--z-primary-soft); color: var(--z-primary-on-soft); }
  .z-combo-option[aria-selected="true"] { font-weight: 750; }
  .z-combo-empty { padding: 10px; color: var(--z-muted); font-size: var(--z-font-sm); text-align: center; }
  .z-combo [data-z-combo-toggle] .z-icon { transform: rotate(90deg); transition: transform .14s ease; }
  .z-combo.is-open [data-z-combo-toggle] .z-icon { transform: rotate(-90deg); }
  .z-multi-combo-control { min-height: var(--z-control-h); border: 1px solid var(--z-border); border-radius: var(--z-radius-sm); display: flex; align-items: center; gap: 4px; background: var(--z-surface-raised); box-shadow: inset 0 1px 2px rgba(36, 49, 60, .06); transition: border-color .14s ease, box-shadow .14s ease; }
  .z-multi-combo-control:focus-within { border-color: var(--z-primary); box-shadow: var(--z-focus-ring), inset 0 1px 2px rgba(36, 49, 60, .06); }
  .z-multi-combo-chips { min-width: 0; padding-left: 4px; display: contents; }
  .z-chip { max-width: 180px; min-height: 24px; border: 1px solid color-mix(in srgb, var(--z-primary) 48%, var(--z-border)); border-radius: 999px; padding: 2px 5px 2px 8px; display: inline-flex; align-items: center; gap: 5px; background: linear-gradient(180deg, color-mix(in srgb, var(--z-primary-soft) 62%, var(--z-surface-raised)), var(--z-primary-soft)); color: var(--z-primary-on-soft); font: inherit; font-size: var(--z-font-xs); font-weight: 700; line-height: 1; cursor: pointer; box-shadow: 0 1px 1px rgba(31, 49, 65, .08); }
  .z-chip > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .z-chip:hover { border-color: var(--z-primary); filter: brightness(.98); }
  .z-chip:focus-visible { outline: 0; box-shadow: var(--z-focus-ring); }
  .z-chip-remove { width: 15px; height: 15px; display: inline-grid; place-items: center; border-radius: 50%; background: color-mix(in srgb, var(--z-primary) 18%, transparent); font-size: var(--z-font-md); line-height: 1; }
  .z-multi-combo-input { min-width: 82px; flex: 1 1 100px; height: calc(var(--z-control-h) - 2px); border: 0; padding-inline: 5px; background: transparent; color: var(--z-text); outline: 0; font: inherit; }
  .z-multi-combo-control > .z-input-action { align-self: stretch; border-block: 0; border-right: 0; }
  .z-multi-combo .z-combo-option[aria-selected="true"]::after { content: "\2713"; margin-left: auto; color: var(--z-primary-strong); font-weight: 800; }

  .z-date-input, .z-time-input { color-scheme: light; }
  [data-z-theme="dark"] :is(.z-date-input, .z-time-input) { color-scheme: dark; }
  :is(.z-date-input, .z-time-input)::-webkit-calendar-picker-indicator { opacity: .66; cursor: pointer; }

  .z-upload { min-width: 0; }
  .z-upload-dropzone {
    min-height: 132px;
    border: 1px dashed var(--z-border-strong);
    border-radius: var(--z-radius-md);
    padding: 18px;
    display: grid;
    place-items: center;
    align-content: center;
    gap: 8px;
    background: linear-gradient(145deg, var(--z-surface-raised), var(--z-surface-2));
    color: var(--z-muted);
    text-align: center;
    cursor: pointer;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .62);
    transition: border-color .14s ease, background .14s ease, box-shadow .14s ease, transform .14s ease;
  }
  .z-upload-dropzone:hover, .z-upload.is-dragover .z-upload-dropzone { border-color: var(--z-primary); background: var(--z-primary-softer); box-shadow: var(--z-focus-ring), inset 0 1px 0 rgba(255, 255, 255, .62); }
  .z-upload.is-dragover .z-upload-dropzone { transform: translateY(-1px); }
  .z-upload.is-disabled .z-upload-dropzone { opacity: .52; cursor: not-allowed; filter: saturate(.45); }
  .z-upload-dropzone .z-icon { width: 28px; height: 28px; color: var(--z-primary-strong); }
  .z-upload-copy strong { display: block; margin-bottom: 3px; color: var(--z-text); }
  .z-upload-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
  .z-upload-list { margin: 10px 0 0; padding: 0; display: grid; gap: 7px; list-style: none; }
  .z-upload-file { min-width: 0; padding: 8px 9px; border: 1px solid var(--z-border-soft); border-radius: var(--z-radius-sm); display: grid; grid-template-columns: minmax(0, 1fr) 110px 32px; gap: 9px; align-items: center; background: var(--z-surface-raised); }
  .z-upload-file-meta { min-width: 0; }
  .z-upload-file-name { display: block; overflow: hidden; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
  .z-upload-file-size { color: var(--z-muted); font-size: var(--z-font-xs); }
  .z-upload-file-progress { display: grid; gap: 4px; }
  .z-upload-file-progress small { color: var(--z-muted); text-align: right; }
  .z-upload-remove { width: 30px; min-width: 30px; min-height: 28px; padding: 0; }
  .z-upload-error { min-height: 17px; margin-top: 6px; color: var(--z-danger-strong); font-size: var(--z-font-sm); }
  .z-upload.has-error .z-upload-dropzone { border-color: var(--z-danger); }

  /* The input is visually hidden and the track is what gets clicked, so the
     switch must be a <label> (or sit inside one) or clicks go nowhere. */
  /* flex: none, because the switch shares a row with buttons in
     .z-tile-actions. As an ordinary flex item it was free to shrink, and
     since the track is inset: 0 with a round 18px knob, a shrunken switch
     reads as a circle rather than as a pill -- the control stops looking
     like something you slide. */
  /* align-self, and a track with its own size rather than inset: 0.
     A flex or grid parent that stretches its children was making the label
     42 tall as well as 42 wide, and a square with a 999px radius is a circle
     -- which is what the switch looked like on an iPad. Now neither the label
     nor the track can be stretched into one. */
  .z-switch { position: relative; display: inline-flex; flex: none; align-self: center; width: 42px; min-width: 42px; height: 22px; min-height: 22px; max-height: 22px; vertical-align: middle; }
  .z-switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
  .z-switch-track { position: absolute; left: 0; top: 0; width: 42px; height: 22px; border: 1px solid #9eabb5; border-radius: 999px; background: linear-gradient(180deg, #c5ccd2, #aeb8c0); box-shadow: inset 0 2px 4px rgba(27, 39, 51, .12), 0 1px 0 rgba(255, 255, 255, .50); cursor: pointer; transition: background .16s ease; }
  .z-switch-track::after { content: ""; position: absolute; width: 18px; height: 18px; top: 1px; left: 1px; border: 1px solid #c4ccd3; border-radius: 50%; background: linear-gradient(180deg, #fff, #e8edf1); box-shadow: 0 2px 5px rgba(27, 39, 51, .24); transition: transform .16s ease; }
  .z-switch input:focus-visible + .z-switch-track { box-shadow: var(--z-focus-ring); }
  .z-switch input:checked + .z-switch-track { background: linear-gradient(180deg, #8ea7c4, var(--z-primary)); border-color: var(--z-primary-strong); }
  .z-switch input:checked + .z-switch-track::after { transform: translateX(20px); }
  .z-switch input:disabled + .z-switch-track { opacity: .45; cursor: not-allowed; }

  .z-slider-row { display: grid; grid-template-columns: 100px minmax(120px, 1fr) 54px; gap: 12px; align-items: center; }
  .z-slider { appearance: none; width: 100%; height: 6px; border-radius: 999px; background: linear-gradient(90deg, var(--z-primary) 0 65%, var(--z-border) 65% 100%); box-shadow: inset 0 1px 3px rgba(27, 39, 51, .16); outline: none; }
  .z-slider::-webkit-slider-thumb { appearance: none; width: 19px; height: 19px; border: 1px solid var(--z-border-strong); border-radius: 50%; background: linear-gradient(180deg, #fff, #dfe6ec); box-shadow: 0 2px 6px rgba(27, 39, 51, .25); cursor: pointer; }
  .z-slider::-moz-range-thumb { width: 18px; height: 18px; border: 1px solid var(--z-border-strong); border-radius: 50%; background: linear-gradient(180deg, #fff, #dfe6ec); box-shadow: 0 2px 6px rgba(27, 39, 51, .25); cursor: pointer; }
  .z-slider:focus-visible { box-shadow: var(--z-focus-ring), inset 0 1px 3px rgba(27, 39, 51, .16); }
  .z-value { text-align: right; font-variant-numeric: tabular-nums; color: var(--z-muted); }

  .z-tabs { min-width: 0; }
  .z-tabs-list { display: flex; align-items: flex-end; gap: 2px; border-bottom: 1px solid var(--z-border); }
  .z-tab {
    min-height: 38px;
    border: 0;
    border-bottom: 2px solid transparent;
    padding: 0 13px;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    background: transparent;
    color: var(--z-muted);
    cursor: pointer;
    white-space: nowrap;
  }
  .z-tab:hover { color: var(--z-text); background: var(--z-hover); }
  .z-tab[aria-selected="true"], .z-tab.is-active { color: var(--z-primary-strong); border-bottom-color: var(--z-primary-strong); }
  .z-tab-panel { padding-top: 16px; }

  .z-table-wrap { min-height: 96px; position: relative; border: 1px solid var(--z-border); border-radius: 10px; overflow: hidden; background: var(--z-surface-raised); box-shadow: var(--z-elevation-raised); }
  .z-grid-scroll { max-height: var(--z-grid-max-h, 460px); overflow: auto; scrollbar-gutter: stable; }
  .z-table { width: 100%; border-collapse: collapse; }
  .z-table th { height: var(--z-row-h); padding: var(--z-cell-py) var(--z-cell-px); border-bottom: 1px solid var(--z-border); background: var(--z-table-head); color: var(--z-muted); font-size: var(--z-font-sm); font-weight: 700; letter-spacing: .02em; text-align: left; white-space: nowrap; }
  .z-table td { min-height: var(--z-row-h); padding: var(--z-cell-py) var(--z-cell-px); border-bottom: 1px solid var(--z-border-soft); }
  .z-table tr:last-child td { border-bottom: 0; }
  .z-table tbody tr { transition: background-color var(--z-motion-fast) var(--z-ease-standard), box-shadow var(--z-motion-fast) var(--z-ease-standard); }
  .z-table tbody tr:hover { background: var(--z-hover); }
  .z-table tbody tr.is-selected { background: var(--z-selected); box-shadow: inset 3px 0 0 var(--z-primary); }
  .z-table tbody tr:focus-visible { outline: 0; box-shadow: inset 0 0 0 2px var(--z-primary); }
  /* The button is the whole header cell, not the words inside it. Measured
     in the browser it was 17px tall -- a target you have to aim at, when
     the obvious thing to click is the column title itself. */
  .z-table-sort { appearance: none; width: 100%; min-height: var(--z-row-h); margin: calc(-1 * var(--z-cell-py)) calc(-1 * var(--z-cell-px)); padding: var(--z-cell-py) var(--z-cell-px); border: 0; border-radius: 4px; display: inline-flex; align-items: center; gap: 5px; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
  .z-table-sort:hover { background: var(--z-hover); }
  .z-table-sort:focus-visible { outline: 2px solid var(--z-primary); outline-offset: -2px; }
  .z-table-sort::after { content: "↕"; opacity: .55; }
  .z-table-sort[aria-sort="ascending"]::after { content: "↑"; opacity: 1; }
  .z-table-sort[aria-sort="descending"]::after { content: "↓"; opacity: 1; }

  /* A second way to mark a sortable column, for a table whose rows are drawn
     from data rather than written out: the header cell itself carries
     data-sort and ZemoUI.sortable() wires it. The arrow is drawn from
     aria-sort, so what a screen reader announces and what the eye sees come
     from the same attribute and cannot drift apart. */
  .z-table th[data-sort] { position: relative; padding-right: 26px; white-space: nowrap; cursor: pointer; user-select: none; }
  .z-table th[data-sort]:hover { color: var(--z-text); }
  .z-table th[data-sort]:focus-visible { outline: 2px solid var(--z-primary); outline-offset: -2px; }
  /* Strelica tece odmah iza natpisa, a ne zalepljena za desnu ivicu: tako desnih 26 px ostaje
     prazno za hvatiste sirine kolone, umesto da se ta dva bore za isti prostor. */
  .z-table th[data-sort]::after { content: "↕"; margin-left: 5px; opacity: .55; }
  .z-table th[data-sort][aria-sort="ascending"]::after { content: "↑"; opacity: 1; }
  .z-table th[data-sort][aria-sort="descending"]::after { content: "↓"; opacity: 1; }

  /* A column of numbers reads down the last digit, not the first: right-aligned
     and in one pitch, so 9 and 10 line up their units under each other. */
  .z-table td.z-num { text-align: right; font-variant-numeric: tabular-nums; }

  /* The row's position in what is on screen now -- after sorting and
     filtering -- which is the only number that means anything when someone
     reads "row seven" down a telephone. It takes the width it needs and no
     more, so numbering costs the data nothing. */
  .z-table .z-ordinal { width: 1%; padding-right: var(--z-space-2); color: var(--z-muted); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }

  /* Columns the reader sets the width of. `data-z-columns` turns it on and
     names the widths, so they are still there the next time the page is opened.

     The fixed layout is what makes a width hold -- under the automatic one the
     browser widens a column back to fit its longest cell -- and it is switched
     on only once a width has been set, so a table nobody has touched still
     sizes itself to its content. Once it is on, a cell keeps what it cannot
     show to itself: without that, a narrowed column would spill its text over
     the one beside it instead of getting narrower. */
  .z-table[data-z-columns] th { position: relative; }
  .z-table.is-column-sized { table-layout: fixed; }
  .z-table.is-column-sized :is(th, td) { overflow: hidden; }
  .z-table.is-column-sized th { text-overflow: ellipsis; }
  /* A table widened past its frame has to stay reachable: the frame that rounds
     off the corners scrolls sideways rather than cutting the last column off.
     A scrollable frame already scrolls both ways and keeps its own rule below. */
  .z-table-wrap:not(.is-scrollable):has(> .z-table.is-column-sized) { overflow: auto hidden; }

  /* A long table scrolls inside its own frame, so the header above it and the
     filters above that stay on screen instead of the whole page travelling
     through several hundred rows. */
  .z-table-wrap.is-scrollable { max-height: var(--z-table-max-h); overflow: auto; }
  .z-table-wrap.is-scrollable thead th { position: sticky; top: 0; z-index: 2; }
  .z-table-footer { min-height: 42px; padding: 7px 10px; display: flex; align-items: center; gap: 8px; border-top: 1px solid var(--z-border); background: var(--z-surface-2); }
  .z-data-grid { min-width: 900px; table-layout: fixed; }
  .z-data-grid[data-z-sticky-header] thead th { position: sticky; z-index: 5; top: 0; }
  .z-data-grid th { position: relative; overflow: visible; user-select: none; }
  .z-data-grid th[draggable="true"] { cursor: grab; }
  .z-data-grid th.is-dragging { opacity: .48; cursor: grabbing; }
  .z-data-grid th.is-drop-target { box-shadow: inset 3px 0 0 var(--z-primary); }
  .z-data-grid td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .z-data-grid [data-z-editable] { cursor: text; }
  .z-data-grid [data-z-editable]:hover { box-shadow: inset 0 -2px 0 color-mix(in srgb, var(--z-primary) 48%, transparent); }
  .z-data-grid [data-z-editable]:focus-visible { outline: 0; box-shadow: inset 0 0 0 2px var(--z-primary); }
  .z-data-grid .z-grid-editor { width: 100%; min-width: 0; height: 28px; padding-inline: 7px; }
  .z-data-grid .z-grid-editor.is-invalid { border-color: var(--z-danger); box-shadow: 0 0 0 2px color-mix(in srgb, var(--z-danger) 22%, transparent); }
  :is(.z-data-grid, .z-table) .z-column-resizer { width: 9px; position: absolute; z-index: 8; inset: 0 -5px 0 auto; cursor: col-resize; touch-action: none; }
  :is(.z-data-grid, .z-table) .z-column-resizer::after { content: ""; width: 1px; position: absolute; inset: 7px auto 7px 4px; background: var(--z-border-strong); opacity: .55; }
  :is(.z-data-grid, .z-table) .z-column-resizer:hover::after, :is(.z-data-grid, .z-table) .z-column-resizer:focus-visible::after, :is(.z-data-grid, .z-table).is-column-resizing .z-column-resizer::after { width: 2px; background: var(--z-primary); opacity: 1; }
  :is(.z-data-grid, .z-table) .z-column-resizer:focus-visible { outline: 0; box-shadow: 0 0 0 2px color-mix(in srgb, var(--z-primary) 30%, transparent); }
  /* Hvatiste za sirinu kolone je meta koja mora da bude bar 24 px siroka (WCAG 2.5.8 AA), a
     stoji uz zaglavlje koje se klikce radi sortiranja. Dva resenja se sama nude i oba su losa:
     usko hvatiste promasuje pravilo, siroko jede klik namenjen sortiranju.

     Izlaz je da se oslobodi prostor koji ionako niko ne koristi. Zaglavlje koje se sortira vec
     drzi 26 px praznine sa desne strane, samo zato sto je strelica bila zalepljena za ivicu.
     Kad strelica krene da tece odmah iza teksta, ta praznina ostaje slobodna i hvatiste je uzme
     celu: 24 px sirine preko cistog vazduha. Ni tekst ni strelica se ne pokrivaju, sirina
     zaglavlja se ne menja, a sortiranje i dalje radi klikom na natpis i na strelicu.

     Praznina se rezervise na svakom zaglavlju tabele koja se moze siriti, ne samo na onom koje
     se sortira: kolona bez sortiranja nema strelicu, pa bi joj hvatiste inace prekrilo kraj
     natpisa. */
  .z-table[data-z-columns] th { padding-right: 26px; }
  .z-table:not(.z-data-grid) .z-column-resizer { inset: 0 0 0 auto; width: 24px; }
  /* Linija ostaje na samoj ivici kolone, jer se cilja granica a ne sredina hvatista. */
  .z-table:not(.z-data-grid) .z-column-resizer::after { inset: 7px 4px 7px auto; }
     what it cannot show would clip a grip that straddled its edge. */
  /* While an edge is being dragged the pointer is doing one thing, not
     selecting the words it passes over. */
  :is(.z-data-grid, .z-table).is-column-resizing { cursor: col-resize; user-select: none; }
  .z-data-grid :is(th, td).is-frozen { position: sticky; left: var(--z-grid-sticky-left, 0); z-index: 4; background: var(--z-surface-raised); box-shadow: 1px 0 0 var(--z-border); }
  .z-data-grid th.is-frozen { z-index: 7; background: var(--z-table-head); }
  .z-data-grid tbody tr:hover > td.is-frozen { background: color-mix(in srgb, var(--z-hover) 88%, var(--z-surface-raised)); }
  .z-data-grid tbody tr.is-selected > td.is-frozen { background: var(--z-selected); }
  .z-data-grid .z-grid-detail > td { padding: 0; overflow: visible; white-space: normal; background: var(--z-surface-2); }
  .z-grid-detail-panel { padding: var(--z-space-3) var(--z-space-4) var(--z-space-4) calc(var(--z-space-6) + var(--z-space-5)); border-bottom: 1px solid var(--z-border); display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--z-layout-gap); box-shadow: inset 0 4px 8px rgba(31, 47, 60, .04); animation: z-grid-detail-in var(--z-motion-slow) var(--z-ease-emphasized) both; }
  .z-grid-detail-toggle[aria-expanded="true"] .z-icon { transform: rotate(90deg); }
  .z-grid-detail-toggle .z-icon { transition: transform var(--z-motion-normal) var(--z-ease-emphasized); }
  .z-grid-selection-summary { min-width: 112px; color: var(--z-muted); font-variant-numeric: tabular-nums; }
  .z-grid-column-list { min-width: 220px; display: grid; gap: 8px; }
  .z-grid-column-list .z-check { justify-content: space-between; }
  .z-grid-context-toolbar { min-height: calc(var(--z-control-h) + var(--z-space-3)); margin-bottom: var(--z-space-3); padding: var(--z-space-2) var(--z-space-3); border: 1px solid color-mix(in srgb, var(--z-primary) 46%, var(--z-border)); border-radius: var(--z-radius-sm); display: flex; align-items: center; gap: var(--z-space-2); background: linear-gradient(180deg, color-mix(in srgb, var(--z-primary-soft) 62%, var(--z-surface-raised)), var(--z-primary-soft)); box-shadow: var(--z-elevation-raised), var(--z-shadow-inset); animation: z-context-toolbar-in var(--z-motion-slow) var(--z-ease-emphasized) both; }
  .z-grid-context-toolbar strong { color: var(--z-primary-strong); font-variant-numeric: tabular-nums; }
  .z-grid-context-toolbar[hidden] { display: none; }
  .z-grid-state { min-height: 220px; position: absolute; z-index: 20; inset: 0; padding: var(--z-space-5); display: grid; place-items: center; align-content: center; gap: var(--z-space-3); background: color-mix(in srgb, var(--z-surface-raised) 94%, transparent); text-align: center; backdrop-filter: blur(2px); animation: z-grid-state-in var(--z-motion-normal) var(--z-ease-standard) both; }
  .z-grid-state-symbol { width: 42px; height: 42px; border: 1px solid var(--z-border); border-radius: 50%; display: grid; place-items: center; background: var(--z-surface-2); color: var(--z-muted); font-size: var(--z-font-xl); box-shadow: var(--z-elevation-panel); }
  .z-grid-state-title { font-size: var(--z-font-lg); font-weight: 750; }
  .z-grid-state-message { max-width: 420px; color: var(--z-muted); }
  .z-grid-state[data-state="error"] .z-grid-state-symbol { border-color: color-mix(in srgb, var(--z-danger) 45%, var(--z-border)); background: var(--z-danger-soft); color: var(--z-danger-strong); }
  .z-grid-state-skeleton { width: min(420px, 72vw); display: grid; gap: var(--z-space-2); }
  .z-grid-state-skeleton span { height: 11px; border-radius: 999px; background: linear-gradient(90deg, var(--z-border-soft) 20%, var(--z-surface-raised) 42%, var(--z-border-soft) 64%); background-size: 220% 100%; animation: z-skeleton 1.25s linear infinite; }
  .z-grid-state-skeleton span:last-child { width: 72%; }
  .z-data-grid [data-z-editable].is-just-edited { animation: z-cell-saved 900ms var(--z-ease-standard); }
  @keyframes z-context-toolbar-in { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } }
  @keyframes z-grid-state-in { from { opacity: 0; } to { opacity: 1; } }
  @keyframes z-grid-detail-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
  @keyframes z-cell-saved { 0% { background: color-mix(in srgb, var(--z-success-soft) 80%, transparent); box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--z-success) 62%, transparent); } 100% { background: transparent; box-shadow: none; } }
  @keyframes z-skeleton { to { background-position: -220% 0; } }

  /* The same shimmer as the grid's own placeholder above, but free of the grid:
     a page that fills a plain <table>, a card or a panel can stand something in
     for content that has not arrived yet, instead of showing an empty box and
     then jolting the layout when rows appear.

     Deliberately without a size. What it stands in for decides how big it is --
     a table cell, a line of a paragraph, an avatar -- so the caller sets width
     and height, and `.z-skeleton-text` covers the one case common enough to
     name: a line of text. Global prefers-reduced-motion already stills it. */
  .z-skeleton { display: block; border-radius: var(--z-radius-xs); background: linear-gradient(90deg, var(--z-border-soft) 20%, var(--z-surface-raised) 42%, var(--z-border-soft) 64%); background-size: 220% 100%; animation: z-skeleton 1.25s linear infinite; }
  .z-skeleton-text { height: 11px; border-radius: 999px; }

  .z-tree, .z-tree ul { margin: 0; padding: 0; list-style: none; }
  .z-tree ul { padding-left: 20px; }
  .z-tree-item { min-height: 32px; display: flex; align-items: center; gap: 7px; border-radius: 6px; padding: 3px 7px; cursor: default; }
  .z-tree-item:hover { background: var(--z-hover); }
  .z-tree-item.is-selected { background: var(--z-selected); color: var(--z-primary-on-soft); }
  .z-tree-toggle { width: 20px; height: 20px; border: 0; padding: 0; display: inline-grid; place-items: center; background: transparent; color: var(--z-muted); cursor: pointer; }
  .z-tree-toggle::before { content: "›"; font-size: 1.125rem; transition: transform .14s ease; }
  .z-tree-toggle[aria-expanded="true"]::before { transform: rotate(90deg); }
  .z-tree-toggle[aria-expanded="false"] ~ .z-tree-label + ul { display: none; }
  .z-tree-spacer { width: 20px; }

  .z-stat { min-height: 104px; position: relative; overflow: hidden; box-shadow: var(--z-elevation-raised), var(--z-shadow-inset); }
  /* The glow belongs behind the icon. On a card without one it sat in the
     corner on its own and read as a smudge, not as decoration. */
  .z-stat:has(.z-stat-icon)::after { content: ""; position: absolute; width: 120px; height: 120px; border-radius: 50%; right: -42px; top: -46px; background: radial-gradient(circle, color-mix(in srgb, var(--z-primary) 22%, transparent), transparent 68%); }
  .z-stat-icon { position: absolute; top: var(--z-card-pad); right: var(--z-card-pad); width: 22px; height: 22px; color: var(--z-primary-strong); }
  .z-stat:has(.z-stat-icon) .z-stat-label { padding-right: 30px; }
  .z-stat-label { color: var(--z-muted); font-size: var(--z-font-sm); text-transform: uppercase; letter-spacing: .06em; }
  .z-stat-value { margin-top: var(--z-space-2); font-size: var(--z-font-2xl); font-weight: 750; line-height: var(--z-leading-tight); }
  .z-progress { height: 7px; overflow: hidden; border-radius: 999px; background: var(--z-border); box-shadow: inset 0 1px 2px rgba(0, 0, 0, .14); }
  /* Width is animated so a bar that jumps from 12 to 60 reads as progress
     rather than as a redraw. An indeterminate bar has no width to animate: it
     is a short segment travelling across the track, which says "working, total
     unknown" without claiming a position it does not have. */
  .z-progress > span { display: block; width: var(--z-progress, 0%); height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--z-primary-strong), var(--z-primary)); transition: width var(--z-motion-slow) var(--z-ease-standard); }
  .z-progress.is-indeterminate > span { width: 36%; animation: z-progress-slide 1.15s var(--z-ease-standard) infinite; }
  @keyframes z-progress-slide { from { transform: translateX(-115%); } to { transform: translateX(315%); } }
  /* A strip of running background jobs, each one a label, a reading and a
     .z-progress bar. It came over from the 0.2.0 line with the progress bar it
     wraps and went missing in the 0.15.0 port, the same way .z-num did: it was
     older than the changelog the port worked from. An application that reports
     two or three background jobs at once should not lay that out itself.

     The columns fit as many jobs as the width allows and no fewer than one, so
     a single running job does not sit in a narrow column beside empty space. */
  /* Ranked bars: how much, by category. The form answers "which is biggest and by how
     much", which a reader gets from bar length far faster than from a ring or a number
     column -- and unlike a pie it stays readable past four categories.

     Length is the whole encoding, so the bars share one colour: a different hue per row
     would say the rows differ in kind, when they differ only in size. Every bar carries
     its own value, because a bar without a number can be compared but not quoted.

     The track is drawn under every bar so an empty category is visibly empty rather than
     missing, and the fill is rounded only at the data end -- the baseline end must stay
     square or short bars look like they float off the axis. */
  .z-bars { display: grid; gap: var(--z-space-2); min-width: 0; }
  .z-bar { display: grid; grid-template-columns: minmax(80px, 30%) 1fr auto; align-items: center; gap: var(--z-space-3); min-width: 0; }
  .z-bar-label { font-size: var(--z-font-sm); color: var(--z-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .z-bar-track { height: 10px; border-radius: 2px; background: var(--z-viz-track); overflow: hidden; }
  .z-bar-fill { display: block; height: 100%; width: var(--z-bar, 0%); border-radius: 0 4px 4px 0; background: var(--z-bar-color, var(--z-viz-1)); transition: width var(--z-motion-slow) var(--z-ease-standard); }
  .z-bar-value { font-size: var(--z-font-sm); font-variant-numeric: tabular-nums; color: var(--z-muted); min-width: 3ch; text-align: right; }
  /* Under a narrow column the label needs its own line, or it is squeezed to an ellipsis
     and the chart stops naming what it measures. */
  @media (max-width: 560px) {
    .z-bar { grid-template-columns: 1fr auto; grid-template-areas: "label value" "track track"; }
    .z-bar-label { grid-area: label; }
    .z-bar-value { grid-area: value; }
    .z-bar-track { grid-area: track; }
  }

  .z-operation-progress { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--z-space-3); padding: var(--z-space-3); border: 1px solid var(--z-border); border-radius: var(--z-radius-md); background: var(--z-surface); box-shadow: var(--z-shadow-sm); }
  .z-operation { display: grid; gap: var(--z-space-2); min-width: 0; }
  .z-operation-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--z-space-3); font-weight: 650; }
  /* Tabular figures: a count that climbs must not shift the text beside it. */
  .z-operation-meta { color: var(--z-muted); font-size: var(--z-font-xs); font-variant-numeric: tabular-nums; white-space: nowrap; }

  .z-chart { display: flex; align-items: center; gap: var(--z-space-5); flex-wrap: wrap; }
  .z-chart-ring { --z-ring-size: 208px; --z-ring-width: 26; width: var(--z-ring-size); height: var(--z-ring-size); position: relative; flex: 0 0 auto; }
  .z-chart-ring > svg { width: 100%; height: 100%; overflow: visible; filter: drop-shadow(0 7px 12px rgba(24, 37, 49, .30)) drop-shadow(0 1px 1px rgba(24, 37, 49, .18)); }
  .z-chart-ring circle { fill: none; stroke-width: var(--z-ring-width); }
  .z-chart-ring .z-ring-track { stroke: var(--z-viz-track); }
  .z-chart-ring .z-ring-arc { stroke-linecap: butt; transition: stroke-dashoffset var(--z-motion-slow) var(--z-ease-emphasized); }
  .z-chart-ring .z-ring-arc:hover { filter: brightness(1.07) saturate(1.06); }
  /* Lit from the top left across the whole ring, so the segments read as one
     raised object rather than as separately shaded arcs. */
  .z-chart-ring .z-ring-gloss { stroke: url(#z-ring-gloss); pointer-events: none; }
  .z-chart-ring .z-ring-edge { stroke-width: 1.4; }
  .z-chart-ring .z-ring-edge-outer { stroke: rgba(12, 20, 28, .28); }
  .z-chart-ring .z-ring-edge-inner { stroke: rgba(255, 255, 255, .55); }
  .z-chart-center { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; pointer-events: none; }
  .z-chart-center strong { display: block; font-size: var(--z-font-xl); font-weight: 750; line-height: var(--z-leading-tight); font-variant-numeric: tabular-nums; }
  .z-chart-center small { color: var(--z-muted); font-size: var(--z-font-sm); }

  .z-chart-legend { flex: 1 1 190px; min-width: 0; margin: 0; padding: 0; list-style: none; display: grid; gap: var(--z-space-2); }
  .z-chart-legend li { min-width: 0; display: grid; grid-template-columns: 10px minmax(0, 1fr) auto auto; align-items: center; gap: var(--z-space-2); font-size: var(--z-font-sm); }
  .z-chart-legend i { min-width: 32px; color: var(--z-muted); font-size: var(--z-font-xs); font-style: normal; font-variant-numeric: tabular-nums; text-align: right; }

  /* Line chart: stretched to its box, so the stroke is drawn in user units and
     given a non-scaling width to stay even when the box is wider than tall. */
  .z-line-chart { display: block; width: 100%; height: var(--z-line-h, 64px); overflow: visible; }
  .z-line-chart .z-line-stroke { fill: none; stroke: var(--z-line-color, var(--z-primary-strong)); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
  .z-line-chart .z-line-marker { stroke: var(--z-line-color, var(--z-primary-strong)); stroke-width: 1.4; stroke-dasharray: 2 2; opacity: .55; vector-effect: non-scaling-stroke; }
  .z-chart-legend li > span:nth-child(2) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .z-chart-swatch { width: 10px; height: 10px; border-radius: 3px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12); }
  .z-chart-legend b { font-weight: 700; font-variant-numeric: tabular-nums; }

  /* Says that something is happening right now — a scan, a sync, a deploy.
     Distinct from a status badge, which reports a state that persists. */
  /* Safari cannot be asked to install a page, so the page asks the person.
     Sits above the browser's own bottom bar, out of the way of the content. */
  .z-install-hint {
    position: fixed;
    left: max(var(--z-space-4), env(safe-area-inset-left));
    right: max(var(--z-space-4), env(safe-area-inset-right));
    bottom: calc(var(--z-space-4) + env(safe-area-inset-bottom));
    z-index: 60;
    display: flex;
    align-items: center;
    gap: var(--z-space-3);
    padding: var(--z-space-3) var(--z-space-4);
    border: 1px solid var(--z-border);
    border-radius: var(--z-radius-md);
    background: var(--z-surface-overlay);
    box-shadow: var(--z-elevation-overlay);
    backdrop-filter: blur(10px);
    animation: z-hint-in var(--z-motion-slow) var(--z-ease-emphasized) both;
  }
  .z-install-hint-body { flex: 1 1 auto; min-width: 0; display: grid; gap: 2px; font-size: var(--z-font-sm); }
  .z-install-hint-body span { color: var(--z-muted); }
  .z-install-hint-body b { color: var(--z-text); font-weight: 700; }
  @keyframes z-hint-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
  @media (prefers-reduced-motion: reduce) { .z-install-hint { animation: none; } }

  .z-live { display: inline-flex; align-items: center; gap: var(--z-space-2); padding: 3px 10px; border: 1px solid color-mix(in srgb, var(--z-primary) 45%, var(--z-border)); border-radius: 999px; background: var(--z-primary-softer); color: var(--z-accent-text); font-size: var(--z-font-sm); font-weight: 650; }
  .z-live-dot { width: 8px; height: 8px; flex: 0 0 auto; border-radius: 50%; background: var(--z-primary-strong); animation: z-live-pulse 1.1s ease-in-out infinite; }
  .z-live.ok { border-color: color-mix(in srgb, var(--z-success) 45%, var(--z-border)); background: var(--z-success-soft); color: var(--z-success-strong); }
  .z-live.ok .z-live-dot { background: var(--z-success-strong); animation: none; }
  @keyframes z-live-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .3; transform: scale(.7); } }
  @media (prefers-reduced-motion: reduce) { .z-live-dot { animation: none; } }

  /* Packs blocks of uneven height into columns. A row-based grid would leave a
     tall gap under every short block; columns fill from the top instead. */
  .z-columns { columns: var(--z-column-min, 360px) var(--z-column-count, 3); column-gap: var(--z-layout-gap); }
  .z-columns > * { break-inside: avoid; margin-bottom: var(--z-space-6); }

  /* An action at the end of a row: findable, but not competing with the row. */
  .z-row-action { opacity: .4; transition: opacity var(--z-motion-normal) var(--z-ease-standard); }
  :is(li, tr, .z-tile):hover .z-row-action, .z-row-action:focus-visible { opacity: 1; }

  .z-feed { margin: 0; padding: 0 var(--z-space-3) 0 0; list-style: none; display: grid; max-height: var(--z-feed-max-h, 232px); overflow-y: auto; scrollbar-gutter: stable; }
  .z-feed li { min-width: 0; display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: baseline; gap: var(--z-space-3); padding: var(--z-space-2) 0; border-bottom: 1px solid var(--z-border-soft); font-size: var(--z-font-sm); }
  /* Padding on the right, not only scrollbar-gutter: iOS does not support the
     gutter and draws its scrollbar over the content, so the bar was sitting on
     top of the last few characters of every row. */
  .z-feed li:last-child { border-bottom: 0; }
  .z-feed time { color: var(--z-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
  .z-feed-text { min-width: 0; overflow-wrap: anywhere; }

  /* A roster: things with a name, a detail and a reading on the right. Built
     for lists of machines or people, where the reading is what gets scanned. */
  .z-roster { margin: 0; padding: 0 var(--z-space-3) 0 0; list-style: none; display: grid; max-height: var(--z-roster-max-h, 320px); overflow-y: auto; scrollbar-gutter: stable; }
  .z-roster li { min-width: 0; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--z-space-3); padding: var(--z-space-2) 0; border-bottom: 1px solid var(--z-border-soft); font-size: var(--z-font-sm); }
  .z-roster li:last-child { border-bottom: 0; }
  .z-roster .z-icon { color: var(--z-muted); flex: none; }
  .z-roster-name { min-width: 0; display: grid; }
  .z-roster-name strong { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .z-roster-sub { color: var(--z-muted); font-size: var(--z-font-xs); font-variant-numeric: tabular-nums; }
  .z-roster-value { color: var(--z-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
  .z-roster li.is-off { opacity: .55; }

  .z-metrics { display: flex; flex-wrap: wrap; gap: var(--z-space-1) var(--z-space-2); }
  .z-metric { gap: var(--z-space-1); padding: 3px 9px; font-variant-numeric: tabular-nums; }
  .z-metric > span:first-child { color: var(--z-muted); }
  .z-metric > b { font-weight: 700; }
  .z-metric.is-on { color: var(--z-success-strong); border-color: color-mix(in srgb, var(--z-success) 38%, var(--z-border)); background: var(--z-success-soft); }
  .z-metric.is-alert { color: var(--z-danger-strong); border-color: color-mix(in srgb, var(--z-danger) 38%, var(--z-border)); background: var(--z-danger-soft); }

  /* --z-tile-min lets a page raise the floor for tiles that carry more than
     a name, such as device tiles with a headline reading. */
  .z-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--z-tile-min, 240px), 1fr)); gap: var(--z-layout-gap); }
  .z-tile { min-width: 0; display: grid; gap: var(--z-space-3); align-content: start; padding: var(--z-space-4); border: 1px solid var(--z-border); border-radius: var(--z-radius-md); background: linear-gradient(180deg, var(--z-surface-raised), var(--z-surface)); box-shadow: var(--z-elevation-panel), var(--z-shadow-inset); transition: border-color var(--z-motion-normal) var(--z-ease-standard), box-shadow var(--z-motion-normal) var(--z-ease-standard), transform var(--z-motion-normal) var(--z-ease-standard); }
  .z-tile:hover { border-color: var(--z-border-strong); box-shadow: var(--z-elevation-raised), var(--z-shadow-inset); transform: translateY(-1px); }
  .z-tile-head { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--z-space-2); }
  .z-tile-name { min-width: 0; overflow: hidden; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
  .z-tile.is-offline { opacity: .74; }

  /* Device tile: an icon medallion carries the kind of device, so a room full
     of tiles can be read by shape before a single label is. The medallion also
     carries the state — lit when the device draws power, red when it alarms —
     because that is the one thing worth seeing across the room. */
  .z-device-tile .z-tile-head { grid-template-columns: auto minmax(0, 1fr) auto; }
  .z-tile-icon { width: 42px; height: 42px; display: grid; place-items: center; flex: 0 0 auto; color: var(--z-muted); border: 1px solid var(--z-border); border-radius: var(--z-radius-md); background: linear-gradient(160deg, var(--z-surface-raised), var(--z-surface-2)); box-shadow: var(--z-shadow-inset), var(--z-shadow-sm); transition: color var(--z-motion-normal) var(--z-ease-standard), background var(--z-motion-normal) var(--z-ease-standard), border-color var(--z-motion-normal) var(--z-ease-standard), box-shadow var(--z-motion-normal) var(--z-ease-standard); }
  .z-tile-icon .z-icon { width: 24px; height: 24px; }
  .z-tile-titles { min-width: 0; display: grid; }
  .z-tile-sub { min-width: 0; overflow: hidden; color: var(--z-muted); font-size: var(--z-font-xs); text-overflow: ellipsis; white-space: nowrap; }
  .z-tile-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--z-space-2); }
  .z-tile-readout { display: flex; align-items: baseline; gap: var(--z-space-2); min-width: 0; }
  .z-tile-readout strong { font-size: var(--z-font-xl); font-weight: 750; line-height: var(--z-leading-tight); font-variant-numeric: tabular-nums; }
  .z-tile-readout small { min-width: 0; overflow: hidden; color: var(--z-muted); font-size: var(--z-font-sm); text-overflow: ellipsis; white-space: nowrap; }
  /* .is-on namerno ne boji ikonu: svaka ploca sa prekidacem vec nosi .z-switch koji
     stoji na upaljeno, a onaj bez njega pokazuje potrosnju kao broj. Boja je isto
     govorila trece put, pa su upaljeni uredjaji ispadali iz reda ikona oko sebe --
     jedina razlika koju je oko hvatalo pre imena. Stanje nosi kontrola, ne ikona.
     .is-alert ispod je izuzetak i to ostaje: kvar mora da se vidi pre nego sto se
     procita. */
  .z-device-tile.is-alert .z-tile-icon { color: var(--z-danger-strong); border-color: color-mix(in srgb, var(--z-danger) 48%, var(--z-border)); background: linear-gradient(160deg, var(--z-danger-soft), color-mix(in srgb, var(--z-danger-soft) 62%, var(--z-surface-2))); box-shadow: var(--z-shadow-inset), 0 0 0 3px color-mix(in srgb, var(--z-danger) 16%, transparent); }
  .z-device-tile.is-offline .z-tile-icon { background: var(--z-surface-2); box-shadow: none; }

  .z-section-head { display: flex; align-items: baseline; gap: var(--z-space-3); margin-bottom: var(--z-space-3); }
  .z-section-icon { width: 22px; height: 22px; align-self: center; color: var(--z-primary-strong); }
  .z-section-head h2 { margin: 0; font-size: var(--z-font-lg); letter-spacing: -.2px; }
  .z-section-rule { flex: 1 1 auto; height: 1px; background: linear-gradient(90deg, var(--z-border), transparent); }

  .z-chart-row { display: grid; grid-template-columns: .8fr 1.4fr; gap: 16px; }
  .z-chart-box { min-height: 260px; display: grid; place-items: center; }
  .z-donut { --z-donut-size: min(220px, 70vw); width: var(--z-donut-size); container-type: inline-size; aspect-ratio: 1; border: 1px solid color-mix(in srgb, var(--z-border-strong) 74%, transparent); border-radius: 50%; position: relative; isolation: isolate; background: conic-gradient(from -90deg, #9ab6d7 0, #7598c3 24%, #587ba7 42%, #9bc2a5 42%, #73a582 57%, #4d7e5c 70%, #e0c184 70%, #c9a15d 80%, #9f7737 88%, #dda0a0 88%, #c36e6e 95%, #984d52 100%); box-shadow: inset 12px 14px 20px rgba(255, 255, 255, .30), inset -14px -16px 22px rgba(31, 48, 64, .24), 0 7px 0 -3px rgba(46, 63, 78, .22), 0 18px 30px rgba(32, 48, 62, .18); }
  .z-donut::after { content: ""; position: absolute; inset: 1px; z-index: 1; border-radius: 50%; pointer-events: none; background: radial-gradient(circle at 31% 24%, rgba(255, 255, 255, .54), transparent 34%), linear-gradient(145deg, rgba(255, 255, 255, .12), transparent 43%, rgba(24, 38, 51, .16)); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .22); }
  .z-donut::before { content: ""; position: absolute; inset: 27%; z-index: 2; border: 1px solid color-mix(in srgb, var(--z-border) 78%, transparent); border-radius: 50%; background: linear-gradient(145deg, var(--z-surface-raised), var(--z-surface-2)); box-shadow: -5px -5px 10px rgba(255, 255, 255, .34), 5px 6px 12px rgba(30, 44, 57, .18), inset 3px 3px 7px rgba(255, 255, 255, .22), inset -4px -4px 9px rgba(50, 65, 77, .10); }
  .z-donut-center { position: absolute; inset: 0; z-index: 3; display: grid; place-items: center; text-align: center; }
  /* The hole is 46% of the ring, so the label has to be measured in the ring's
     own size. At a flat 30px it fitted the 220px default and spilled over the
     rim on every smaller one, where the word underneath ran into the colours. */
  .z-donut-center > * { max-width: 44%; }
  /* Measured against the ring's own width with cqw, not with calc() on the
     size variable: that variable holds min(180px, 100%), and inside a
     font-size a percentage means the parent's font size, not a width -- so
     the figure resolved to about 2px and vanished into the hole. The px
     values are the fallback for anything without container queries. */
  .z-donut-center strong { display: block; font-size: 1.875rem; font-size: 17cqw; line-height: 1.1; }
  .z-donut-center .z-subtle { font-size: var(--z-font-sm); font-size: 6.2cqw; }
  .z-step-chart { width: 100%; height: 260px; display: block; }
  .z-step-chart .grid { stroke: var(--z-border); stroke-width: 1; }
  .z-step-chart .area { fill: url(#zBlueArea); }
  .z-step-chart .line { fill: none; stroke: var(--z-primary); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; filter: drop-shadow(0 4px 4px rgba(71, 100, 131, .20)); }
  .z-step-chart .point { fill: var(--z-surface-raised); stroke: var(--z-primary); stroke-width: 2; }

  .z-context {
    position: fixed;
    min-width: 200px;
    max-width: calc(100vw - 24px);
    padding: 6px;
    border: 1px solid var(--z-border);
    border-radius: 9px;
    display: none;
    background: var(--z-surface-overlay);
    box-shadow: var(--z-shadow-lg);
    backdrop-filter: blur(12px);
    z-index: 1100;
  }
  .z-context.is-open { display: block; }
  .z-context-item { width: 100%; min-height: 32px; border: 0; border-radius: 6px; padding: 0 10px; display: flex; align-items: center; gap: 9px; background: transparent; color: var(--z-text); text-align: left; cursor: pointer; }
  .z-context-item:hover, .z-context-item:focus-visible { background: var(--z-primary-soft); }
  .z-context-item.danger { color: var(--z-danger-strong); }
  .z-context-item:disabled { opacity: .45; cursor: not-allowed; }
  .z-context-sep { height: 1px; background: var(--z-border); margin: 5px 4px; }
  /* Names what the menu is acting on, so one menu can serve several tables. */
  .z-context-title { max-width: 260px; margin-bottom: 5px; padding: 6px 10px 8px; border-bottom: 1px solid var(--z-border); color: var(--z-muted); font-size: var(--z-font-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  .z-tooltip {
    max-width: min(280px, calc(100vw - 20px));
    position: fixed;
    z-index: 1400;
    visibility: hidden;
    opacity: 0;
    padding: 6px 9px;
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 6px;
    background: var(--z-tooltip-bg);
    color: var(--z-tooltip-text);
    box-shadow: 0 7px 18px rgba(16, 24, 31, .26), inset 0 1px 0 rgba(255, 255, 255, .10);
    font-size: var(--z-font-sm);
    line-height: 1.35;
    pointer-events: none;
    transform: translateY(3px);
    transition: opacity var(--z-motion-fast) var(--z-ease-standard), transform var(--z-motion-fast) var(--z-ease-emphasized), visibility var(--z-motion-fast);
  }
  .z-tooltip.is-open { visibility: visible; opacity: 1; transform: translateY(0); }

  .z-popover {
    width: max-content;
    min-width: 240px;
    max-width: min(380px, calc(100vw - 20px));
    position: fixed;
    z-index: 1350;
    display: none;
    border: 1px solid var(--z-border);
    border-radius: var(--z-radius-md);
    background: var(--z-surface-overlay);
    box-shadow: var(--z-elevation-overlay), var(--z-shadow-inset);
    backdrop-filter: blur(12px);
  }
  .z-popover.is-open { display: block; animation: z-overlay-in var(--z-motion-normal) var(--z-ease-emphasized) both; }
  .z-popover-header { min-height: 42px; padding: 8px 11px; border-bottom: 1px solid var(--z-border); display: flex; align-items: center; gap: 8px; font-weight: 750; }
  .z-popover-body { padding: 12px; }
  .z-popover-footer { min-height: 42px; padding: 7px 10px; border-top: 1px solid var(--z-border); display: flex; justify-content: flex-end; gap: 7px; background: var(--z-surface-2); border-radius: 0 0 var(--z-radius-md) var(--z-radius-md); }
  @keyframes z-overlay-in { from { opacity: 0; transform: translateY(4px) scale(.985); } to { opacity: 1; transform: translateY(0) scale(1); } }

  .z-accordion { display: grid; gap: 7px; }
  .z-details { overflow: clip; border: 1px solid var(--z-border); border-radius: var(--z-radius-sm); background: var(--z-surface-raised); box-shadow: var(--z-elevation-panel); }
  .z-details > summary { min-height: 38px; padding: 7px 11px; display: flex; align-items: center; gap: 9px; background: linear-gradient(180deg, var(--z-surface-raised), var(--z-surface-2)); color: var(--z-text); font-weight: 700; cursor: pointer; list-style: none; user-select: none; }
  .z-details > summary::-webkit-details-marker { display: none; }
  .z-details > summary::before { content: ""; width: 7px; height: 7px; flex: 0 0 auto; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; color: var(--z-muted); transform: rotate(-45deg); transition: transform var(--z-motion-normal) var(--z-ease-emphasized); }
  .z-details[open] > summary { border-bottom: 1px solid var(--z-border-soft); color: var(--z-primary-strong); }
  .z-details[open] > summary::before { transform: rotate(45deg); }
  .z-details-body { padding: var(--z-space-3); color: var(--z-muted); }
  .z-details[open] .z-details-body { animation: z-details-in var(--z-motion-slow) var(--z-ease-emphasized) both; }
  @keyframes z-details-in { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: translateY(0); } }
  .z-details[aria-disabled="true"] { opacity: .5; }
  .z-details[aria-disabled="true"] > summary { cursor: not-allowed; }

  .z-menu-bar { min-width: 0; position: relative; }
  .z-menu-root { min-height: var(--z-control-h); width: max-content; max-width: 100%; padding: 3px; border: 1px solid var(--z-border); border-radius: var(--z-radius-sm); display: flex; align-items: center; gap: 2px; background: linear-gradient(180deg, var(--z-surface-raised), var(--z-surface-2)); box-shadow: var(--z-shadow-sm), var(--z-shadow-inset); }
  .z-menu-trigger { min-height: 28px; border: 1px solid transparent; border-radius: 5px; padding: 0 10px; display: inline-flex; align-items: center; gap: 6px; background: transparent; color: var(--z-text); cursor: pointer; white-space: nowrap; }
  .z-menu-trigger:hover, .z-menu-trigger:focus-visible, .z-menu-trigger[aria-expanded="true"] { border-color: var(--z-border); background: var(--z-primary-soft); color: var(--z-primary-on-soft); }
  .z-menu-popup { min-width: 210px; max-width: min(300px, calc(100vw - 20px)); position: fixed; z-index: 1370; display: none; padding: 5px; border: 1px solid var(--z-border); border-radius: var(--z-radius-sm); background: var(--z-surface-overlay); box-shadow: var(--z-elevation-overlay); backdrop-filter: blur(12px); }
  .z-menu-popup.is-open { display: block; animation: z-overlay-in var(--z-motion-fast) var(--z-ease-emphasized) both; }
  .z-menu-item { width: 100%; min-height: 32px; border: 0; border-radius: 5px; padding: 5px 9px; display: grid; grid-template-columns: 18px minmax(0, 1fr) auto; gap: 8px; align-items: center; background: transparent; color: var(--z-text); text-align: left; cursor: pointer; }
  .z-menu-item:hover, .z-menu-item:focus-visible { background: var(--z-primary-soft); color: var(--z-primary-on-soft); }
  .z-menu-item.danger { color: var(--z-danger-strong); }
  .z-menu-item:disabled, .z-menu-item[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
  .z-menu-item[data-z-submenu]::after { content: ""; width: 6px; height: 6px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); }
  .z-menu-shortcut { color: var(--z-muted); font-size: var(--z-font-xs); }
  .z-menu-separator { height: 1px; margin: 5px 4px; background: var(--z-border); }

  .z-split { --z-split-position: 40%; min-width: 0; min-height: 300px; overflow: hidden; border: 1px solid var(--z-border); border-radius: var(--z-radius-md); display: grid; grid-template-columns: minmax(0, var(--z-split-position)) 9px minmax(0, 1fr); background: var(--z-surface); box-shadow: var(--z-shadow-sm); }
  .z-split[data-z-orientation="vertical"], .z-split[data-z-stacked="true"] { height: 380px; grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, var(--z-split-position)) 9px minmax(0, 1fr); }
  .z-split-pane { min-width: 0; min-height: 0; overflow: auto; padding: 14px; background: var(--z-surface-raised); }
  .z-splitter + .z-split-pane { background: var(--z-surface); }
  .z-splitter { position: relative; z-index: 2; border: 0; padding: 0; background: linear-gradient(90deg, var(--z-border-soft), var(--z-border-strong), var(--z-border-soft)); cursor: col-resize; touch-action: none; }
  .z-splitter::after { content: ""; width: 3px; height: 34px; position: absolute; inset: 50% auto auto 50%; border-left: 1px solid color-mix(in srgb, var(--z-muted) 72%, transparent); border-right: 1px solid color-mix(in srgb, var(--z-muted) 72%, transparent); transform: translate(-50%, -50%); }
  :is(.z-split[data-z-orientation="vertical"], .z-split[data-z-stacked="true"]) > .z-splitter { background: linear-gradient(180deg, var(--z-border-soft), var(--z-border-strong), var(--z-border-soft)); cursor: row-resize; }
  :is(.z-split[data-z-orientation="vertical"], .z-split[data-z-stacked="true"]) > .z-splitter::after { width: 34px; height: 3px; border: 0; border-top: 1px solid color-mix(in srgb, var(--z-muted) 72%, transparent); border-bottom: 1px solid color-mix(in srgb, var(--z-muted) 72%, transparent); }
  .z-splitter:hover, .z-splitter:focus-visible, .z-split.is-resizing > .z-splitter { background: var(--z-primary); box-shadow: 0 0 0 2px color-mix(in srgb, var(--z-primary) 24%, transparent); }

  .z-dialog-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1000;
    padding: 10px;
    display: none;
    place-items: center;
    background: var(--z-overlay);
    backdrop-filter: blur(3px);
  }
  .z-dialog-backdrop.is-open { display: grid; }
  .z-dialog { width: min(560px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); overflow: auto; border: 1px solid var(--z-border); border-radius: var(--z-radius-md); background: var(--z-surface-raised); box-shadow: var(--z-elevation-overlay), var(--z-shadow-inset); animation: z-overlay-in var(--z-motion-slow) var(--z-ease-emphasized) both; }
  /* The dialog is the page — a login, say — so the backdrop paints the app
     background with a glow behind the panel instead of dimming what is below. */
  .z-dialog-backdrop.is-page { background: radial-gradient(1100px 600px at 50% -8%, color-mix(in srgb, var(--z-primary) 18%, transparent), transparent 68%); backdrop-filter: none; }
  .z-dialog-sm { width: min(400px, calc(100vw - 32px)); }
  .z-dialog-sm .z-dialog-footer .z-btn { width: 100%; }

  .z-dialog-header, .z-dialog-footer { min-height: 54px; padding: 10px 16px; display: flex; align-items: center; gap: 10px; }
  .z-dialog-header { border-bottom: 1px solid var(--z-border); }
  .z-dialog-title { margin: 0; font-size: var(--z-font-lg); }
  .z-dialog-body { padding: 18px 16px; }
  .z-dialog-footer { justify-content: flex-end; border-top: 1px solid var(--z-border); background: var(--z-surface-2); }

  /* A dialog driven by the hidden attribute rather than by .is-open, for a
     page that opens it from script through ZemoUI.modal(). The panel is also
     a .z-card, so it keeps the surface every other panel has instead of
     inventing a second look for dialogs. */
  .z-modal-backdrop { position: fixed; inset: 0; z-index: 1200; display: grid; place-items: center; padding: 20px; background: var(--z-backdrop); backdrop-filter: blur(2px); }
  .z-modal { width: var(--z-modal-w); max-height: var(--z-modal-h); overflow: auto; animation: z-overlay-in var(--z-motion-slow) var(--z-ease-emphasized) both; }
  .z-modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
  .z-modal-head :is(h2, h3) { margin: 0 0 14px; }

  /* The dialog ZemoUI.notice() and ZemoUI.confirm() build. It is narrower than
     a .z-modal a page writes itself, because it holds a sentence and two
     buttons and a wide panel makes that sentence hard to read. */
  .z-alert { width: min(460px, 100%); }
  .z-alert-message { margin: 0; white-space: pre-wrap; line-height: var(--z-leading-normal); overflow-wrap: anywhere; }
  .z-alert-actions { display: flex; justify-content: flex-end; gap: var(--z-space-2); margin-top: var(--z-space-6); }
  /* The tone colours the border rather than the whole panel: it should mark the
     dialog without turning a message the operator must read into a warning
     sign they look past. */
  .z-modal-backdrop[data-tone="danger"] .z-alert { border-color: color-mix(in srgb, var(--z-danger) 62%, var(--z-border)); }
  .z-modal-backdrop[data-tone="warning"] .z-alert { border-color: color-mix(in srgb, var(--z-warning) 62%, var(--z-border)); }
  .z-modal-backdrop[data-tone="success"] .z-alert { border-color: color-mix(in srgb, var(--z-success) 62%, var(--z-border)); }

  /* Label and value pairs inside a panel: <dl class="z-fields">. The value
     column is minmax(0, 1fr) so a long unbroken string wraps instead of
     stretching the dialog past the width its token asked for. */
  .z-fields { margin: 0; display: grid; grid-template-columns: var(--z-fields-label-w) minmax(0, 1fr); gap: 9px 14px; align-items: start; }
  .z-fields dt { color: var(--z-muted); font-size: var(--z-font-xs); font-weight: 750; letter-spacing: .035em; text-transform: uppercase; }
  .z-fields dd { margin: 0; overflow-wrap: anywhere; }

  .z-toast-region { position: fixed; inset: 18px 18px auto auto; z-index: 1200; width: min(360px, calc(100vw - 36px)); display: grid; gap: 10px; pointer-events: none; }
  .z-toast { position: relative; padding: 13px 38px 13px 14px; border: 1px solid var(--z-border); border-left: 4px solid var(--z-info); border-radius: var(--z-radius-sm); background: var(--z-surface-overlay); box-shadow: var(--z-elevation-overlay); pointer-events: auto; animation: z-toast-in var(--z-motion-slow) var(--z-ease-emphasized) both; }
  .z-toast[data-type="success"] { border-left-color: var(--z-success); }
  .z-toast[data-type="warning"] { border-left-color: var(--z-warning); }
  .z-toast[data-type="danger"] { border-left-color: var(--z-danger); }
  .z-toast-title { display: block; margin-bottom: 3px; font-weight: 750; }
  .z-toast-close { position: absolute; top: 7px; right: 7px; width: 26px; height: 26px; border: 0; border-radius: 5px; background: transparent; color: var(--z-muted); cursor: pointer; }
  .z-toast-close:hover { background: var(--z-hover); color: var(--z-text); }
  .z-toast.is-leaving { animation: z-toast-out .16s ease both; }
  @keyframes z-toast-in { from { opacity: 0; transform: translateY(-8px) scale(.98); } }
  @keyframes z-toast-out { to { opacity: 0; transform: translateX(12px); } }

  /* A short label above a heading, often carrying a section number. */
  .z-eyebrow {
    display: flex;
    align-items: center;
    gap: var(--z-space-3);
    color: var(--z-muted);
    font-size: var(--z-font-xs);
    letter-spacing: .18em;
    text-transform: uppercase;
  }
  .z-eyebrow span { color: var(--z-primary); font-variant-numeric: tabular-nums; }

  /* An aside that qualifies what is on screen — not an error, but something
     the reader should know before drawing a conclusion from the data. */
  .z-notice {
    padding: var(--z-space-3) var(--z-space-4);
    border-left: 2px solid var(--z-primary);
    border-radius: 0 var(--z-radius-sm) var(--z-radius-sm) 0;
    background: color-mix(in srgb, var(--z-primary) 7%, transparent);
    color: var(--z-muted);
    font-size: var(--z-font-sm);
    line-height: 1.5;
  }
  .z-notice.is-warning { border-left-color: var(--z-warning); background: color-mix(in srgb, var(--z-warning) 8%, transparent); }
  .z-notice.is-danger { border-left-color: var(--z-danger); background: color-mix(in srgb, var(--z-danger) 8%, transparent); }
  .z-notice.is-success { border-left-color: var(--z-success); background: color-mix(in srgb, var(--z-success) 8%, transparent); }

  /* Decoration only: a soft glow and a ruled grid behind the page. Both are
     inert to the pointer and hidden from assistive technology by the author. */
  .z-ambient {
    position: fixed;
    z-index: 0;
    width: 24rem;
    height: 24rem;
    border-radius: 50%;
    filter: blur(100px);
    pointer-events: none;
    opacity: .1;
    background: var(--z-primary);
  }
  .z-ambient-start { top: 5rem; left: -12rem; }
  .z-ambient-end { top: 42rem; right: -14rem; }
  .z-ambient-grid {
    position: fixed;
    z-index: 0;
    inset: 0;
    pointer-events: none;
    opacity: .22;
    background-image:
      linear-gradient(var(--z-border-soft) 1px, transparent 1px),
      linear-gradient(90deg, var(--z-border-soft) 1px, transparent 1px);
    background-size: 72px 72px;
    mask-image: linear-gradient(to bottom, black, transparent 78%);
  }
  @media (prefers-reduced-motion: reduce) { .z-ambient { display: none; } }

  /* A run of label/value pairs in a hairline grid. A <dl> is the honest
     element for "this is called that", and every application that shows a
     record -- a captured request, a device, a disk -- wants the same shape. */
  .z-def-grid { display: grid; grid-template-columns: repeat(var(--z-def-columns, 2), minmax(0, 1fr)); gap: 1px; margin: 0; overflow: hidden; border: 1px solid var(--z-border-soft); border-radius: var(--z-radius-sm); background: var(--z-border-soft); }
  .z-def-grid > div { min-width: 0; padding: var(--z-space-3); background: var(--z-surface-raised); }
  .z-def-grid dt { margin-bottom: var(--z-space-1); color: var(--z-muted); font-size: var(--z-font-xs); font-weight: 700; letter-spacing: .045em; text-transform: uppercase; }
  .z-def-grid dd { min-width: 0; margin: 0; overflow-wrap: anywhere; font-weight: 650; }
  @media (max-width: 620px) { .z-def-grid { grid-template-columns: 1fr; } }

  /* One state, said once: a round icon, a headline and a line of detail.
     The tone classes carry the meaning so the markup does not repeat it. */
  .z-status-strip { display: flex; align-items: center; gap: var(--z-space-4); padding: var(--z-space-4); border: 1px solid var(--z-border-soft); border-radius: var(--z-radius-md); background: var(--z-surface-2); }
  .z-status-icon { flex: 0 0 48px; width: 48px; height: 48px; display: grid; place-items: center; border-radius: 50%; color: var(--z-primary-on-soft); background: var(--z-primary-soft); box-shadow: var(--z-shadow-inset); }
  .z-status-icon .z-icon { width: 26px; height: 26px; }
  .z-status-title { display: block; margin-bottom: var(--z-space-1); font-size: var(--z-font-lg); }
  .z-status-strip.is-success .z-status-icon { color: var(--z-success-strong); background: var(--z-success-soft); }
  .z-status-strip.is-danger .z-status-icon { color: var(--z-danger-strong); background: var(--z-danger-soft); }

  /* An icon, a paragraph and an action on one accent-bordered line. This is
     .z-notice with something to look at and something to do. */
  .z-notice.z-notice-wide { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--z-space-4); align-items: center; padding: var(--z-space-4); border-left-width: 4px; color: var(--z-text); font-size: var(--z-font-md); }
  .z-notice-wide > .z-icon { width: 28px; height: 28px; color: var(--z-primary-strong); }
  .z-notice-wide p { margin: var(--z-space-1) 0 0; color: var(--z-muted); }
  @media (max-width: 620px) {
    .z-notice.z-notice-wide { grid-template-columns: auto minmax(0, 1fr); }
    .z-notice-wide > .z-btn { grid-column: 1 / -1; }
  }

  /* A section that opens with a large heading and carries one control on the
     same line -- a search, a filter, a link out. */
  .z-section-bar { display: flex; gap: var(--z-space-4); align-items: end; justify-content: space-between; margin-bottom: var(--z-space-4); }
  .z-section-bar h2 { margin: var(--z-space-1) 0; font-size: clamp(24px, 3vw, 34px); line-height: 1.15; }
  .z-section-bar p { margin: 0; }
  .z-section-bar :is(.z-input, .z-field, .z-input-group) { width: min(320px, 100%); }
  @media (max-width: 820px) {
    .z-section-bar { align-items: stretch; flex-direction: column; }
    .z-section-bar :is(.z-input, .z-field, .z-input-group) { width: 100%; }
  }

  /* An icon, a badge and a short explanation: a card that describes a thing
     rather than reporting a number. */
  .z-feature-card { display: grid; grid-template-columns: auto 1fr; gap: var(--z-space-3); align-content: start; min-width: 0; }
  .z-feature-card > .z-icon { width: 28px; height: 28px; color: var(--z-primary-strong); }
  .z-feature-card > .z-badge { justify-self: end; }
  .z-feature-card :is(h3, p) { grid-column: 1 / -1; }
  .z-feature-card h3 { margin: var(--z-space-2) 0 0; font-size: var(--z-font-lg); }
  .z-feature-card p { margin: 0; color: var(--z-muted); }

  /* A table read as "this is called that, and here is where it came from".
     Fixed columns so the labels line up down the page, and on a phone each
     row becomes a small card -- the header disappears and every cell prints
     its own label from data-label, because four columns cannot be read at
     360px however hard they try. */
  .z-kv-table { table-layout: fixed; }
  .z-kv-table :is(th, td):first-child { width: var(--z-kv-label-w, 24%); }
  .z-kv-table :is(th, td):nth-child(2) { width: var(--z-kv-value-w, 38%); }
  .z-kv-table td { overflow-wrap: anywhere; vertical-align: top; }
  .z-kv-table td:first-child { color: var(--z-muted); font-size: var(--z-font-xs); font-weight: 750; letter-spacing: .035em; text-transform: uppercase; }
  .z-kv-table td:nth-child(2) { color: var(--z-text); font-family: var(--z-font-mono); font-size: var(--z-font-sm); font-weight: 650; }
  .z-kv-table td:last-child { color: var(--z-muted); font-size: var(--z-font-sm); }
  /* A value the source would not give us is absent, not empty: it reads as
     prose rather than as data so it cannot be mistaken for one. */
  .z-kv-table tr.is-unavailable td:nth-child(2) { color: var(--z-muted); font-family: var(--z-font); font-style: italic; font-weight: 500; }
  .z-kv-table tr.is-filtered-out { display: none; }
  @media (max-width: 620px) {
    .z-kv-table, .z-kv-table tbody, .z-kv-table tr, .z-kv-table td { display: block; width: 100% !important; }
    .z-kv-table thead { display: none; }
    .z-kv-table tr { padding: var(--z-space-3); border-bottom: 1px solid var(--z-border-soft); }
    .z-kv-table td { min-height: 0; padding: var(--z-space-1) 0; border: 0; }
    .z-kv-table td::before { display: block; margin-bottom: 2px; color: var(--z-muted); content: attr(data-label); font-family: var(--z-font); font-size: 0.625rem; font-style: normal; font-weight: 750; letter-spacing: .04em; text-transform: uppercase; }
    .z-kv-table td:first-child { color: var(--z-primary-strong); }
    .z-kv-table td:first-child::before { display: none; }
  }

  /* A page of writing rather than of readings: a privacy notice, a runbook,
     a changelog. Wider measure, looser lines, headings that breathe. */
  .z-prose { width: min(940px, calc(100% - 32px)); margin: 0 auto; padding: clamp(28px, 6vw, 72px) 0; }
  .z-prose-head { display: flex; gap: var(--z-space-3); align-items: center; justify-content: space-between; margin-bottom: var(--z-space-4); }
  .z-prose-card { padding: clamp(24px, 5vw, 58px); }
  .z-prose-card h1 { margin: 0 0 var(--z-space-5); font-size: clamp(34px, 5vw, 58px); letter-spacing: -.035em; }
  .z-prose-card h2 { margin: var(--z-space-6) 0 var(--z-space-2); font-size: var(--z-font-xl); }
  .z-prose-card :is(p, li) { color: var(--z-muted); line-height: 1.7; }
  @media (max-width: 620px) { .z-prose { width: min(100% - 24px, 940px); } }

  /* Shown only when scripts do not run, so it has to carry itself: fixed to
     the bottom, in the danger colour, above everything. */
  .z-noscript { position: fixed; z-index: 200; right: 16px; bottom: 16px; left: 16px; padding: 14px; border: 1px solid var(--z-danger); border-radius: var(--z-radius-sm); color: var(--z-text); background: var(--z-danger-soft); box-shadow: var(--z-shadow-lg); text-align: center; }

  .z-page-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--z-space-6);
    margin-top: var(--z-space-6);
    padding: var(--z-space-6) 0;
    border-top: 1px solid var(--z-border);
    color: var(--z-muted);
    font-size: var(--z-font-xs);
  }
  @media (max-width: 620px) { .z-page-footer { display: block; } }

  /* A fixed application frame: bars that stay where they are and one region
     that scrolls. The scrolling region must be allowed to shrink to nothing —
     give it a minimum height and a short screen (a phone held sideways, or a
     phone with the keyboard open) pushes whatever sits below it past the
     bottom edge, where the frame's own overflow:hidden makes it unreachable. */
  /* The page fills the *largest* viewport so its background still covers the
     strip that appears when a mobile browser hides its toolbar; the frame
     inside stays at the smallest, so its controls are never pushed out of
     reach. Without this the shell ends where the toolbar used to be and leaves
     a bare band along the bottom edge. */
  .z-fixed-page, .z-fixed-page > body { height: 100%; height: 100lvh; overflow: hidden; }
  .z-app-shell {
    display: flex;
    flex-direction: column;
    width: min(var(--z-shell-max, 880px), 100%);
    height: 100vh;
    height: 100dvh;
    margin-inline: auto;
    overflow: hidden;
    padding: max(var(--z-space-3), env(safe-area-inset-top)) var(--z-space-4)
             max(var(--z-space-2), env(safe-area-inset-bottom));
  }
  .z-app-shell > .z-shell-body {
    flex: 1 1 0;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  .z-app-shell-bar {
    display: flex;
    align-items: center;
    gap: var(--z-space-3);
    min-height: 70px;
    padding: 10px 4px 14px;
    border-bottom: 1px solid var(--z-border);
  }
  @media (max-width: 620px) { .z-app-shell { padding-inline: 11px; } }

  .z-chat-thread {
    display: flex;
    flex-direction: column;
    gap: var(--z-space-2);
    padding: var(--z-space-4) var(--z-space-2);
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  .z-chat-thread.is-empty { align-items: center; justify-content: center; color: var(--z-muted); }
  .z-chat-bubble {
    align-self: flex-start;
    max-width: min(76%, 640px);
    padding: 10px 13px 7px;
    border: 1px solid var(--z-border-soft);
    border-radius: var(--z-radius-lg) var(--z-radius-lg) var(--z-radius-lg) var(--z-radius-xs);
    background: var(--z-surface-raised);
    color: var(--z-text);
    box-shadow: var(--z-elevation-raised);
    line-height: 1.42;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
  }
  .z-chat-bubble-outgoing {
    align-self: flex-end;
    border-color: color-mix(in srgb, var(--z-primary) 34%, var(--z-border-soft));
    border-radius: var(--z-radius-lg) var(--z-radius-lg) var(--z-radius-xs) var(--z-radius-lg);
    background: var(--z-primary-soft);
  }
  /* A picture in a bubble. It sets the bubble's width rather than being
     squeezed into it, and the aspect ratio comes from the width and height
     attributes so the thread does not jump while it loads. */
  .z-chat-photo { display: block; margin: -4px -5px 6px; }
  .z-chat-photo img { display: block; width: 100%; height: auto; max-height: 60vh; object-fit: cover; border-radius: var(--z-radius-md); background: var(--z-surface-2); }
  .z-chat-photo:focus-visible { outline: 2px solid var(--z-primary); outline-offset: 2px; border-radius: var(--z-radius-md); }
  .z-chat-bubble:has(.z-chat-photo) { width: min(320px, 78%); max-width: min(320px, 78%); }
  /* The picture opens a viewer, so it behaves like a button even though it is
     a link -- the href is there for a long press and for a page without JS. */
  .z-chat-photo { cursor: zoom-in; }

  .z-chat-bubble-time { display: flex; align-items: center; justify-content: flex-end; gap: 5px; margin-top: 4px; color: var(--z-muted); font-size: var(--z-font-xs); }
  /* One tick sent, two ticks read -- the idiom every messenger already uses,
     so it needs no key. The ticks overlap slightly, as they do elsewhere. */
  .z-chat-receipt { display: inline-flex; align-items: center; flex: none; }
  .z-chat-receipt .z-icon { width: 14px; height: 14px; }
  .z-chat-receipt .z-icon + .z-icon { margin-left: -7px; }
  .z-chat-receipt.is-read { color: var(--z-primary-strong); }

  .z-composer {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--z-space-2);
    align-items: end;
    padding: var(--z-space-3) 0 var(--z-space-1);
    border-top: 1px solid var(--z-border);
  }
  .z-composer-field {
    width: 100%;
    min-height: 44px;
    max-height: 160px;
    padding: 11px 14px;
    resize: none;
    border: 1px solid var(--z-border);
    border-radius: var(--z-radius-lg);
    outline: none;
    color: var(--z-text);
    background: var(--z-surface);
    font: inherit;
    transition: border-color var(--z-motion-normal) var(--z-ease-standard), box-shadow var(--z-motion-normal) var(--z-ease-standard);
  }
  .z-composer-field:focus-visible { border-color: var(--z-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--z-primary) 20%, transparent); }

  .z-dialog-backdrop.z-media-viewer { padding: 0; background: rgba(0, 0, 0, .94); }
  .z-media-viewer .z-dialog {
    width: 100vw;
    max-width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    display: grid;
    grid-template-rows: 1fr;
    border: 0;
    border-radius: 0;
    background: #000;
    box-shadow: none;
  }
  .z-media-viewer .z-dialog-header {
    position: absolute;
    z-index: 2;
    top: max(var(--z-space-3), env(safe-area-inset-top));
    right: max(var(--z-space-3), env(safe-area-inset-right));
    left: max(var(--z-space-3), env(safe-area-inset-left));
    min-height: 0;
    border: 0;
    border-radius: var(--z-radius-md);
    background: rgba(0, 0, 0, .6);
    backdrop-filter: blur(12px);
  }
  .z-media-viewer .z-dialog-title { color: #fff; }
  .z-media-viewer-subtitle { color: rgba(255, 255, 255, .64); font-size: var(--z-font-xs); }
  .z-media-stage { position: relative; display: grid; place-items: center; min-height: 0; overflow: hidden; background: #000; }
  .z-media-stage img, .z-media-stage video { display: block; width: 100%; height: 100%; object-fit: contain; }
  .z-media-loading { position: absolute; color: rgba(255, 255, 255, .7); font-size: var(--z-font-sm); }
}
