/* ==========================================================================
   File Vault — design system «Сейф»

   Deep forest green for action and navigation, brass only as a mark, warm paper,
   flat calm surfaces, the rounded square of the safe door as the recurring shape.

   Structure
     @layer reset       element defaults, [hidden]
     @layer tokens      :root (light), :root[data-theme="dark"], density by layout and pointer
     @layer base        document, focus ring, helpers
     @layer components  one block per component; each block is mobile first and carries its own
                        @media (min-width: 600px), desktop, (hover: hover) and motion rules
     @layer layout      page skeletons (css/app.css and the files of the pages outside the application:
                        css/share.css, css/upload-link.css, css/app-download.css)
     @layer pages       page-specific rules (the same files)

   Breakpoints (media queries cannot read custom properties, so they are literals):
     phone layout       base rules
     wide phone/tablet  @media (min-width: 600px)
     desktop layout     @media (min-width: 900px) and (min-height: 500px)
     compact density    desktop layout and (hover: hover) and (pointer: fine)
     short window       @media (max-height: 499px)

   This is a source file: it is edited in place. Nothing generates it and nothing compares it with a copy.
   Where a rule goes and what guards the stylesheets: docs/UI.md, section 13.
   ========================================================================== */

@layer reset, tokens, base, components, layout, pages;

/* ==========================================================================
   Tokens
   ========================================================================== */
@layer tokens {
  :root {
    color-scheme: light;

    /* ---- Type ---- */
    --font-sans: "Manrope", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
    --weight-regular: 500;
    --weight-medium: 600;
    --weight-bold: 700;
    --font-size-xs: 0.75rem;    /* 12 */
    --font-size-sm: 0.8125rem;  /* 13 */
    --font-size-md: 0.875rem;   /* 14 */
    --font-size-lg: 0.9375rem;  /* 15 */
    --font-size-xl: 1rem;       /* 16 */
    --font-size-2xl: 1.125rem;  /* 18 */
    --font-size-3xl: 1.25rem;   /* 20 */
    --font-size-4xl: 1.375rem;  /* 22 */
    --font-size-5xl: 1.75rem;   /* 28 */
    --leading-xs: 1rem;         /* 16 */
    --leading-sm: 1.25rem;      /* 20 */
    --leading-md: 1.5rem;       /* 24 */
    --leading-lg: 1.75rem;      /* 28 */
    --leading-xl: 2.25rem;      /* 36 */
    --tracking-tight: -0.01em;

    /* ---- Space: 4px grid ---- */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-8: 32px;
    --space-10: 40px;
    --space-12: 48px;
    --space-16: 64px;

    /* ---- Shape ---- */
    --radius-xs: 6px;
    --radius-sm: 8px;
    --radius-md: 10px;
    --radius-lg: 14px;
    --radius-xl: 20px;
    --radius-fab: 16px;
    --radius-full: 999px;
    --icon-stroke: 1.75px;

    /* ---- Density: touch (phone layout and coarse pointers) ---- */
    --control-height: 48px;
    --control-height-sm: 44px;
    --control-font: var(--font-size-xl);
    --button-font: var(--font-size-lg);
    --body-font: var(--font-size-lg);
    --icon-button-size: 44px;
    --hit-size: 44px;
    --row-height: 64px;
    --tile-size: 40px;
    --tile-radius: var(--radius-md);
    --tile-icon: 22;
    --menu-item-height: 52px;
    --menu-item-font: var(--font-size-xl);
    --nav-item-height: 48px;
    --tree-row-height: 44px;
    --chip-height: 24px;
    --gutter: 16px;

    /* ---- Shell ---- */
    --appbar-height: 56px;
    --appbar-pad-start: calc(var(--gutter) - 10px + var(--safe-left));   /* side paddings of the app bar: a 44px glyph button */
    --appbar-pad-end: calc(var(--gutter) - 4px + var(--safe-right));     /* stands with its glyph on the gutter line */
    --context-height: 48px;
    --bottomnav-height: 64px;
    --fab-size: 56px;
    --sidebar-width: 264px;
    --topbar-height: 64px;
    --table-head-height: 36px;
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);
    --page-column: 1152px;      /* content column of a service page (administration, metrics) */
    --page-pad-start: calc(var(--gutter) + var(--safe-left));   /* side paddings of such a page; on desktop they centre the column */
    --page-pad-end: calc(var(--gutter) + var(--safe-right));
    --bottom-bar-space: 0px;    /* height of a fixed bottom bar under the toast and the floating button; set by the screen that has one */
    --col-size: 152px;          /* size column of a list with columns (file rows, section rows), from 600px; 168px in the desktop layout */
    --col-date: 140px;          /* date column of such a list; 164px in the desktop layout */
    --col-actions: 140px;       /* action column of the desktop table under a mouse: three quick actions and «⋯» */
    --check-compact-size: 18px; /* checkbox of the desktop table and of its selection bar */
    --check-compact-radius: 5px;
    --check-compact-glyph: 12px;
    --check-compact-gap: 14px;  /* between that checkbox and the icon tile */
    --view-bar-height: 56px;    /* bar of the viewer. It carries filled buttons, so it keeps 56px in a short window too */

    /* ---- Stacking: bars < floating button < scrim < menus and sheets < dialogs < toast ---- */
    --z-sticky: 10;
    --z-bar: 20;
    --z-float: 30;
    --z-scrim: 40;
    --z-popover: 50;
    --z-modal: 60;
    --z-toast: 70;

    /* ---- Motion ---- */
    --duration-fast: 120ms;
    --duration-base: 200ms;
    --duration-slow: 300ms;
    --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
    --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);

    /* ---- Colour: page and surfaces ---- */
    --bg: #F7F4EE;
    --surface: #FFFFFF;
    --surface-raised: #FFFFFF;
    --surface-overlay: #FFFFFF;
    --surface-sunken: rgba(125, 95, 25, 0.10);
    --nav-bg: var(--bg);
    --nav-edge: rgba(111, 86, 24, 0.24);
    --scrim: rgba(27, 36, 32, 0.46);

    /* ---- Colour: text ---- */
    --text: #1B2420;
    --text-muted: #595C54;
    --text-subtle: #85837A;
    --text-disabled: #A6A398;

    /* ---- Colour: lines ---- */
    --border: rgba(111, 86, 24, 0.16);
    --border-strong: #8A867B;

    /* ---- Colour: state layers (translucent, valid on every surface) ---- */
    --state-hover: rgba(94, 74, 22, 0.08);
    --state-pressed: rgba(94, 74, 22, 0.14);

    /* ---- Colour: controls. A control fill is never darker than its container ---- */
    --control-bg: #FFFFFF;
    --control-disabled-bg: rgba(94, 74, 22, 0.08);
    --field-bg: #FFFFFF;
    --control-track: rgba(125, 95, 25, 0.12);
    --control-thumb: #FFFFFF;
    --chip-bg: rgba(125, 95, 25, 0.12);

    /* ---- Colour: accent (forest green) — action and place ---- */
    --accent: #1E5A47;
    --accent-hover: #17493A;
    --accent-pressed: #123B2F;
    --accent-contrast: #FFFFFF;
    --accent-soft: rgba(30, 90, 71, 0.13);
    --accent-soft-hover: rgba(30, 90, 71, 0.19);
    --focus-ring: #1E5A47;

    /* ---- Colour: brand fill — the same deep green in both themes (logo, avatar, active section, selection bar) ---- */
    --brand: #1E5A47;
    --brand-contrast: #FFFFFF;
    --brand-line: #A9CFC0;
    --brand-highlight: #EAC683;
    --brand-danger: #B3311F;
    --brand-danger-soft: rgba(179, 49, 31, 0.10);
    --brand-state-hover: rgba(255, 255, 255, 0.12);
    --nav-active-icon: #FFFFFF;

    /* ---- Colour: highlight (brass) — favourites and public links only ---- */
    --highlight: #A9782B;
    --highlight-strong: #7F5510;
    --highlight-soft: rgba(214, 160, 60, 0.24);

    /* ---- Colour: status ---- */
    --danger: #B3311F;
    --danger-solid: #B3311F;
    --danger-solid-hover: #9A2718;
    --danger-contrast: #FFFFFF;
    --danger-soft: rgba(179, 49, 31, 0.10);
    --success: #1E6B4E;
    --success-soft: rgba(30, 90, 71, 0.13);
    --warning: #8A4B00;
    --warning-soft: rgba(214, 130, 20, 0.18);
    --info: #2C5E97;
    --info-soft: rgba(44, 94, 151, 0.12);
    --toast-bg: #1B2420;
    --toast-text: #F3F5F2;
    --toast-error-bg: #9A2718;
    --toast-error-text: #FFFFFF;

    /* ---- Colour: media stage of the viewer — dark in both themes, like the brand surface ---- */
    --stage-bg: #0B110F;
    --stage-text: #E7ECE8;
    --stage-text-muted: #ADB8B2;
    --stage-control-bg: rgba(11, 17, 15, 0.68);
    --stage-control-hover: rgba(11, 17, 15, 0.88);
    --stage-control-line: rgba(231, 236, 232, 0.24);
    --stage-track: rgba(231, 236, 232, 0.28);
    --stage-shade: rgba(4, 8, 7, 0.72);
    --stage-focus: #7CCBA6;
    --stage-edge: none;
    --stage-nav-size: 44px;

    /* ---- Colour: entry icons. Folders are green; files share one paper tile and carry the type colour in the glyph ---- */
    --tile-folder-bg: rgba(30, 90, 71, 0.13);
    --tile-file-bg: rgba(111, 96, 60, 0.12);
    --type-folder: #1E5A47;
    --type-file: #595C54;
    --type-text: #595C54;
    --type-pdf: #B03A28;
    --type-doc: #2C5E97;
    --type-table: #58700F;
    --type-image: #99427A;
    --type-video: #5B4DA8;
    --type-audio: #17727F;
    --type-book: #8A5A1E;
    --type-code: #465865;
    --type-archive: #6F6048;

    /* ---- Elevation ---- */
    --shadow-outline: 0 0 0 1px var(--border);
    --shadow-sm: 0 1px 2px rgba(46, 40, 24, 0.06);
    --shadow-md: 0 2px 4px rgba(46, 40, 24, 0.06), 0 8px 20px rgba(46, 40, 24, 0.10);
    --shadow-lg: 0 4px 10px rgba(46, 40, 24, 0.06), 0 20px 48px rgba(46, 40, 24, 0.18);

    /* ---- Glyphs for controls that cannot hold an <svg> (same paths as the sprite) ----
       Masks are colour-free; the select chevron is a background image, so its stroke repeats --text-muted. */
    --glyph-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
    --glyph-dash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.2' stroke-linecap='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E");
    --glyph-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.33' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23595C54' stroke-width='2.33' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  }

  :root[data-theme="dark"] {
    color-scheme: dark;

    /* Surfaces get lighter with elevation: page < list < raised bars < overlays */
    --bg: #0E1513;
    --surface: #16201D;
    --surface-raised: #1C2925;
    --surface-overlay: #223029;
    --surface-sunken: rgba(2, 6, 5, 0.34);
    --nav-bg: var(--surface-raised);
    --nav-edge: rgba(170, 220, 200, 0.11);
    --scrim: rgba(4, 8, 7, 0.64);

    --text: #E7ECE8;
    --text-muted: #ADB8B2;
    --text-subtle: #7A8982;
    --text-disabled: #5C6964;

    --border: rgba(170, 220, 200, 0.11);
    --border-strong: #7B8C84;

    --state-hover: rgba(231, 236, 232, 0.08);
    --state-pressed: rgba(231, 236, 232, 0.13);

    --control-bg: rgba(170, 220, 200, 0.07);
    --control-disabled-bg: rgba(231, 236, 232, 0.07);
    --field-bg: rgba(170, 220, 200, 0.04);
    --control-track: rgba(2, 6, 5, 0.34);
    --control-thumb: rgba(170, 220, 200, 0.15);
    --chip-bg: rgba(170, 220, 200, 0.10);

    --accent: #7CCBA6;
    --accent-hover: #8FD6B4;
    --accent-pressed: #6AB893;
    --accent-contrast: #0C1512;
    --accent-soft: rgba(124, 203, 166, 0.16);
    --accent-soft-hover: rgba(124, 203, 166, 0.22);
    --focus-ring: #7CCBA6;

    --brand-contrast: #EAF4EF;
    --brand-line: #94BDAD;
    --nav-active-icon: #7CCBA6;

    --highlight: #E0B36A;
    --highlight-strong: #E6BF80;
    --highlight-soft: rgba(224, 179, 106, 0.16);

    --danger: #F0907F;
    --danger-solid: #EE8A78;
    --danger-solid-hover: #F4A090;
    --danger-contrast: #2A0F0A;
    --danger-soft: rgba(240, 144, 127, 0.14);
    --success: #7CCBA6;
    --success-soft: rgba(124, 203, 166, 0.16);
    --warning: #F0A35E;
    --warning-soft: rgba(240, 163, 94, 0.15);
    --info: #8DB7EC;
    --info-soft: rgba(141, 183, 236, 0.15);
    --toast-bg: #E7ECE8;
    --toast-text: #141C19;
    --toast-error-bg: #F0907F;
    --toast-error-text: #2A0F0A;

    /* On the dark page a hairline keeps the dark stage readable as a panel */
    --stage-edge: var(--shadow-outline);

    --tile-folder-bg: rgba(124, 203, 166, 0.16);
    --tile-file-bg: rgba(170, 220, 200, 0.08);
    --type-folder: #7CCBA6;
    --type-file: #AEB9B3;
    --type-text: #AEB9B3;
    --type-pdf: #F19C8C;
    --type-doc: #8DB7EC;
    --type-table: #BFD276;
    --type-image: #E39FC7;
    --type-video: #B4A9F2;
    --type-audio: #7ACBD6;
    --type-book: #E0B36A;
    --type-code: #A9BBC7;
    --type-archive: #C2B59F;

    /* In the dark theme height is carried by surface lightness first; shadows only separate overlays */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.30);
    --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.30), 0 8px 20px rgba(0, 0, 0, 0.36);
    --shadow-lg: 0 4px 10px rgba(0, 0, 0, 0.30), 0 20px 48px rgba(0, 0, 0, 0.50);

    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ADB8B2' stroke-width='2.33' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  }

  /* Wide phones and tablets: wider gutters */
  @media (min-width: 600px) {
    :root {
      --gutter: 24px;
    }
  }

  /* Short windows (landscape phones): compact bars and rows, targets stay 44px */
  @media (max-height: 499px) {
    :root {
      --appbar-height: 48px;
      --context-height: 44px;
      --bottomnav-height: 48px;
      --fab-size: 48px;
      --row-height: 56px;
      --menu-item-height: 48px;
    }
  }

  /* Desktop layout: a service page is a centred column; the viewer is a window with a bar as high as the top bar */
  @media (min-width: 900px) and (min-height: 500px) {
    :root {
      --page-pad-start: max(var(--space-6), calc((100% - var(--page-column)) / 2));
      --page-pad-end: max(var(--space-6), calc((100% - var(--page-column)) / 2));
      --stage-nav-size: 48px;
      --view-bar-height: var(--topbar-height);
      --col-size: 168px;
      --col-date: 164px;
    }
  }

  /* Compact density: the desktop layout driven by a mouse */
  @media (min-width: 900px) and (min-height: 500px) and (hover: hover) and (pointer: fine) {
    :root {
      --control-height: 40px;
      --control-height-sm: 32px;
      --control-font: var(--font-size-md);
      --button-font: var(--font-size-md);
      --body-font: var(--font-size-md);
      --icon-button-size: 36px;
      --hit-size: 32px;
      --row-height: 48px;
      --tile-size: 32px;
      --tile-radius: var(--radius-sm);
      --tile-icon: 20;
      --menu-item-height: 36px;
      --menu-item-font: var(--font-size-md);
      --nav-item-height: 40px;
      --tree-row-height: 32px;
    }
  }
}

/* ==========================================================================
   Reset
   ========================================================================== */
@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
  }

  body,
  h1,
  h2,
  h3,
  h4,
  p,
  ul,
  ol,
  dl,
  dd,
  figure,
  pre {
    margin: 0;
  }

  ul,
  ol {
    padding: 0;
    list-style: none;
  }

  h1,
  h2,
  h3,
  h4 {
    font-size: inherit;
    font-weight: inherit;
  }

  a {
    color: inherit;
    text-decoration: none;
  }

  button,
  input,
  select,
  textarea {
    margin: 0;
    font: inherit;
    letter-spacing: inherit;
    color: inherit;
  }

  button {
    padding: 0;
    border: 0;
    background: none;
    text-align: inherit;
    cursor: pointer;
  }

  a,
  button,
  input,
  select,
  textarea,
  summary,
  label,
  [role="button"],
  [tabindex] {
    -webkit-tap-highlight-color: transparent;
  }

  img,
  video {
    display: block;
    max-width: 100%;
  }

  table {
    border-collapse: collapse;
    border-spacing: 0;
  }

  fieldset {
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
  }

  legend {
    padding: 0;
  }

  summary {
    list-style: none;
    cursor: pointer;
  }

  summary::-webkit-details-marker {
    display: none;
  }

  /* The product toggles everything with the hidden attribute; nothing may override it */
  [hidden] {
    display: none !important;
  }
}

/* ==========================================================================
   Base
   ========================================================================== */
@layer base {
  html {
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-sans);
    font-weight: var(--weight-regular);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }

  body {
    min-height: 100vh;
    min-height: 100dvh;
    background: var(--bg);
    font-size: var(--body-font);
    line-height: var(--leading-sm);
  }

  /* A dialog or a sheet is open: the page behind does not scroll */
  body.modal-open {
    overflow: hidden;
  }

  ::placeholder {
    color: var(--text-muted);
    opacity: 1;
  }

  ::selection {
    background: var(--accent-soft-hover);
  }

  /* One focus ring for every interactive element; components only adjust offset or colour */
  :focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
  }

  :focus:not(:focus-visible) {
    outline: none;
  }

  code,
  kbd,
  samp,
  pre,
  .mono {
    font-family: var(--font-mono);
    font-variant-ligatures: none;
  }

  /* Numbers that must line up digit under digit */
  .tabular {
    font-variant-numeric: tabular-nums;
  }

  /* One line with an ellipsis; the parent needs min-width: 0 */
  .truncate {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

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

  /* At most two lines, then an ellipsis: a list of names of unknown length in a meta line */
  .clamp-2 {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }
}

/* ==========================================================================
   Components
   ========================================================================== */
@layer components {
  @keyframes spin {
    to {
      transform: rotate(360deg);
    }
  }

  /* ------------------------------------------------------------------------
     Icon
     <svg class="icon" aria-hidden="true"><use href="/icons.svg#i-folder"></use></svg>
     24px grid; the stroke stays --icon-stroke at every size.
     ------------------------------------------------------------------------ */
  .icon {
    --icon-size: 20;
    display: block;
    flex: none;
    width: calc(var(--icon-size) * 1px);
    height: calc(var(--icon-size) * 1px);
    fill: none;
    stroke: currentColor;
    stroke-width: calc(var(--icon-stroke) * 24 / var(--icon-size));
    stroke-linecap: round;
    stroke-linejoin: round;
    pointer-events: none;
  }

  .icon-16 {
    --icon-size: 16;
  }

  .icon-fill {
    fill: currentColor;
  }

  /* ------------------------------------------------------------------------
     Button
     .btn + .btn-primary | .btn-secondary | .btn-ghost | .btn-danger | .btn-ok | .btn-danger-solid
     shape: .btn-icon, .btn-sm, .btn-block; states: :disabled, [aria-disabled], .is-loading, .is-active
     ------------------------------------------------------------------------ */
  .btn {
    --btn-bg: transparent;
    --btn-fg: var(--text);
    --btn-fg-hover: var(--btn-fg);
    --btn-border: transparent;
    --btn-layer: transparent;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    flex: none;
    min-width: 0;
    height: var(--control-height);
    padding: 0 var(--space-4);
    border: 1px solid var(--btn-border);
    border-radius: var(--radius-md);
    background: linear-gradient(var(--btn-layer), var(--btn-layer)) var(--btn-bg);
    color: var(--btn-fg);
    font-size: var(--button-font);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
  }

  /* A glyph next to a label sits 2px closer to the edge and to the text than the 8px gap alone */
  .btn > .icon {
    --icon-size: 18;
    margin: 0 -2px;
  }

  /* .btn-collapse below 600px is a glyph: as quiet as an icon button. It stands before the variants and the colour
     modifiers on purpose — each of them overrides it */
  @media (max-width: 599px) {
    .btn-collapse {
      --btn-fg: var(--text-muted);
      --btn-fg-hover: var(--text);
    }
  }

  .btn-primary {
    --btn-bg: var(--accent);
    --btn-fg: var(--accent-contrast);
  }

  .btn-secondary {
    --btn-bg: var(--control-bg);
    --btn-border: var(--border-strong);
  }

  /* Text button: no box, so it needs less side padding */
  .btn-ghost {
    padding: 0 var(--space-3);
  }

  .btn-danger-solid {
    --btn-bg: var(--danger-solid);
    --btn-fg: var(--danger-contrast);
  }

  .btn-icon {
    --btn-fg: var(--text-muted);
    --btn-fg-hover: var(--text);
    width: var(--icon-button-size);
    height: var(--icon-button-size);
    padding: 0;
  }

  .btn-icon > .icon {
    --icon-size: 20;
    margin: 0;
  }

  /* Colour modifiers: combine with .btn-ghost, .btn-icon or .btn-secondary */
  .btn-danger {
    --btn-fg: var(--danger);
    --btn-fg-hover: var(--danger);
  }

  .btn-ok {
    --btn-fg: var(--success);
    --btn-fg-hover: var(--success);
  }

  /* The action of a row or of a line that must read as the action without being a filled button */
  .btn-accent {
    --btn-fg: var(--accent);
    --btn-fg-hover: var(--accent);
  }

  /* Toggled on (a pressed tool, a filter that is applied) */
  .btn.is-active {
    --btn-fg: var(--accent);
    --btn-fg-hover: var(--accent);
  }

  .btn-sm {
    height: var(--control-height-sm);
    padding: 0 var(--space-3);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
  }

  .btn-icon.btn-sm {
    width: var(--control-height-sm);
    padding: 0;
  }

  .btn-block {
    display: flex;
    width: 100%;
  }

  /* A labelled button that is a glyph below 600px. The label is a <span>; it stays in the tree for assistive
     technology (the button also carries aria-label when the bare word is not enough) */
  .btn-collapse {
    width: var(--icon-button-size);
    padding: 0;
  }

  .btn-collapse.btn-sm {
    width: var(--control-height-sm);
  }

  .btn-collapse > .icon {
    --icon-size: 20;
    margin: 0;
  }

  .btn-collapse > span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  @media (min-width: 600px) {
    .btn-collapse,
    .btn-collapse.btn-sm {
      width: auto;
      padding: 0 var(--space-4);
    }

    .btn-collapse.btn-ghost,
    .btn-collapse.btn-sm {
      padding: 0 var(--space-3);
    }

    .btn-collapse > .icon {
      --icon-size: 18;
      margin: 0 -2px;
    }

    .btn-collapse > span {
      position: static;
      width: auto;
      height: auto;
      overflow: visible;
      clip-path: none;
    }
  }

  /* .btn-collapse-wide: the label appears only in the desktop layout — for a bar that is short of room up to there
     («Открыть отдельно» in the bar of the viewer). After the 600px block on purpose: it takes the label back */
  @media (max-width: 899px), (max-height: 499px) {
    .btn-collapse-wide,
    .btn-collapse-wide.btn-sm {
      width: var(--icon-button-size);
      padding: 0;
    }

    .btn-collapse-wide.btn-sm {
      width: var(--control-height-sm);
    }

    .btn-collapse-wide > .icon {
      --icon-size: 20;
      margin: 0;
    }

    .btn-collapse-wide > span {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
  }

  @media (hover: hover) {
    .btn:hover {
      --btn-layer: var(--state-hover);
      color: var(--btn-fg-hover);
    }

    .btn-primary:hover {
      --btn-bg: var(--accent-hover);
      --btn-layer: transparent;
    }

    .btn-danger-solid:hover {
      --btn-bg: var(--danger-solid-hover);
      --btn-layer: transparent;
    }

    .btn-danger:hover {
      --btn-layer: var(--danger-soft);
    }

    .btn-ok:hover {
      --btn-layer: var(--success-soft);
    }

    .btn-accent:hover {
      --btn-layer: var(--accent-soft);
    }
  }

  .btn:active {
    --btn-layer: var(--state-pressed);
  }

  .btn-primary:active {
    --btn-bg: var(--accent-pressed);
    --btn-layer: transparent;
  }

  .btn-danger-solid:active {
    --btn-bg: var(--danger-solid-hover);
    --btn-layer: var(--state-pressed);
  }

  /* A button whose menu is open stays pressed */
  .btn[aria-expanded="true"] {
    --btn-layer: var(--state-pressed);
  }

  .btn:disabled,
  .btn[aria-disabled="true"] {
    --btn-fg: var(--text-disabled);
    --btn-fg-hover: var(--text-disabled);
    --btn-border: transparent;
    --btn-layer: transparent;
    cursor: default;
    pointer-events: none;
  }

  .btn-primary:disabled,
  .btn-primary[aria-disabled="true"],
  .btn-secondary:disabled,
  .btn-secondary[aria-disabled="true"],
  .btn-danger-solid:disabled,
  .btn-danger-solid[aria-disabled="true"] {
    --btn-bg: var(--control-disabled-bg);
  }

  /* Busy: the label keeps its width, a spinner takes its place. Add aria-busy="true" */
  .btn.is-loading {
    color: transparent;
    pointer-events: none;
  }

  .btn.is-loading::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 18px;
    height: 18px;
    margin: -9px 0 0 -9px;
    border: 2px solid var(--btn-fg);
    border-right-color: transparent;
    border-radius: 50%;
  }

  @media (prefers-reduced-motion: no-preference) {
    .btn {
      transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
    }

    .btn.is-loading::after {
      animation: spin 0.7s linear infinite;
    }
  }

  /* ------------------------------------------------------------------------
     Split button: a main action and a menu toggle in one control
     <div class="btn-split popover-anchor"><button class="btn btn-primary">…</button><button class="btn btn-primary" aria-haspopup="menu">chevron</button>
       [<div class="popover" hidden>…</div>]</div>
     ------------------------------------------------------------------------ */
  .btn-split {
    display: inline-flex;
    flex: none;
  }

  .btn-split > .btn:first-child {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
  }

  .btn-split > .btn + .btn {
    width: var(--icon-button-size);
    margin-left: 1px;
    padding: 0;
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
  }

  .btn-split > .btn + .btn > .icon {
    margin: 0;
  }

  /* ------------------------------------------------------------------------
     Field: caption + control (+ hint or error)
     <label class="field"><span>Caption</span><input></label>
     <div class="field"><label for="x">Caption</label><div class="input-group">…</div></div>
     ------------------------------------------------------------------------ */
  .field {
    display: grid;
    align-content: start;
    gap: 6px;
    min-width: 0;
  }

  .field > span:first-child,
  .field > label:first-child {
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
  }

  .field-hint {
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    color: var(--text-muted);
  }

  .field-error {
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    color: var(--danger);
  }

  /* Sub-heading between groups of fields inside a dialog or a form */
  .field-label {
    font-size: var(--font-size-lg);
    line-height: var(--leading-sm);
    font-weight: var(--weight-bold);
  }

  /* ------------------------------------------------------------------------
     Field layouts
     .field-row      fields and their button on one line from 600px: 1fr + button; .has-aside adds a 180px field
                     and wraps the button to its own line while the first field would get less than 280px
     .field-pair     two equal fields side by side from 600px
     .action-row     a sentence about the consequence and the button it belongs to; from 600px the button stands
                     beside the sentence, or under it while the sentence would get less than 220px
     .form-actions   buttons under a form inside a section: the footer rule (primary on top / on the right)
     ------------------------------------------------------------------------ */
  .field-row,
  .field-pair,
  .action-row,
  .created-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    min-width: 0;
  }

  /* Stacked fields keep the 16px rhythm of a form; 12px is the gap between the columns of the row */
  .field-row {
    align-items: end;
    gap: var(--space-4) var(--space-3);
  }

  .field-pair {
    align-items: start;
    gap: var(--space-4);
  }

  .action-row {
    align-items: center;
    gap: var(--space-3);
  }

  .action-row-text {
    min-width: 0;
  }

  /* A sentence with a disclosure under it: the button stays beside the sentence and does not move when
     «Подробнее» opens */
  .action-row-text + .btn {
    align-self: start;
  }

  .form-actions {
    display: flex;
    flex-direction: column-reverse;
    gap: var(--space-2);
  }

  @media (min-width: 600px) {
    .field-row {
      grid-template-columns: minmax(0, 1fr) auto;
    }

    .field-row.has-aside {
      display: flex;
      flex-wrap: wrap;
      align-items: flex-end;
    }

    .field-row.has-aside > .field {
      flex: 1 1 280px;
      min-width: 0;
    }

    .field-row.has-aside > .field + .field {
      flex: 0 0 180px;
    }

    .field-row.has-aside > .btn {
      flex: none;
      margin-left: auto;
    }

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

    .action-row {
      display: flex;
      flex-wrap: wrap;
      column-gap: var(--space-6);
    }

    .action-row > :first-child {
      flex: 1 1 220px;
      min-width: 0;
    }

    .action-row > .btn {
      flex: none;
      margin-left: auto;
    }

    .form-actions {
      flex-direction: row;
      justify-content: flex-end;
    }
  }

  /* ------------------------------------------------------------------------
     Text input, select, textarea
     Classes .input / .select / .textarea, or a bare control directly inside .field.
     Editable: field fill and a strong border. Read-only: sunken well and a hairline.
     ------------------------------------------------------------------------ */
  :where(.input, .select, .textarea, .field > input, .field > select, .field > textarea) {
    display: block;
    width: 100%;
    min-width: 0;
    height: var(--control-height);
    padding: 0 var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background-color: var(--field-bg);
    color: var(--text);
    font-size: var(--control-font);
    line-height: var(--leading-sm);
    font-weight: var(--weight-regular);
    -webkit-appearance: none;
    appearance: none;
  }

  :where(.textarea, .field > textarea) {
    height: auto;
    min-height: 96px;
    padding: var(--space-3);
    line-height: var(--leading-md);
    resize: vertical;
  }

  /* The chevron sits on the axis of a trailing .btn-icon of an .input-group */
  :where(.select, .field > select) {
    padding-right: var(--space-10);
    background-image: var(--select-chevron);
    background-repeat: no-repeat;
    background-position: right calc(var(--icon-button-size) / 2 - 8px) center;
    background-size: 18px 18px;
    text-overflow: ellipsis;
    cursor: pointer;
  }

  @media (hover: hover) {
    :where(.input, .select, .textarea, .field > input, .field > select, .field > textarea):hover {
      border-color: var(--text-muted);
    }
  }

  :where(.input, .select, .textarea, .field > input, .field > select, .field > textarea):focus-visible {
    border-color: var(--focus-ring);
    outline: 2px solid var(--focus-ring);
    outline-offset: -1px;
  }

  :where(.input, .textarea, .field > input, .field > textarea)[readonly] {
    border-color: var(--border);
    background-color: var(--surface-sunken);
    text-overflow: ellipsis;
  }

  :where(.input, .select, .textarea, .field > input, .field > select, .field > textarea)[aria-invalid="true"] {
    border-color: var(--danger);
    outline-color: var(--danger);
  }

  :where(.input, .select, .textarea, .field > input, .field > select, .field > textarea):disabled {
    border-color: var(--border);
    background-color: var(--control-disabled-bg);
    color: var(--text-disabled);
    -webkit-text-fill-color: var(--text-disabled);
    opacity: 1;
    cursor: default;
  }

  /* ------------------------------------------------------------------------
     Input group: one field box around an input and its buttons or a leading icon
     <div class="input-group [is-readonly]"><input><button class="btn btn-icon">…</button></div>
     ------------------------------------------------------------------------ */
  .input-group {
    display: flex;
    align-items: center;
    min-width: 0;
    height: var(--control-height);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--field-bg);
  }

  .input-group > input {
    flex: 1;
    width: auto;
    min-width: 0;
    height: 100%;
    padding: 0 var(--space-3);
    border: 0;
    border-radius: calc(var(--radius-md) - 1px);
    background: none;
    font-size: var(--control-font);
    text-overflow: ellipsis;
    -webkit-appearance: none;
    appearance: none;
  }

  .input-group > input:focus-visible {
    outline-offset: -2px;
  }

  /* The group is one field: while the focus stands in its input the ring goes around the whole box, the button
     included — as the ring of a plain field does. The script keeps .is-focused on the group for that time
     (CSS cannot name a parent by the state of its child without :has()); without the script the input keeps
     the inset ring above. :focus-within makes a mark left on a group that was hidden with the focus in it
     (no focusout comes then) harmless. */
  .input-group.is-focused:focus-within {
    border-color: var(--focus-ring);
    outline: 2px solid var(--focus-ring);
    outline-offset: -1px;
  }

  .input-group.is-focused:focus-within > input:focus-visible {
    outline: none;
  }

  /* A search field in a group gets its clear button from the group (a trailing .btn-icon), not from the browser */
  .input-group > input[type="search"]::-webkit-search-cancel-button {
    -webkit-appearance: none;
  }

  .input-group > .icon {
    margin-left: var(--space-3);
    color: var(--text-muted);
  }

  .input-group > .icon + input {
    padding-left: var(--space-2);
  }

  .input-group > .btn-icon {
    --btn-fg: var(--accent);
    --btn-fg-hover: var(--accent);
    margin-right: 1px;
  }

  /* Inside a field the ring is inset, as the ring of the input is: outside it would cross the border of the box */
  .input-group > .btn-icon:focus-visible {
    outline-offset: -2px;
  }

  .input-group.is-readonly {
    border-color: var(--border);
    background: var(--surface-sunken);
  }

  /* ------------------------------------------------------------------------
     Created value: a token or a one-time link that is shown once
     div.created-block > div.field > label + div.created-row (input[readonly] + button) + span.field-hint
     ------------------------------------------------------------------------ */
  .created-block {
    padding: var(--space-3);
    border-radius: var(--radius-md);
    background: var(--accent-soft);
  }

  .created-row {
    gap: var(--space-2);
  }

  .created-block .field-hint {
    color: var(--text);
  }

  /* On the tint --border-strong falls under 3:1, so the outline of the copy button takes the muted text colour */
  .created-block .btn-secondary {
    --btn-border: var(--text-muted);
  }

  @media (min-width: 600px) {
    .created-block {
      padding: var(--space-4);
    }

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

  /* ------------------------------------------------------------------------
     Checkbox
     <label class="check"><input type="checkbox"><span class="check-box"></span><span>Text</span></label>
     The native input is an invisible --hit-size target over the mark.
     ------------------------------------------------------------------------ */
  .check {
    position: relative;
    display: inline-flex;
    align-items: flex-start;
    gap: 10px;
    min-width: 0;
    padding: calc((var(--hit-size) - var(--leading-sm)) / 2) 0;
    line-height: var(--leading-sm);
    cursor: pointer;
  }

  /* The label wraps; a word without spaces (a group name of 100 characters) breaks instead of widening its dialog */
  .check > span:last-child {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  .check > input {
    position: absolute;
    top: 0;
    left: calc((20px - var(--hit-size)) / 2);
    z-index: 1;
    width: var(--hit-size);
    height: var(--hit-size);
    margin: 0;
    opacity: 0;
    cursor: pointer;
  }

  .check-box {
    position: relative;
    flex: none;
    width: 20px;
    height: 20px;
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-xs);
    background: var(--field-bg);
    color: var(--accent-contrast);
  }

  /* The glyph is centred on the box itself, whatever the border */
  .check-box::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 14px;
    height: 14px;
    margin: -7px 0 0 -7px;
    background: currentColor;
    -webkit-mask: var(--glyph-check) center / contain no-repeat;
    mask: var(--glyph-check) center / contain no-repeat;
    opacity: 0;
  }

  .check > input:checked + .check-box,
  .check > input:indeterminate + .check-box {
    border-color: var(--accent);
    background: var(--accent);
  }

  .check > input:checked + .check-box::after,
  .check > input:indeterminate + .check-box::after {
    opacity: 1;
  }

  .check > input:indeterminate + .check-box::after {
    -webkit-mask-image: var(--glyph-dash);
    mask-image: var(--glyph-dash);
  }

  .check > input:focus-visible + .check-box {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
  }

  .check > input:disabled {
    cursor: default;
  }

  .check > input:disabled ~ * {
    color: var(--text-disabled);
    cursor: default;
  }

  .check > input:disabled + .check-box {
    border-color: var(--text-disabled);
    background: var(--control-disabled-bg);
  }

  .check > input:disabled:checked + .check-box,
  .check > input:disabled:indeterminate + .check-box {
    border-color: transparent;
    background: var(--text-disabled);
    color: var(--surface);
  }

  /* Options that are saved together: div.check-list[role=group] > label.check … */
  .check-list {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }

  @media (hover: hover) {
    .check > input:enabled:hover + .check-box {
      border-color: var(--accent);
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .check-box {
      transition: background-color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
    }
  }
  /* ------------------------------------------------------------------------
     Segmented control: two to four exclusive options
     <div class="segmented" role="radiogroup"><label class="segmented-item"><input type="radio" name="n" checked><span>…</span></label>…</div>
     or buttons: <button class="segmented-item active" role="tab" aria-selected="true"><span>…</span></button>
     The inner <span> is the visible segment; the item is the target.
     ------------------------------------------------------------------------ */
  .segmented {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    padding: 0 3px;
    border-radius: var(--radius-md);
    background: var(--control-track);
    box-shadow: inset 0 0 0 1px var(--border);
  }

  .segmented-item {
    position: relative;
    display: grid;
    min-width: 0;
    height: var(--control-height);
    padding: 3px 0;
    border-radius: var(--radius-sm);
    cursor: pointer;
  }

  .segmented-item > input {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
  }

  .segmented-item > span {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-width: 0;
    padding: 0 var(--space-3);
    border-radius: 7px;
    color: var(--text-muted);
    font-size: var(--font-size-md);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    white-space: nowrap;
  }

  .segmented-item > span > .icon {
    --icon-size: 18;
  }

  /* The chosen segment is told from the others by more than its fill (thumb on track is under 1.5:1 in both
     themes): a ring of the strong border colour, 3:1 and more against the thumb and the track */
  .segmented-item.active > span,
  .segmented-item[aria-selected="true"] > span,
  .segmented-item[aria-pressed="true"] > span,
  .segmented-item > input:checked + span {
    background: var(--control-thumb);
    box-shadow: inset 0 0 0 1.5px var(--border-strong), var(--shadow-sm);
    color: var(--text);
  }

  .segmented-item:focus-visible {
    outline: none;
  }

  .segmented-item:focus-visible > span,
  .segmented-item > input:focus-visible + span {
    outline: 2px solid var(--focus-ring);
    outline-offset: 1px;
  }

  @media (hover: hover) {
    .segmented-item:hover > span {
      color: var(--text);
    }
  }

  /* ------------------------------------------------------------------------
     Folder picker: the destination of «Переместить»
     div.folder-picker[role=radiogroup] > label.folder-option[.is-depth-1…4]
       > input[type=radio] + span.folder-option-icon + span.folder-option-name
         + span.folder-option-check | span.folder-option-note
     The radio is the whole row (hover, focus ring, selected tint); the rest is painted over it.
     Depth: a class, or the custom property --folder-depth set from script.
     ------------------------------------------------------------------------ */
  .folder-picker {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    padding: var(--space-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--field-bg);
  }

  .folder-option {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    height: var(--tree-row-height);
    padding: 0 var(--space-3) 0 calc(var(--space-3) + var(--folder-depth, 0) * var(--space-5));
    font-size: var(--body-font);
    line-height: var(--leading-sm);
  }

  .folder-option.is-depth-1 {
    --folder-depth: 1;
  }

  .folder-option.is-depth-2 {
    --folder-depth: 2;
  }

  .folder-option.is-depth-3 {
    --folder-depth: 3;
  }

  .folder-option.is-depth-4 {
    --folder-depth: 4;
  }

  .folder-option > input {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    border-radius: var(--radius-sm);
    background: none;
    -webkit-appearance: none;
    appearance: none;
    cursor: pointer;
  }

  .folder-option > input:checked {
    background: var(--accent-soft);
  }

  .folder-option > input:disabled {
    cursor: default;
  }

  .folder-option > input:focus-visible {
    outline-offset: -2px;
  }

  .folder-option > span {
    position: relative;
    pointer-events: none;
  }

  .folder-option-icon {
    display: grid;
    flex: none;
    color: var(--text-muted);
  }

  .folder-option-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .folder-option-note {
    flex: none;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
  }

  .folder-option-check {
    flex: none;
    width: 18px;
    height: 18px;
    background: var(--accent);
    -webkit-mask: var(--glyph-check) center / contain no-repeat;
    mask: var(--glyph-check) center / contain no-repeat;
    visibility: hidden;
  }

  .folder-option > input:checked ~ .folder-option-check {
    visibility: visible;
  }

  .folder-option > input:checked ~ .folder-option-name {
    font-weight: var(--weight-bold);
  }

  .folder-option > input:checked ~ .folder-option-icon {
    color: var(--accent);
  }

  .folder-option > input:disabled ~ .folder-option-name {
    color: var(--text-muted);
  }

  @media (min-width: 900px) and (min-height: 500px) and (hover: hover) and (pointer: fine) {
    .folder-option-icon > .icon {
      --icon-size: 18;
    }
  }

  @media (hover: hover) {
    .folder-option > input:enabled:hover {
      background: var(--state-hover);
    }

    .folder-option > input:checked:hover {
      background: var(--accent-soft-hover);
    }
  }

  /* ------------------------------------------------------------------------
     Tabs: sections of one screen, underline style; the row scrolls sideways when it does not fit
     <div class="tabs" role="tablist"><button class="tab active" role="tab" aria-selected="true">
       <span class="tab-label" data-label="Пользователи">Пользователи</span> <span class="count">5</span></button>…</div>
     Product aliases: .admin-tabs / .admin-tab
     --tab-pad is the side padding of a tab and the inset of its underline (a page may tighten a crowded row).
     ------------------------------------------------------------------------ */
  .tabs,
  .admin-tabs {
    --tab-pad: var(--space-3);
    display: flex;
    gap: var(--space-1);
    min-width: 0;
    overflow-x: auto;
    box-shadow: inset 0 -1px 0 var(--border);
    scrollbar-width: none;
  }

  .tabs::-webkit-scrollbar,
  .admin-tabs::-webkit-scrollbar {
    display: none;
  }

  .tab,
  .admin-tab {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex: none;
    height: var(--control-height);
    padding: 0 var(--tab-pad);
    color: var(--text-muted);
    font-size: var(--button-font);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    white-space: nowrap;
  }

  /* The label keeps the width of its bold form (an invisible copy from data-label), so the active tab is bold
     and switching tabs moves nothing in the row */
  .tab-label {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
  }

  .tab-label::after {
    content: attr(data-label);
    content: attr(data-label) / "";
    height: 0;
    overflow: hidden;
    font-weight: var(--weight-bold);
    visibility: hidden;
    user-select: none;
    -webkit-user-select: none;
    pointer-events: none;
  }

  .tab::after,
  .admin-tab::after {
    content: "";
    position: absolute;
    right: var(--tab-pad);
    bottom: 0;
    left: var(--tab-pad);
    height: 2px;
    border-radius: 2px 2px 0 0;
    background: transparent;
  }

  .tab.active,
  .tab[aria-selected="true"],
  .admin-tab.active,
  .admin-tab[aria-selected="true"] {
    color: var(--text);
    font-weight: var(--weight-bold);
  }

  .tab.active::after,
  .tab[aria-selected="true"]::after,
  .admin-tab.active::after,
  .admin-tab[aria-selected="true"]::after {
    background: var(--accent);
  }

  .tab:focus-visible,
  .admin-tab:focus-visible {
    outline-offset: -2px;
    border-radius: var(--radius-sm);
  }

  /* Something behind the tab needs attention: <span class="tab-alert" role="img" aria-label="…"></span> */
  .tab-alert {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--danger);
  }

  @media (hover: hover) {
    .tab:hover,
    .admin-tab:hover {
      color: var(--text);
    }
  }

  /* ------------------------------------------------------------------------
     Chips and badges
     .tag-chips > .tag-chip   tags of an entry; the row clips them by whole chips
     .fav-badge, .share-badge, .upload-badge
                              brass marks next to a name: favourite (filled star), public link, upload link (a folder)
     .badge                   role or status: .badge-accent | -ok | -warn | -danger | -info | -brass, .badge-dot
     .count                   a number next to a title or a tab
     Product aliases: .user-badge = .badge; .peer-self, .act-out = accent; .peer-online = ok; .peer-offline = danger;
     .act-in = info; .act-local needs no rule (neutral)
     ------------------------------------------------------------------------ */
  .tag-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--space-1);
    min-width: 0;
    height: var(--chip-height);
    overflow: hidden;
  }

  /* A zero-width first item lets even the first chip wrap away instead of being cut */
  .tag-chips::before {
    content: "";
    width: 0;
    height: var(--chip-height);
    margin-right: calc(var(--space-1) * -1);
  }

  /* A free-standing set of chips (dialogs, properties) wraps onto more lines */
  .tag-chips.is-wrapping {
    gap: var(--space-1);
    height: auto;
    overflow: visible;
  }

  .tag-chips.is-wrapping::before {
    content: none;
  }

  /* The line is as high as the chip, so a name longer than its container is cut with an ellipsis inside the chip */
  .tag-chip {
    display: inline-block;
    flex: none;
    max-width: 100%;
    height: var(--chip-height);
    padding: 0 var(--space-2);
    overflow: hidden;
    border-radius: var(--radius-xs);
    background: var(--chip-bg);
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    line-height: var(--chip-height);
    font-weight: var(--weight-medium);
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
  }

  .fav-badge,
  .share-badge,
  .upload-badge {
    display: inline-grid;
    place-items: center;
    flex: none;
    color: var(--highlight);
  }

  .fav-badge > .icon,
  .share-badge > .icon,
  .upload-badge > .icon {
    --icon-size: 16;
  }

  .fav-badge > .icon {
    fill: currentColor;
  }

  .badge,
  .user-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex: none;
    max-width: 100%;
    height: 22px;
    padding: 0 var(--space-2);
    border-radius: var(--radius-xs);
    background: var(--chip-bg);
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    line-height: var(--leading-xs);
    font-weight: var(--weight-medium);
    white-space: nowrap;
  }

  .badge > .icon {
    --icon-size: 14;
    margin-left: -2px;
  }

  .badge-dot::before {
    content: "";
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
  }

  .badge-accent,
  .peer-self,
  .act-out {
    background: var(--accent-soft);
    color: var(--accent);
  }

  .badge-ok,
  .peer-online {
    background: var(--success-soft);
    color: var(--success);
  }

  .badge-warn {
    background: var(--warning-soft);
    color: var(--warning);
  }

  .badge-danger,
  .peer-offline {
    background: var(--danger-soft);
    color: var(--danger);
  }

  .badge-info,
  .act-in {
    background: var(--info-soft);
    color: var(--info);
  }

  .badge-brass {
    background: var(--highlight-soft);
    color: var(--highlight-strong);
  }

  .count {
    display: inline-grid;
    place-items: center;
    flex: none;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: var(--radius-xs);
    background: var(--chip-bg);
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    line-height: var(--leading-xs);
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
  }

  /* ------------------------------------------------------------------------
     Avatar: the only circle in the system
     <span class="avatar [avatar-sm]">A</span>
     ------------------------------------------------------------------------ */
  .avatar {
    display: grid;
    place-items: center;
    flex: none;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--brand);
    color: var(--brand-highlight);
    font-size: var(--font-size-lg);
    line-height: 1;
    font-weight: var(--weight-bold);
    text-transform: uppercase;
  }

  .avatar-sm {
    width: 32px;
    height: 32px;
    font-size: var(--font-size-md);
  }

  /* An account that cannot sign in (disabled, waiting to be enabled) loses the brand colours */
  .avatar-muted {
    background: var(--tile-file-bg);
    color: var(--text-muted);
  }

  /* ------------------------------------------------------------------------
     Brand: the mark and the product name
     <div class="brand"><img class="brand-mark" src="/logo.svg" alt="" width="32" height="32"><span class="brand-name">File Vault</span></div>
     ------------------------------------------------------------------------ */
  .brand {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
  }

  .brand-mark {
    flex: none;
    width: 32px;
    height: 32px;
  }

  .brand-name {
    min-width: 0;
    overflow: hidden;
    font-size: var(--font-size-2xl);
    line-height: var(--leading-md);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* ------------------------------------------------------------------------
     Entry icon: rounded-square tile with a type glyph
     <span class="entry-icon folder-icon">…</span>                      folder
     <span class="entry-icon file-icon">…</span>                        file without a known type
     <span class="entry-icon type-icon type-pdf">…</span>               pdf | docx | xls | table | txt | image | video | audio | epub | archive | code
     Size: --tile-size (40 touch, 32 compact); .entry-icon-lg is 56px.
     ------------------------------------------------------------------------ */
  .entry-icon {
    display: grid;
    place-items: center;
    flex: none;
    width: var(--tile-size);
    height: var(--tile-size);
    border-radius: var(--tile-radius);
    background: var(--tile-file-bg);
    color: var(--type-file);
  }

  .entry-icon > .icon {
    --icon-size: var(--tile-icon);
  }

  .entry-icon.folder-icon {
    background: var(--tile-folder-bg);
    color: var(--type-folder);
  }

  .entry-icon.type-txt {
    color: var(--type-text);
  }

  .entry-icon.type-pdf {
    color: var(--type-pdf);
  }

  .entry-icon.type-docx {
    color: var(--type-doc);
  }

  .entry-icon.type-xls,
  .entry-icon.type-table {
    color: var(--type-table);
  }

  .entry-icon.type-image {
    color: var(--type-image);
  }

  .entry-icon.type-video {
    color: var(--type-video);
  }

  .entry-icon.type-audio {
    color: var(--type-audio);
  }

  .entry-icon.type-epub {
    color: var(--type-book);
  }

  .entry-icon.type-code {
    color: var(--type-code);
  }

  .entry-icon.type-archive {
    color: var(--type-archive);
  }

  .entry-icon-lg {
    width: 56px;
    height: 56px;
    border-radius: var(--radius-lg);
  }

  .entry-icon-lg > .icon {
    --icon-size: 28;
  }

  /* ------------------------------------------------------------------------
     File list and row
     ul.file-list > li.file-item.file|folder [.is-fav][.is-shared][.is-upload-link][.is-syncing][.is-selected]
       button.row-select > span.row-check + span.entry-icon     selection toggle
       div.entry-main > div.file-info                           open target: name row, meta, tags
       span.file-size, span.file-date, span.file-date-short     columns from 600px (the short date: see below)
       div.row-actions > div.file-actions + button.row-menu-btn
     Phone: two lines. From 600px: size and date columns. Desktop: a table under .list-head.
     Selection mode: .is-selecting on an ancestor (#app-shell).
     ------------------------------------------------------------------------ */
  .file-item {
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    min-height: var(--row-height);
    padding: 0 calc(var(--gutter) - 12px) 0 calc(var(--gutter) - 2px);
  }

  .file-item + .file-item::before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    left: calc(var(--gutter) + var(--tile-size) + var(--space-3));
    border-top: 1px solid var(--border);
    pointer-events: none;
  }

  .file-item.is-selected {
    background: var(--accent-soft);
  }

  /* On the fill of a selected row — and of one under the pointer — the muted text of a tag falls to 4.33:1 */
  .file-item.is-selected .tag-chip {
    color: var(--text);
  }

  /* Drag and drop: the row being dragged, a folder row that will receive the drop */
  .file-item.dragging {
    opacity: 0.5;
  }

  .file-item.drop-target {
    background: var(--accent-soft);
    box-shadow: inset 0 0 0 1.5px var(--accent);
  }

  /* Leading control: the icon tile is the selection toggle (a 44px target around a 40px tile) */
  .row-select {
    position: relative;
    display: grid;
    place-items: center;
    width: calc(var(--tile-size) + 4px);
    height: calc(var(--tile-size) + 4px);
    border-radius: calc(var(--tile-radius) + 2px);
  }

  .row-select:focus-visible {
    outline-offset: 0;
  }

  /* Selection mark. Phone layout: it replaces the tile while selecting; desktop: an 18px box before the tile */
  .row-check {
    position: relative;
    display: none;
    place-items: center;
    width: var(--tile-size);
    height: var(--tile-size);
    border-radius: var(--tile-radius);
    background: var(--tile-file-bg);
    color: var(--accent-contrast);
  }

  .row-check::before {
    content: "";
    width: 20px;
    height: 20px;
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-xs);
    background: var(--field-bg);
  }

  .row-check::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 18px;
    height: 18px;
    margin: -9px 0 0 -9px;
    background: currentColor;
    -webkit-mask: var(--glyph-check) center / contain no-repeat;
    mask: var(--glyph-check) center / contain no-repeat;
    opacity: 0;
  }

  .is-selecting .row-select > .entry-icon {
    display: none;
  }

  .is-selecting .row-check {
    display: grid;
  }

  .file-item.is-selected .row-check {
    background: var(--accent);
  }

  .file-item.is-selected .row-check::before {
    visibility: hidden;
  }

  .file-item.is-selected .row-check::after {
    opacity: 1;
  }

  /* Open target */
  .entry-main {
    display: flex;
    align-items: center;
    align-self: stretch;
    min-width: 0;
    padding: 0 var(--space-2) 0 10px;
    cursor: pointer;
  }

  .entry-main:focus-visible {
    border-radius: var(--radius-sm);
    outline-offset: -2px;
  }

  .file-info {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    flex: 1;
    min-width: 0;
  }

  .file-name-row {
    display: flex;
    grid-column: 1 / -1;
    align-items: center;
    gap: 6px;
    min-width: 0;
  }

  .file-name {
    min-width: 0;
    overflow: hidden;
    font-size: var(--body-font);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .file-meta {
    min-width: 0;
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .file-item .tag-chips {
    --chip-height: 20px;
    margin-left: var(--space-2);
  }

  /* span.file-date-short is the date without the time: a table too narrow for the full date shows it instead
     (the storage screen does, from 900 to 1023px) */
  .file-size,
  .file-date,
  .file-date-short {
    display: none;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  .file-size {
    padding-right: var(--space-5);
    text-align: right;
  }

  /* A folder shows its statistics there: running text, proportional figures */
  .file-item.folder > .file-size {
    font-variant-numeric: normal;
  }

  /* Row actions. Closed: only «⋯» (plus three quick actions on a wide desktop under the pointer).
     Open (.is-open): every action with its label — see Menu. */
  .row-actions {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: flex-end;
  }

  .file-actions {
    display: none;
  }

  .row-actions .btn-icon,
  .side-entry-actions .btn-icon,
  .item-actions .btn-icon {
    width: var(--control-height-sm);
    height: var(--control-height-sm);
    border-radius: var(--radius-sm);
  }

  .row-menu-btn {
    --btn-fg: var(--text-subtle);
  }

  .row-menu-btn[aria-expanded="true"] {
    --btn-fg: var(--text);
  }

  /* Favourite, public link and upload link are brass when set */
  .btn.fav.is-active,
  .file-actions > [data-share].is-active,
  .file-actions > [data-upload-link].is-active {
    --btn-fg: var(--highlight);
    --btn-fg-hover: var(--highlight);
  }

  .btn.fav.is-active > .icon {
    fill: currentColor;
  }

  .is-selecting .row-menu-btn {
    visibility: hidden;
  }

  /* Small spinner next to a name that is still syncing */
  .sync-spinner {
    flex: none;
    width: 14px;
    height: 14px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
  }

  /* Column titles of a table: sticky at the top of its scroll container. Not shown until a layout asks for it
     (the file table and the section lists on desktop; other tables in their own stylesheet) */
  .list-head {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    display: none;
    align-items: center;
    height: var(--table-head-height);
    border-bottom: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    line-height: var(--leading-xs);
    font-weight: var(--weight-medium);
  }

  @media (min-width: 600px) {
    .file-item {
      grid-template-columns: auto minmax(0, 1fr) var(--col-size) var(--col-date) auto;
    }

    .file-size,
    .file-date {
      display: block;
    }

    .file-meta {
      display: none;
    }

    /* One line: the tags follow the name and are clipped by whole chips */
    .file-info {
      display: flex;
      gap: var(--space-2);
    }

    .file-name-row {
      flex: 0 1 auto;
    }

    .file-item .tag-chips {
      --chip-height: 24px;
      flex: 1 1 0;
      margin-left: 0;
    }
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .list-head,
    .file-item {
      --col-lead: calc(var(--check-compact-size) + var(--check-compact-gap) + var(--space-1) + var(--tile-size));
      grid-template-columns: var(--col-lead) minmax(0, 1fr) var(--col-size) var(--col-date) var(--control-height-sm);
      padding: 0 var(--space-2) 0 var(--space-3);
    }

    .list-head {
      display: grid;
    }

    .list-head-name {
      grid-column: 1 / 3;
      padding-left: var(--space-8);
    }

    .list-head-size {
      padding-right: var(--space-6);
      text-align: right;
    }

    /* Sortable column title */
    .list-sort {
      display: inline-flex;
      align-items: center;
      gap: var(--space-1);
      height: var(--hit-size);
      margin: 0 calc(var(--space-2) * -1);
      padding: 0 var(--space-2);
      border-radius: var(--radius-xs);
      color: inherit;
      font-weight: inherit;
    }

    .list-sort > .icon {
      --icon-size: 14;
      display: none;
      color: var(--accent);
    }

    /* The header is as high as the button: a ring outside it would be cut by the edge of the scroll container */
    .list-sort:focus-visible {
      outline-offset: -2px;
    }

    .list-sort.is-active {
      color: var(--text);
      font-weight: var(--weight-bold);
    }

    .list-sort.is-active[data-dir="asc"] > .sort-ico-up,
    .list-sort.is-active[data-dir="desc"] > .sort-ico-down {
      display: block;
    }

    .file-item + .file-item::before {
      left: 0;
    }

    .row-select {
      display: flex;
      align-items: center;
      width: auto;
      height: calc(var(--tile-size) + 4px);
      margin-right: var(--space-1);
      border-radius: var(--tile-radius);
    }

    .row-check {
      display: block;
      visibility: hidden;
      flex: none;
      width: var(--check-compact-size);
      height: var(--check-compact-size);
      margin-right: var(--check-compact-gap);
      border: 1.5px solid var(--border-strong);
      border-radius: var(--check-compact-radius);
      background: var(--field-bg);
    }

    .row-check::before {
      content: none;
    }

    .row-check::after {
      width: var(--check-compact-glyph);
      height: var(--check-compact-glyph);
      margin: calc(var(--check-compact-glyph) / -2) 0 0 calc(var(--check-compact-glyph) / -2);
    }

    .is-selecting .row-select > .entry-icon {
      display: grid;
    }

    .is-selecting .row-check {
      display: block;
    }

    .is-selecting .row-check,
    .file-item.is-selected .row-check,
    .row-select:focus-visible > .row-check {
      visibility: visible;
    }

    .file-item.is-selected .row-check {
      border-color: var(--accent);
      background: var(--accent);
    }

    .entry-main {
      padding: 0 var(--space-3) 0 var(--space-2);
    }

    .file-size {
      padding-right: var(--space-6);
    }
  }

  @media (min-width: 900px) and (min-height: 500px) and (hover: hover) {
    .list-sort:hover {
      background: var(--state-hover);
      color: var(--text);
    }

    .file-item:hover {
      background: var(--state-hover);
    }

    .file-item.is-selected:hover {
      background: var(--accent-soft-hover);
    }

    .file-item:hover .row-check {
      visibility: visible;
    }

    .file-item:hover .row-menu-btn {
      --btn-fg: var(--text-muted);
    }
  }

  /* Wide desktop with a mouse: download, public link and favourite appear under the pointer — in the order of
     the markup, which is the order of the menu: Tab walks them left to right (an order of their own, set with
     the property order, was walked right to left) */
  @media (min-width: 1200px) and (min-height: 500px) and (hover: hover) and (pointer: fine) {
    .list-head,
    .file-item {
      grid-template-columns: var(--col-lead) minmax(0, 1fr) var(--col-size) var(--col-date) var(--col-actions);
    }

    .file-actions:not(.is-open) {
      display: flex;
      visibility: hidden;
      gap: var(--space-1);
      margin-right: var(--space-1);
    }

    .file-actions:not(.is-open) > * {
      display: none;
    }

    .file-actions:not(.is-open) > .fav,
    .file-actions:not(.is-open) > [data-share],
    .file-actions:not(.is-open) > a.btn {
      display: inline-flex;
    }

    .file-actions:not(.is-open) .action-label {
      display: none;
    }

    .file-actions:not(.is-open) > .btn > .icon {
      --icon-size: 18;
    }

    .row-menu-btn > .icon {
      --icon-size: 18;
    }

    .file-item:hover .file-actions,
    .file-item:focus-within .file-actions {
      visibility: visible;
    }

    .is-selecting .file-item .file-actions:not(.is-open) {
      visibility: hidden;
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .sync-spinner {
      animation: spin 0.9s linear infinite;
    }
  }

  /* ------------------------------------------------------------------------
     Tiles view of the file list
     ul.file-list.tiles-view > li.file-item — the markup of the list row, laid out as a tile:
       [icon tile = selection toggle]            [«⋯»]
       name, up to two lines
       size                          [favourite] [public link]
     section.files-section.is-tiles > div.list-head — the column titles become a row of sort buttons (desktop)
     ------------------------------------------------------------------------ */
  .file-list.tiles-view {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    gap: 6px;
    padding: var(--space-2) calc(var(--gutter) - 4px) var(--space-4);
  }

  /* The icon stands 8px from the edges of the tile: the selection target around it is 2px larger on every side, so
     the padding is 6px. In the three columns of a phone the name may run to 4px from the right edge — a two-line
     name needs every pixel there; from 600px its box is symmetric. */
  .tiles-view > .file-item {
    --tile-size: 48px;
    --tile-icon: 24;
    --tile-radius: var(--radius-md);
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto minmax(0, 1fr) auto;
    align-items: start;
    min-height: 0;
    padding: 6px 0 10px 6px;
    border-radius: var(--radius-lg);
    box-shadow: inset 0 0 0 1px var(--border);
  }

  .tiles-view > .file-item + .file-item::before {
    content: none;
  }

  .tiles-view > .file-item.is-selected {
    box-shadow: inset 0 0 0 1.5px var(--accent);
  }

  /* The icon tile is the selection toggle on every device: it turns into a check tile */
  .tiles-view .row-select {
    display: grid;
    grid-area: 1 / 1;
    width: calc(var(--tile-size) + 4px);
    height: calc(var(--tile-size) + 4px);
    margin: 0;
    border-radius: calc(var(--tile-radius) + 2px);
  }

  .tiles-view .row-check {
    display: none;
    visibility: visible;
    width: var(--tile-size);
    height: var(--tile-size);
    margin: 0;
    border: 0;
    border-radius: var(--tile-radius);
    background: var(--tile-file-bg);
  }

  .tiles-view .row-check::before {
    content: "";
  }

  .tiles-view .row-check::after {
    width: 18px;
    height: 18px;
    margin: -9px 0 0 -9px;
  }

  .is-selecting .tiles-view .row-select > .entry-icon {
    display: none;
  }

  .is-selecting .tiles-view .row-check {
    display: grid;
  }

  .tiles-view .file-item.is-selected .row-check {
    background: var(--accent);
  }

  .tiles-view .row-actions {
    grid-area: 1 / 2;
    align-self: center;
    margin-right: 2px;
  }

  .tiles-view .file-actions:not(.is-open) {
    display: none;
  }

  /* The open target: two lines of the name; its ::after stretches it over the rest of the tile under the icon row */
  .tiles-view .entry-main {
    grid-area: 2 / 1 / 3 / -1;
    align-items: flex-start;
    align-self: start;
    min-height: calc(6px + 2 * var(--leading-sm));
    padding: 6px var(--space-1) 0 2px;
  }

  /* Focus: the ring is drawn around the whole tile (the open target is the tile, not its two lines of text) */
  .tiles-view .entry-main:focus-visible {
    outline: none;
  }

  .tiles-view .entry-main:focus-visible::before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    border-radius: var(--radius-lg);
    outline: 2px solid var(--focus-ring);
    outline-offset: -2px;
    pointer-events: none;
  }

  .tiles-view .entry-main::after {
    content: "";
    position: absolute;
    top: calc(var(--tile-size) + var(--space-3));
    right: 0;
    bottom: 0;
    left: 0;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  }

  .tiles-view .file-info,
  .tiles-view .file-name-row {
    display: block;
  }

  .tiles-view .file-name {
    display: -webkit-box;
    font-size: var(--font-size-sm);
    white-space: normal;
    overflow-wrap: anywhere;
    text-wrap: pretty;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }

  .tiles-view .file-meta,
  .tiles-view .tag-chips,
  .tiles-view .file-date {
    display: none;
  }

  .tiles-view .file-size {
    display: block;
    grid-area: 3 / 1 / 4 / -1;
    padding: var(--space-1) var(--space-2) 0 2px;
    overflow: hidden;
    font-size: var(--font-size-xs);
    line-height: var(--leading-xs);
    text-align: left;
    text-overflow: ellipsis;
  }

  /* A folder's totals («4 файла · 1,8 КБ») do not fit a tile of the phone layout: there the tile shows only the
     count; the size (span.folder-size inside .file-size) comes back with the desktop tile */
  .tiles-view .folder-size {
    display: none;
  }

  /* The marks stand on the size line, in the bottom right corner of the tile */
  .tiles-view .fav-badge,
  .tiles-view .share-badge,
  .tiles-view .upload-badge {
    position: absolute;
    right: 10px;
    bottom: 10px;
  }

  /* The order of the name row is kept: favourite, public link, upload link — the last one stands in the corner */
  .tiles-view .is-shared .fav-badge,
  .tiles-view .is-upload-link .fav-badge,
  .tiles-view .is-upload-link .share-badge {
    right: 32px;
  }

  .tiles-view .is-shared.is-upload-link .fav-badge {
    right: 54px;
  }

  .tiles-view .is-fav > .file-size,
  .tiles-view .is-shared > .file-size,
  .tiles-view .is-upload-link > .file-size {
    padding-right: 30px;
  }

  .tiles-view .is-fav.is-shared > .file-size,
  .tiles-view .is-fav.is-upload-link > .file-size,
  .tiles-view .is-shared.is-upload-link > .file-size {
    padding-right: 52px;
  }

  .tiles-view .is-fav.is-shared.is-upload-link > .file-size {
    padding-right: 74px;
  }

  @media (min-width: 600px) {
    .file-list.tiles-view {
      grid-template-columns: repeat(auto-fill, minmax(136px, 1fr));
      gap: var(--space-3);
      padding: var(--space-2) var(--gutter) var(--space-6);
    }

    .tiles-view > .file-item {
      padding: var(--space-2) 2px var(--space-3) var(--space-2);
    }

    /* The name keeps the same 10px from both edges of the tile */
    .tiles-view .entry-main {
      padding-right: var(--space-2);
    }

    .tiles-view .fav-badge,
    .tiles-view .share-badge,
    .tiles-view .upload-badge {
      right: var(--space-3);
      bottom: var(--space-3);
    }

    .tiles-view .is-shared .fav-badge,
    .tiles-view .is-upload-link .fav-badge,
    .tiles-view .is-upload-link .share-badge {
      right: 34px;
    }

    .tiles-view .is-shared.is-upload-link .fav-badge {
      right: 56px;
    }
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .file-list.tiles-view {
      grid-template-columns: repeat(auto-fill, minmax(152px, 1fr));
      padding: var(--space-4) var(--space-5) var(--space-5);
    }

    /* The column titles have no columns to stand over: they become a row of sort buttons */
    .files-section.is-tiles > .list-head {
      display: flex;
      gap: var(--space-5);
      padding: 0 var(--space-5);
    }

    .files-section.is-tiles .list-head-name,
    .files-section.is-tiles .list-head-size {
      padding: 0;
    }

    .tiles-view .folder-size {
      display: inline;
    }

    /* The dropdown opens to the right of «⋯»: a tile in the first column has no room on its left */
    .tiles-view .file-actions.is-open {
      right: auto;
      left: 0;
    }

    /* …except in the last column, where it would leave the list: the script sets .is-end */
    .tiles-view .file-actions.is-open.is-end {
      right: 0;
      left: auto;
    }
  }

  @media (min-width: 900px) and (min-height: 500px) and (hover: hover) {
    .tiles-view .row-select:hover > .entry-icon,
    .tiles-view .row-select:focus-visible > .entry-icon {
      display: none;
    }

    .tiles-view .row-select:hover > .row-check,
    .tiles-view .row-select:focus-visible > .row-check {
      display: grid;
    }
  }

  /* ------------------------------------------------------------------------
     Section list row: «Недавние», «Избранное», «Корзина» — the same row as in the file list, without selection
     ul.side-entry-list > li.side-entry[.is-folder]
       button.side-entry-open | a.side-entry-open[href]     open target: icon + name + meta
         span.side-entry-ico.entry-icon.type-icon.type-…   the same tile as in file rows
         span.side-entry-main > span.side-entry-name + span.side-entry-meta
       span.side-entry-actions > .btn.btn-icon …
     li.side-entry.side-entry-empty                          the line shown when the list is empty
     ------------------------------------------------------------------------ */
  .side-entry {
    position: relative;
    display: flex;
    align-items: center;
    min-height: var(--row-height);
    padding: 0 calc(var(--gutter) - 12px) 0 var(--gutter);
  }

  .side-entry + .side-entry::before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    left: calc(var(--gutter) + var(--tile-size) + var(--space-3));
    border-top: 1px solid var(--border);
    pointer-events: none;
  }

  .side-entry-open {
    display: flex;
    align-items: center;
    align-self: stretch;
    gap: var(--space-3);
    flex: 1;
    min-width: 0;
    padding-right: var(--space-2);
    text-align: left;
  }

  .side-entry-open:focus-visible {
    border-radius: var(--radius-sm);
    outline-offset: -2px;
  }

  .side-entry-main {
    display: grid;
    flex: 1;
    min-width: 0;
  }

  .side-entry-name {
    overflow: hidden;
    font-size: var(--body-font);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .side-entry-meta {
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .side-entry-actions {
    display: flex;
    align-items: center;
    flex: none;
    gap: var(--space-1);
  }

  /* Inside a list the ring is inset: the last action of a row stands 4px from the edge of the screen or the card */
  .side-entry-actions > .btn:focus-visible {
    outline-offset: -2px;
  }

  /* The same line says that the list could not be loaded: li.side-entry.side-entry-empty > span + button.btn.btn-secondary.btn-sm */
  .side-entry-empty {
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    min-height: 0;
    padding-top: var(--space-6);
    padding-bottom: var(--space-6);
    color: var(--text-muted);
  }

  @media (min-width: 900px) and (min-height: 500px) {
    /* --side-indent is the left edge of the rows and of their column titles: the card padding by default; the
       storage screen sets it to the tile column of the file table (after the checkbox column) */
    .side-entry,
    .list-head.side-head {
      padding-right: var(--space-2);
      padding-left: var(--side-indent, var(--space-5));
    }

    .side-entry + .side-entry::before {
      left: 0;
    }
  }

  @media (min-width: 900px) and (min-height: 500px) and (hover: hover) {
    .side-entry:not(.side-entry-empty):hover {
      background: var(--state-hover);
    }
  }

  /* Columns. A list whose rows carry a size and a date is .side-entry-list.has-columns: from 600px the cells
     replace the meta line; on desktop the list stands under column titles.
       li.side-entry > .side-entry-open + span.side-entry-size + span.side-entry-date + span.side-entry-actions[.is-wide]
                       [+ div.popover-anchor.side-entry-more — «⋯» with a menu, below 600px only]
       div.list-head.side-head > span.side-head-name + span.side-head-size + span.side-head-date + span.side-head-actions
     The cells may also stand inside .side-entry-open (a row that is one button). Widths are those of the file
     table; the action column is --side-actions-width, set by the list or the screen. */
  .side-entry-size,
  .side-entry-date {
    display: none;
    flex: none;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  /* A folder shows its statistics there: running text, proportional figures */
  .side-entry.is-folder .side-entry-size {
    font-variant-numeric: normal;
  }

  .side-entry-more {
    flex: none;
  }

  /* Labelled actions that need a wide row (.side-entry-actions.is-wide): shown from 600px; below that the same
     actions are in the menu behind «⋯» (.side-entry-more) */
  .side-entry-actions.is-wide {
    display: none;
  }

  /* Names that must be readable in full (a public page has no viewer and no properties to read them in) */
  .side-entry-list.is-wrapping .side-entry-open {
    padding-top: 10px;
    padding-bottom: 10px;
  }

  .side-entry-list.is-wrapping .side-entry-name {
    overflow: visible;
    overflow-wrap: anywhere;
    white-space: normal;
  }

  /* A one-line row is --row-height high in a short window too (56px there, the tile is 40px) */
  @media (max-height: 499px) {
    .side-entry-list.is-wrapping .side-entry-open {
      padding-top: var(--space-2);
      padding-bottom: var(--space-2);
    }
  }

  @media (min-width: 600px) {
    .side-entry-list.has-columns .side-entry-meta {
      display: none;
    }

    .side-entry-size,
    .side-entry-date {
      display: block;
    }

    .side-entry-size {
      width: var(--col-size);
      padding-right: var(--space-5);
      text-align: right;
    }

    .side-entry-date {
      width: var(--col-date);
    }

    .side-entry-more {
      display: none;
    }

    .side-entry-actions.is-wide {
      display: flex;
    }
  }

  /* A wrapping list has nothing to cut a name with: below 800px the cells would leave it a few letters per line,
     so it keeps the meta line until then */
  @media (min-width: 600px) and (max-width: 799px) {
    .side-entry-list.has-columns.is-wrapping .side-entry-meta {
      display: block;
    }

    .side-entry-list.is-wrapping .side-entry-size,
    .side-entry-list.is-wrapping .side-entry-date {
      display: none;
    }
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .list-head.side-head {
      display: flex;
    }

    .side-head-name {
      flex: 1;
      min-width: 0;
    }

    .side-head-size,
    .side-entry-size {
      width: var(--col-size);
      padding-right: var(--space-6);
      text-align: right;
    }

    .side-head-date,
    .side-entry-date {
      width: var(--col-date);
    }

    .side-head-actions,
    .side-entry-actions {
      flex: none;
      width: var(--side-actions-width);
      justify-content: flex-end;
    }

    /* Under a column title («Удалён») the word is not repeated in the cell */
    .side-entry-date-word {
      display: none;
    }
  }

  @media (min-width: 900px) and (min-height: 500px) and (hover: hover) and (pointer: fine) {
    .side-entry-list.is-wrapping .side-entry-open {
      padding-top: var(--space-2);
      padding-bottom: var(--space-2);
    }
  }

  /* ------------------------------------------------------------------------
     Generic list item: users, groups, tokens, keys, nodes — anything that is not a file row
     <ul class="item-list"><li class="item">
       [avatar | .item-icon] <div class="item-main"><div class="item-title">…</div><div class="item-meta">…</div></div>
       <div class="item-actions">…</div></li></ul>
     Product aliases: .token-item, .token-meta, .token-name
     ------------------------------------------------------------------------ */
  .item-list {
    display: grid;
    min-width: 0;
  }

  .item,
  .token-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
    min-height: var(--row-height);
    padding: var(--space-2) 0;
  }

  .item + .item,
  .token-item + .token-item {
    border-top: 1px solid var(--border);
  }

  .item-icon {
    display: grid;
    place-items: center;
    flex: none;
    width: var(--tile-size);
    height: var(--tile-size);
    border-radius: var(--tile-radius);
    background: var(--tile-file-bg);
    color: var(--text-muted);
  }

  /* A tile that carries the state of its object (a node that answers, a node that does not) */
  .item-icon.is-ok {
    background: var(--success-soft);
    color: var(--success);
  }

  .item-icon.is-danger,
  .entry-icon.is-danger {
    background: var(--danger-soft);
    color: var(--danger);
  }

  .item-main,
  .token-meta {
    display: grid;
    flex: 1;
    gap: 2px;
    min-width: 0;
  }

  .item-title,
  .token-name {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
    min-width: 0;
    font-weight: var(--weight-medium);
    overflow-wrap: anywhere;
  }

  .item-meta {
    min-width: 0;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    overflow-wrap: anywhere;
  }

  /* An identifier that is read and copied as one token (a bucket name): small enough to stay on one line
     beside the action on a 360px screen */
  .item-meta.mono {
    font-size: var(--font-size-xs);
  }

  .item-actions {
    display: flex;
    align-items: center;
    flex: none;
    gap: var(--space-1);
  }

  /* ------------------------------------------------------------------------
     Tight item list: every row carries the same glyph, so the tile is dropped in the phone
     layout and the text gets its width (tokens, login links, passkeys)
     <ul class="item-list is-tight">
     ------------------------------------------------------------------------ */
  .item-list.is-tight > .item > .item-icon {
    display: none;
  }

  @media (min-width: 600px) {
    .item-list.is-tight > .item > .item-icon {
      display: grid;
    }
  }

  /* ------------------------------------------------------------------------
     Card: a white sheet on the paper page. It has no padding of its own; the rows of a list inside it keep the
     card padding, so the hairline between rows spans the card
     ------------------------------------------------------------------------ */
  .card {
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-outline), var(--shadow-sm);
  }

  .card > .item-list > .item {
    padding-right: var(--space-4);
    padding-left: var(--space-4);
  }

  @media (min-width: 600px) {
    .card > .item-list > .item {
      padding-right: var(--space-5);
      padding-left: var(--space-5);
    }
  }

  /* ------------------------------------------------------------------------
     Page section: a titled block of a page
     section.page-section > div.page-section-head (h2.page-section-title [> span.count] + [p.page-section-note | a.btn.btn-ghost.btn-accent.btn-sm])
                            + content
     ------------------------------------------------------------------------ */
  .page-section {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
    gap: var(--space-3);
    min-width: 0;
  }

  .page-section-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0 var(--space-3);
    min-width: 0;
  }

  .page-section-title {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    font-size: var(--font-size-xl);
    line-height: var(--leading-md);
    font-weight: var(--weight-bold);
  }

  .page-section-note {
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
  }

  /* An action in the head is a text button. It keeps its target and gives its extra height and its side padding
     back, so the title is as far from the content as in a head without an action and the label ends on the edge */
  .page-section-head > .btn-ghost {
    margin: calc((var(--leading-md) - var(--control-height-sm)) / 2) calc(var(--space-3) * -1)
      calc((var(--leading-md) - var(--control-height-sm)) / 2) 0;
  }

  /* ------------------------------------------------------------------------
     Definition list (properties)
     <dl class="props-list"><dt class="props-label">Тип</dt><dd class="props-value">…</dd>…</dl>
     ------------------------------------------------------------------------ */
  .props-list {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    min-width: 0;
  }

  .props-label {
    padding-top: 10px;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
  }

  .props-value {
    min-width: 0;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border);
    overflow-wrap: anywhere;
  }

  .props-value:last-child {
    border-bottom: 0;
  }

  @media (min-width: 600px) {
    .props-list {
      grid-template-columns: 148px minmax(0, 1fr);
    }

    .props-label,
    .props-value {
      padding: 10px 0;
      border-bottom: 1px solid var(--border);
    }

    /* The gap between the columns is padding, so the hairline of a row is not cut in two */
    .props-label {
      padding-right: var(--space-4);
    }

    .props-label:nth-last-child(2) {
      border-bottom: 0;
    }
  }

  /* A machine value with a copy button: the value wraps, the button stays on its first line
     dd.props-value.props-copy > span.mono.props-copy-text + button.btn.btn-icon */
  .props-copy {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
  }

  .props-copy-text {
    flex: 1;
    min-width: 0;
  }

  .props-copy > .btn-icon {
    --btn-fg: var(--accent);
    --btn-fg-hover: var(--accent);
    margin: calc((var(--leading-sm) - var(--icon-button-size)) / 2) calc(var(--space-2) * -1) calc((var(--leading-sm) - var(--icon-button-size)) / 2) 0;
  }

  .props-value.mono,
  .props-copy-text {
    font-size: var(--font-size-sm);
  }

  /* A muted addition to a value: «(4 404 019 байт)» */
  .props-note {
    color: var(--text-muted);
  }

  /* A value that keeps its line breaks (a description) */
  .props-text {
    white-space: pre-wrap;
  }

  /* ------------------------------------------------------------------------
     Menu
     One block, two presentations: a bottom sheet in the phone layout, a dropdown on desktop.
     Generic:   <div class="popover-anchor"><button aria-haspopup="menu" aria-expanded="true">…</button>
                  <div class="popover" [hidden]><div class="menu-head">…</div>
                    <div class="menu" role="menu"><button class="menu-item" role="menuitem">icon + label</button>
                    <div class="menu-sep" role="separator"></div>…</div></div></div>
     File row:  div.file-actions.is-open is the popover and its .btn children are the items.
     Modifiers: .is-up (opens above the anchor), .is-start (aligned to the anchor's left edge) — desktop only.
     Sheet scrim: <div class="sheet-backdrop"> — one per page, shown while a sheet is open.
     ------------------------------------------------------------------------ */
  .popover-anchor {
    position: relative;
  }

  .sheet-backdrop {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: var(--z-scrim);
    background: var(--scrim);
  }

  .popover,
  .file-actions.is-open {
    position: fixed;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: var(--z-popover);
    display: flex;
    flex-direction: column;
    max-width: 560px;
    max-height: calc(100vh - 24px);
    max-height: calc(100dvh - 24px - var(--safe-top));
    margin: 0 auto;
    padding: 0 0 calc(var(--space-2) + var(--safe-bottom));
    overflow-y: auto;
    overscroll-behavior: contain;
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    background: var(--surface-overlay);
    box-shadow: var(--shadow-lg);
    color: var(--text);
    text-align: left;
  }

  /* Grabber */
  .popover::before,
  .file-actions.is-open::before {
    content: "";
    flex: none;
    align-self: center;
    width: 36px;
    height: 4px;
    margin: var(--space-2) 0;
    border-radius: var(--radius-full);
    background: var(--border-strong);
    opacity: 0.5;
  }

  /* Sheet header: what the actions apply to. Stays in place while the list scrolls */
  .menu-head {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: none;
    padding: var(--space-1) var(--gutter) var(--space-3);
    border-bottom: 1px solid var(--border);
    background: var(--surface-overlay);
  }

  /* An avatar (36px) stands in the 40px tile column, so the name keeps the text edge of the items */
  .menu-head > .avatar {
    margin: 0 2px;
  }

  .menu-head-text {
    display: grid;
    flex: 1;
    min-width: 0;
  }

  .menu-head-title {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    font-size: var(--font-size-lg);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
  }

  .menu-head-title > :first-child {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .menu-head-meta {
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .menu {
    display: flex;
    flex-direction: column;
    flex: none;
    padding: var(--space-1) 0;
  }

  .file-actions.is-open > .menu-head {
    margin-bottom: var(--space-1);
  }

  .menu-item,
  .file-actions.is-open > .btn {
    --btn-fg: var(--text);
    --btn-fg-hover: var(--text);
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-3);
    flex: none;
    width: 100%;
    height: var(--menu-item-height);
    padding: 0 var(--gutter);
    border: 0;
    border-radius: 0;
    color: var(--text);
    font-size: var(--menu-item-font);
    line-height: var(--leading-md);
    font-weight: var(--weight-regular);
    text-align: left;
    white-space: nowrap;
  }

  /* The glyph is centred on the tile column of the header, so labels share its text edge */
  .menu-item > .icon,
  .file-actions.is-open > .btn > .icon {
    --icon-size: 22;
    margin: 0 calc((var(--tile-size) - 22px) / 2);
    color: var(--text-muted);
  }

  .menu-item > span:first-of-type,
  .file-actions.is-open .action-label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Trailing part of an item: a value, a shortcut, a check for the chosen option */
  .menu-item-meta {
    flex: none;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
  }

  .menu-item.is-danger,
  .menu-item.is-danger > .icon,
  .file-actions.is-open > .btn-danger,
  .file-actions.is-open > .btn-danger > .icon {
    --btn-fg: var(--danger);
    --btn-fg-hover: var(--danger);
    color: var(--danger);
  }

  .file-actions.is-open > .fav.is-active > .icon,
  .file-actions.is-open > [data-share].is-active > .icon,
  .file-actions.is-open > [data-upload-link].is-active > .icon {
    color: var(--highlight);
  }

  .menu-item:disabled,
  .menu-item[aria-disabled="true"] {
    color: var(--text-disabled);
    cursor: default;
    pointer-events: none;
  }

  .menu-item:disabled > .icon,
  .menu-item[aria-disabled="true"] > .icon {
    color: inherit;
  }

  .menu-item:focus-visible,
  .file-actions.is-open > .btn:focus-visible {
    outline-offset: -2px;
  }

  .menu-item:active {
    background: var(--state-pressed);
  }

  .menu-sep {
    flex: none;
    height: 1px;
    margin: var(--space-1) 0;
    background: var(--border);
  }

  /* Group title or a note inside a menu */
  .menu-note {
    flex: none;
    padding: var(--space-2) var(--gutter);
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
  }

  /* A labelled control inside a menu (the theme switch of the account menu)
     div.menu-field[role=group] > span.menu-field-label + div.segmented */
  .menu-field {
    display: grid;
    gap: var(--space-2);
    padding: var(--space-2) var(--gutter) var(--space-3);
  }

  .menu-field-label {
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
  }

  @media (hover: hover) {
    .menu-item:hover {
      background: var(--state-hover);
    }

    .menu-item.is-danger:hover,
    .file-actions.is-open > .btn-danger:hover {
      --btn-layer: var(--danger-soft);
      background-color: var(--danger-soft);
    }
  }

  @media (max-height: 499px) {
    .popover,
    .file-actions.is-open {
      max-width: 480px;
    }
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .sheet-backdrop {
      display: none;
    }

    .popover,
    .file-actions.is-open {
      position: absolute;
      top: calc(100% + var(--space-1));
      right: 0;
      bottom: auto;
      left: auto;
      width: max-content;
      min-width: 232px;
      max-width: min(320px, calc(100vw - 32px));
      /* --menu-room: set by the script on a menu that fits neither below nor above its anchor — the room of
         the side it opens to; the menu scrolls inside */
      max-height: min(560px, calc(100vh - 32px), var(--menu-room, 560px));
      margin: 0;
      padding: var(--space-1);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-outline), var(--shadow-lg);
    }

    .popover::before,
    .file-actions.is-open::before {
      content: none;
    }

    .popover.is-up,
    .file-actions.is-open.is-up {
      top: auto;
      bottom: calc(100% + var(--space-1));
    }

    .popover.is-start {
      right: auto;
      left: 0;
    }

    .menu-head {
      padding: var(--space-2) var(--space-3) var(--space-3);
    }

    .menu-head > .avatar {
      margin: 0;
    }

    .file-actions.is-open > .menu-head {
      display: none;
    }

    .menu {
      padding: 0;
    }

    .menu-item,
    .file-actions.is-open > .btn {
      padding: 0 var(--space-3);
      border-radius: var(--radius-sm);
    }

    .menu-item > .icon,
    .file-actions.is-open > .btn > .icon {
      --icon-size: 18;
      margin: 0;
    }

    .menu-sep {
      margin: var(--space-1) calc(var(--space-1) * -1);
    }

    .menu-note {
      padding: var(--space-2) var(--space-3);
    }

    .menu-field {
      padding: var(--space-1) var(--space-3) var(--space-3);
    }

    .menu-field .segmented-item > span {
      padding: 0 var(--space-2);
    }

    /* A dropdown hangs from its object, so it has no header — unless the menu is away from what it applies to
       (.popover.has-head: the account menu) */
    .popover:not(.has-head) > .menu-head {
      display: none;
    }

    /* A dropdown that keeps its header: the glyphs are centred on the avatar column (36px), so the labels share
       the text edge of the name — as in the sheet */
    .popover.has-head .menu-item > .icon {
      margin: 0 calc((36px - 18px) / 2);
    }
  }

  @keyframes sheet-in {
    from {
      opacity: 0;
      transform: translateY(24px);
    }
  }

  @keyframes pop-in {
    from {
      opacity: 0;
      transform: translateY(-4px);
    }
  }

  @keyframes fade-in {
    from {
      opacity: 0;
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .sheet-backdrop {
      animation: fade-in var(--duration-base) var(--ease-standard);
    }

    .popover,
    .file-actions.is-open {
      animation: sheet-in var(--duration-base) var(--ease-out);
    }
  }

  @media (prefers-reduced-motion: no-preference) and (min-width: 900px) and (min-height: 500px) {
    .popover,
    .file-actions.is-open {
      animation: pop-in var(--duration-fast) var(--ease-out);
    }
  }

  /* ------------------------------------------------------------------------
     Dialog
     div.modal[.modal-confirm][.modal-full|.modal-screen][hidden] > div.modal-backdrop + div.modal-dialog[.modal-dialog-sm|.modal-dialog-lg]
       > [form >] div.modal-header (h2.modal-title + button.modal-close) + div.modal-body + div.modal-footer
     Phone layout: a sheet from the bottom; .modal-full fills the screen (long dialogs).
     From 600px: a centred window — sm 440, default 560, lg 880.
     Footer: DOM order is destructive, dismiss, primary. Below 600px the buttons stack, primary on top.
     ------------------------------------------------------------------------ */
  .modal {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: flex-end;
    justify-content: center;
  }

  .modal-backdrop {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: var(--scrim);
  }

  .modal-dialog {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 100%;
    max-height: calc(100vh - 24px);
    max-height: calc(100dvh - 24px - var(--safe-top));
    overflow: hidden;
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    background: var(--surface-overlay);
    box-shadow: var(--shadow-lg);
  }

  /* The frame takes the focus when a window opens (the place Tab starts from; a field the window was opened for
     takes it next). It is no control: opened from the keyboard, the frame drew the ring of a control around
     the whole window. */
  .modal-dialog:focus-visible {
    outline: none;
  }

  .modal-dialog::before {
    content: "";
    flex: none;
    align-self: center;
    width: 36px;
    height: 4px;
    margin-top: var(--space-2);
    border-radius: var(--radius-full);
    background: var(--border-strong);
    opacity: 0.5;
  }

  .modal-dialog > form {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-height: 0;
  }

  .modal-header {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: none;
    min-height: 56px;
    padding: var(--space-1) calc(var(--gutter) - 10px) var(--space-1) var(--gutter);
  }

  /* The title carries its own air: with the 4px of the header a one-line title makes the 56px header, and a
     title that wraps (a question on a 360px phone) keeps the same 16px above and below it */
  .modal-title {
    flex: 1;
    min-width: 0;
    padding: var(--space-3) 0;
    font-size: var(--font-size-2xl);
    line-height: var(--leading-md);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    overflow-wrap: anywhere;
  }

  .modal-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
    gap: var(--space-4);
    flex: 1 1 auto;
    min-height: 0;
    padding: var(--space-1) var(--gutter) var(--space-4);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .modal-body:last-child {
    padding-bottom: calc(var(--space-5) + var(--safe-bottom));
  }

  /* First line of a dialog: the object it is about */
  .modal-lead {
    font-weight: var(--weight-medium);
    overflow-wrap: anywhere;
  }

  .modal-footer {
    display: flex;
    flex-direction: column-reverse;
    gap: var(--space-2);
    flex: none;
    padding: var(--space-2) var(--gutter) calc(var(--space-4) + var(--safe-bottom));
  }

  .modal-footer-actions {
    display: flex;
    flex-direction: column-reverse;
    gap: var(--space-2);
  }

  .modal-footer .btn {
    width: 100%;
  }

  /* The body is higher than its box (the script sets .is-scrollable): a hairline marks the edge of the pinned footer */
  .modal-body.is-scrollable + .modal-footer {
    padding-top: var(--space-3);
    box-shadow: 0 -1px 0 var(--border);
  }

  /* A body without a pinned footer has no such edge. While its end is out of view (the script keeps .is-cut-end
     on the body) its last 24px fade out: a window lower than its content reads as "scroll for more", not as a
     box that ends in half a row. The mask belongs to the box of the body and does not scroll with it. */
  .modal-body.is-cut-end:last-child {
    -webkit-mask-image: linear-gradient(to top, transparent, black var(--space-6));
    mask-image: linear-gradient(to top, transparent, black var(--space-6));
  }

  /* The body has been scrolled (the script keeps .is-scrolled on the dialog while scrollTop > 0): the header gets
     the same edge, so fields and rows that slide under the title read as scrolled away, not as clipped */
  .modal-dialog.is-scrolled .modal-header {
    position: relative;
    z-index: 1;
    box-shadow: 0 1px 0 var(--border);
  }

  .modal-full .modal-dialog {
    height: 100%;
    max-height: none;
    padding-top: var(--safe-top);
    border-radius: 0;
  }

  .modal-full .modal-dialog::before {
    content: none;
  }

  @media (min-width: 600px) {
    .modal {
      align-items: center;
      padding: var(--space-6);
    }

    .modal-dialog,
    .modal-full .modal-dialog {
      max-width: 560px;
      height: auto;
      max-height: 100%;
      padding-top: 0;
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-outline), var(--shadow-lg);
    }

    .modal-dialog::before {
      content: none;
    }

    .modal-dialog.modal-dialog-sm,
    .modal-confirm .modal-dialog {
      max-width: 440px;
    }

    .modal-dialog.modal-dialog-lg {
      max-width: 880px;
    }

    .modal-header {
      min-height: 64px;
      padding: var(--space-3) var(--space-4) var(--space-2) var(--space-6);
    }

    /* 24 + 2 × 10 = the 44px box of the header, also of the 52px header of a short window */
    .modal-title {
      padding: 10px 0;
    }

    .modal-body {
      padding: var(--space-1) var(--space-6) var(--space-6);
    }

    .modal-body:last-child {
      padding-bottom: var(--space-6);
    }

    .modal-footer {
      flex-direction: row;
      align-items: center;
      justify-content: flex-end;
      padding: 0 var(--space-6) var(--space-6);
    }

    .modal-footer-actions {
      flex-direction: row;
      margin-left: auto;
    }

    .modal-footer .btn {
      width: auto;
    }

    /* A destructive text button sits alone on the left; its label starts on the edge of the form */
    .modal-footer > .btn-danger:first-child:not(:last-child) {
      margin-right: auto;
      margin-left: calc(var(--space-3) * -1 - 1px);
    }
  }

  @media (min-width: 600px) and (max-height: 499px) {
    /* A landscape phone: the wide windows reach the sides of the screen, where the cut-out and the corners are */
    .modal {
      padding: var(--space-3) calc(var(--space-3) + var(--safe-right)) var(--space-3) calc(var(--space-3) + var(--safe-left));
    }

    .modal-header {
      min-height: 52px;
      padding-top: var(--space-1);
      padding-bottom: var(--space-1);
    }

    .modal-body {
      padding-bottom: var(--space-4);
    }

    .modal-footer {
      padding-bottom: var(--space-4);
    }
  }

  /* .modal-screen (on .modal): a dialog that is a screen in the whole phone layout — tablets and short windows
     included — and a window as large as the browser window on desktop. The viewer. */
  .modal-screen {
    align-items: stretch;
    padding: 0;
  }

  .modal-screen .modal-dialog {
    max-width: none;
    height: 100%;
    max-height: none;
    padding-top: var(--safe-top);
    border-radius: 0;
    box-shadow: none;
  }

  .modal-screen .modal-dialog::before {
    content: none;
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .modal-screen {
      padding: var(--space-8);
    }

    .modal-screen .modal-dialog {
      max-width: 1440px;
      padding-top: 0;
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-outline), var(--shadow-lg);
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .modal-backdrop {
      animation: fade-in var(--duration-base) var(--ease-standard);
    }

    .modal-dialog {
      animation: sheet-in var(--duration-slow) var(--ease-out);
    }
  }

  @media (prefers-reduced-motion: no-preference) and (min-width: 600px) {
    .modal-dialog {
      animation: pop-in var(--duration-base) var(--ease-out);
    }
  }

  /* ------------------------------------------------------------------------
     Dialog body building blocks
     .modal-form     a <form> inside .modal-body keeps the rhythm of the body (16px between fields)
     .modal-object   the object of the dialog with its current marks: p.modal-lead + .tag-chips
     .modal-section  a titled group inside a long dialog: hairline, p.field-label, content
     .modal-columns  two groups side by side on desktop (connection fields | buckets)
     ------------------------------------------------------------------------ */
  .modal-form,
  .modal-columns {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
    gap: var(--space-4);
    min-width: 0;
  }

  .modal-object {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2);
  }

  .modal-section {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
    gap: var(--space-3);
    min-width: 0;
    padding-top: var(--space-4);
    border-top: 1px solid var(--border);
  }

  /* A list directly under the section title: its first row needs no extra air above it */
  .modal-section > .item-list {
    margin: calc(var(--space-2) * -1) 0;
  }

  .modal-body > .item-list {
    margin: calc(var(--space-2) * -1) 0;
  }

  /* A notice instead of the help sentence: the disclosure still continues it */
  .modal-body > .alert + details.context-help {
    margin-top: calc(var(--space-3) * -1);
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .modal-columns {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      column-gap: var(--space-8);
    }

    .modal-columns > .modal-section {
      padding-top: 0;
      border-top: 0;
    }
  }

  /* ------------------------------------------------------------------------
     Help text and disclosure
     <p class="context-help">One visible sentence about the consequence.</p>
     <details class="context-help"><summary>Подробнее</summary><p>…</p></details>
     ------------------------------------------------------------------------ */
  .context-help {
    color: var(--text-muted);
    font-size: var(--font-size-md);
    line-height: var(--leading-sm);
  }

  details.context-help > summary {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--hit-size);
    margin: 0 calc(var(--space-1) * -1);
    padding: 0 var(--space-1);
    border-radius: var(--radius-xs);
    color: var(--accent);
    font-weight: var(--weight-medium);
    user-select: none;
    -webkit-user-select: none;
  }

  /* The summary is padded up to a full target and overlaps the line above it: the ring is drawn just inside the
     box, clear of that line and of its own label */
  details.context-help > summary:focus-visible {
    outline-offset: -1px;
  }

  details.context-help > summary::after {
    content: "";
    flex: none;
    width: 16px;
    height: 16px;
    background: currentColor;
    -webkit-mask: var(--glyph-chevron) center / contain no-repeat;
    mask: var(--glyph-chevron) center / contain no-repeat;
  }

  details.context-help[open] > summary::after {
    transform: rotate(180deg);
  }

  details.context-help > p {
    margin-bottom: var(--space-2);
  }

  /* In a dialog body the disclosure directly continues the sentence above it */
  .modal-body > p.context-help + details.context-help {
    margin-top: calc(var(--space-4) * -1);
  }

  /* The same pair anywhere else: div.help > p.context-help + details.context-help. The text is capped at a readable
     width; the closed summary is a full-size target, so its empty half is taken back from the gap that follows */
  .help {
    display: grid;
    grid-template-columns: minmax(0, 44rem);
    justify-items: start;
  }

  .help > details.context-help:not([open]) {
    margin-bottom: calc((var(--leading-sm) - var(--hit-size)) / 2);
  }

  @media (hover: hover) {
    details.context-help > summary:hover {
      text-decoration: underline;
      text-underline-offset: 3px;
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    details.context-help > summary::after {
      transition: transform var(--duration-fast) var(--ease-standard);
    }
  }

  /* ------------------------------------------------------------------------
     Help topics: a dialog body made of short titled paragraphs
     div.modal-body.help-topics > section.help-topic > span.help-topic-icon + h3.field-label + p
     ------------------------------------------------------------------------ */
  .help-topics {
    gap: var(--space-5);
  }

  .help-topic {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: var(--space-3);
  }

  .help-topic-icon {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    background: var(--tile-file-bg);
    color: var(--text-muted);
  }

  .help-topic-icon > .icon {
    --icon-size: 18;
  }


  .help-topic > p {
    grid-column: 2;
    color: var(--text-muted);
    font-size: var(--font-size-md);
    line-height: var(--leading-sm);
  }

  /* ------------------------------------------------------------------------
     Inline alert and form error
     <div class="alert alert-info|alert-warning|alert-error|alert-success" [role="alert"]>icon + <div>text</div></div>
     <p class="form-error" role="alert">…</p>   error line of a form, directly under the field or at the top of the body
     ------------------------------------------------------------------------ */
  .alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px var(--space-3);
    border-radius: var(--radius-md);
    background: var(--surface-sunken);
    font-size: var(--font-size-md);
    line-height: var(--leading-sm);
  }

  .alert > .icon {
    color: var(--text-muted);
  }

  .alert > :last-child {
    flex: 1;
    min-width: 0;
  }

  .alert-title {
    font-weight: var(--weight-bold);
  }

  .alert-info {
    background: var(--info-soft);
  }

  .alert-info > .icon {
    color: var(--info);
  }

  .alert-success {
    background: var(--success-soft);
  }

  .alert-success > .icon {
    color: var(--success);
  }

  .alert-warning {
    background: var(--warning-soft);
  }

  .alert-warning > .icon {
    color: var(--warning);
  }

  .alert-error {
    background: var(--danger-soft);
  }

  .alert-error > .icon {
    color: var(--danger);
  }

  .form-error {
    padding: 10px var(--space-3);
    border-radius: var(--radius-md);
    background: var(--danger-soft);
    color: var(--danger);
    font-size: var(--font-size-md);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
  }

  /* ------------------------------------------------------------------------
     Toast: one line of feedback above everything, dialogs included
     <div class="toast [show] [error]" id="toast" role="status" aria-live="polite" [hidden]>[icon] text</div>
     ------------------------------------------------------------------------ */
  .toast {
    position: fixed;
    right: var(--gutter);
    bottom: calc(var(--bottom-bar-space) + var(--space-4) + var(--safe-bottom));
    left: var(--gutter);
    z-index: var(--z-toast);
    display: flex;
    align-items: center;
    gap: 10px;
    width: fit-content;
    max-width: min(480px, calc(100% - 2 * var(--gutter)));
    margin: 0 auto;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    background: var(--toast-bg);
    box-shadow: var(--shadow-md);
    color: var(--toast-text);
    font-size: var(--font-size-md);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    opacity: 0;
    pointer-events: none;
  }

  /* A toast takes no presses, shown or not: it has no control of its own, and where it lies on a
     control (the pager, the floating «+») the press goes to that control. */
  .toast.show {
    opacity: 1;
  }

  .toast.error {
    background: var(--toast-error-bg);
    color: var(--toast-error-text);
  }

  /* Phone layout: while a sheet or a dialog is open the bottom of the screen belongs to it — the toast moves to the top */
  body.modal-open .toast {
    top: calc(var(--space-3) + var(--safe-top));
    bottom: auto;
  }

  /* A dialog that fills the phone screen (.modal-full; the script keeps body.modal-full-open while one is open)
     has its header at the top and may have a bar of buttons at the bottom: the toast stands right under the
     56px header and covers neither the title nor the close button. From 600px the dialog is a centred card */
  body.modal-full-open .toast {
    top: calc(var(--safe-top) + 56px + var(--space-2));
  }

  @media (min-width: 600px) {
    body.modal-full-open .toast {
      top: calc(var(--space-3) + var(--safe-top));
    }
  }

  /* The viewer (.modal-screen; the script keeps body.modal-screen-open while it is open) is a screen in the whole
     phone layout, tablets and short windows included: the toast stands right under its bar, which keeps the
     name, «Скачать» and «⋯» free — also while a sheet (properties, the public link) is open over the viewer */
  body.modal-screen-open .toast {
    top: calc(var(--safe-top) + var(--view-bar-height) + var(--space-2));
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .toast,
    body.modal-open .toast,
    body.modal-full-open .toast,
    body.modal-screen-open .toast {
      top: auto;
      bottom: var(--space-6);
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .toast {
      transform: translateY(8px);
      transition: opacity var(--duration-base) var(--ease-standard), transform var(--duration-base) var(--ease-out);
    }

    .toast.show {
      transform: none;
    }
  }

  /* ------------------------------------------------------------------------
     Empty state. The door of the logo, drawn as a line glyph with a brass wheel.
     <div class="empty"><span class="empty-icon"><svg class="icon">#i-vault</svg></span>
       <h2 class="empty-title">…</h2><p>…</p><div class="empty-actions">…</div></div>
     The message is the first <p> (the product writes its text there); the title is a heading.
     ------------------------------------------------------------------------ */
  .empty {
    display: grid;
    justify-items: center;
    gap: var(--space-2);
    padding: var(--space-12) var(--gutter);
    color: var(--text-muted);
    text-align: center;
  }

  .empty-icon {
    display: grid;
    place-items: center;
    width: 72px;
    height: 72px;
    margin-bottom: var(--space-2);
    border-radius: var(--radius-xl);
    background: var(--tile-file-bg);
  }

  /* Two tones through inheritance: the door takes stroke, the wheel takes color */
  .empty-icon > .icon {
    --icon-size: 40;
    --icon-stroke: 2px;
    stroke: var(--text-subtle);
    color: var(--highlight);
  }

  .empty-title {
    color: var(--text);
    font-size: var(--font-size-xl);
    line-height: var(--leading-md);
    font-weight: var(--weight-bold);
  }

  .empty > p {
    max-width: 30em;
    text-wrap: balance;
  }

  /* The actions stand in a row at their own widths; when the row does not fit they stack at one width */
  .empty-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-2);
    max-width: 100%;
    margin-top: var(--space-3);
  }

  .empty-actions > .btn {
    flex: 1 1 0;
    min-width: max-content;
  }

  /* ------------------------------------------------------------------------
     Page title outside an app bar: a public page, a page that is one message
     <h1 class="page-title">Фотографии</h1>   (also on h1.empty-title when an empty state is the whole page)
     ------------------------------------------------------------------------ */
  .page-title {
    font-size: var(--font-size-4xl);
    line-height: var(--leading-lg);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    overflow-wrap: anywhere;
  }

  /* ------------------------------------------------------------------------
     Pagination
     <div class="list-pagination"><span class="page-info">…</span>
       <button class="btn btn-secondary" data-page-step="-1">Назад</button><button class="btn btn-secondary" data-page-step="1">Далее</button></div>
     ------------------------------------------------------------------------ */
  .list-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--gutter);
    border-top: 1px solid var(--border);
  }

  /* DOM order is page-info, «Назад», «Далее»; on screen the page number sits between the buttons */
  .list-pagination > [data-page-step="-1"] {
    order: -1;
  }

  .page-info {
    flex: 1;
    min-width: 0;
    color: var(--text-muted);
    font-size: var(--font-size-md);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    font-variant-numeric: tabular-nums;
    text-align: center;
  }

  @media (min-width: 600px) {
    .page-info {
      flex: none;
      min-width: 128px;
    }
  }

  /* ------------------------------------------------------------------------
     Progress bar and spinner
     <div class="bar [is-error]" role="progressbar" aria-valuenow="48"></div>   width of the fill from --p (set by script)
     <span class="spinner [spinner-sm|spinner-lg]" role="status" aria-label="Загрузка"></span>
     ------------------------------------------------------------------------ */
  .bar {
    position: relative;
    height: 4px;
    overflow: hidden;
    border-radius: var(--radius-full);
    background: var(--control-track);
  }

  .bar::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--p, 0%);
    border-radius: inherit;
    background: var(--accent);
  }

  .bar.is-error::after {
    background: var(--danger);
  }

  .spinner {
    display: inline-block;
    flex: none;
    width: 20px;
    height: 20px;
    border: 2px solid var(--control-track);
    border-top-color: var(--accent);
    border-radius: 50%;
  }

  .spinner-sm {
    width: 14px;
    height: 14px;
  }

  .spinner-lg {
    width: 32px;
    height: 32px;
    border-width: 3px;
  }

  @media (prefers-reduced-motion: no-preference) {
    .bar::after {
      transition: width var(--duration-base) var(--ease-standard);
    }

    .spinner {
      animation: spin 0.9s linear infinite;
    }
  }

  /* ------------------------------------------------------------------------
     Upload panel: the queue of uploads in progress
     div#upload-progress.upload-panel[.collapsed][.is-busy]
       div.upload-panel-header[role=button] > div.upload-panel-title (span.upload-panel-spin.spinner
              + span.upload-panel-text (span.upload-panel-label
                + span.upload-panel-eta (span.upload-panel-eta-lead «осталось» + span — the time left, while it is known)))
            + span.upload-panel-count (errors) + button.upload-panel-close + button.upload-panel-toggle
       div.upload-panel-body > div.bar + p.context-help + ul.upload-queue
            > li.upload-queue-item.is-pending|is-uploading|is-done|is-error|is-cancelled
                > span.upload-queue-ico + span.upload-queue-main (span.upload-queue-name + span.upload-queue-status)
                + button.upload-queue-action
     Phone layout: a sheet with the queue (show .sheet-backdrop with it); .collapsed — a bar beside the «+».
     Desktop: a card in the bottom right corner; .collapsed — its header with the progress line.
     ------------------------------------------------------------------------ */
  .upload-panel {
    position: fixed;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: var(--z-popover);
    display: flex;
    flex-direction: column;
    max-width: 560px;
    max-height: calc(100vh - 24px);
    max-height: calc(100dvh - 24px - var(--safe-top));
    margin: 0 auto;
    padding-bottom: calc(var(--space-2) + var(--safe-bottom));
    overflow: hidden;
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    background: var(--surface-overlay);
    box-shadow: var(--shadow-lg);
  }

  .upload-panel::before {
    content: "";
    flex: none;
    align-self: center;
    width: 36px;
    height: 4px;
    margin: var(--space-2) 0 0;
    border-radius: var(--radius-full);
    background: var(--border-strong);
    opacity: 0.5;
  }

  .upload-panel-header {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: none;
    min-height: 56px;
    padding: 0 calc(var(--gutter) - 10px) 0 var(--gutter);
    cursor: pointer;
  }

  .upload-panel-header:focus-visible {
    outline-offset: -2px;
  }

  .upload-panel-title {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: 1;
    min-width: 0;
  }

  /* The spinner stands in the glyph column of the queue */
  .upload-panel-spin {
    margin: 0 2px;
  }

  .upload-panel:not(.is-busy) .upload-panel-spin {
    display: none;
  }

  /* The label and, under it, the forecast of the time left: the header is high enough for both lines */
  .upload-panel-text {
    display: grid;
    flex: 1;
    min-width: 0;
  }

  .upload-panel-eta {
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    font-weight: var(--weight-regular);
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .upload-panel-label {
    min-width: 0;
    overflow: hidden;
    font-size: var(--font-size-2xl);
    line-height: var(--leading-md);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .upload-panel-count {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex: none;
    color: var(--danger);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
  }

  .upload-panel-count > .icon {
    --icon-size: 18;
  }

  .upload-panel-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .upload-panel-body > .bar {
    flex: none;
    margin: 0 var(--gutter);
  }

  .upload-panel-body > .context-help {
    padding: var(--space-3) var(--gutter) var(--space-2);
  }

  .upload-queue {
    flex: none;
  }

  .upload-queue-item {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 56px;
    padding: var(--space-2) calc(var(--gutter) - 10px) var(--space-2) var(--gutter);
  }

  .upload-queue-item + .upload-queue-item::before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    left: calc(var(--gutter) + 24px + var(--space-3));
    border-top: 1px solid var(--border);
    pointer-events: none;
  }

  .upload-queue-ico {
    display: grid;
    place-items: center;
    flex: none;
    width: 24px;
    height: 24px;
    color: var(--text-subtle);
  }

  .upload-queue-ico > .icon {
    --icon-size: 22;
  }

  .upload-queue-item.is-done > .upload-queue-ico {
    color: var(--success);
  }

  .upload-queue-item.is-error > .upload-queue-ico {
    color: var(--danger);
  }

  .upload-queue-main {
    display: grid;
    flex: 1;
    min-width: 0;
  }

  .upload-queue-name {
    overflow: hidden;
    font-size: var(--body-font);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .upload-queue-item.is-cancelled > .upload-queue-main > .upload-queue-name {
    color: var(--text-muted);
  }

  .upload-queue-status {
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    font-variant-numeric: tabular-nums;
  }

  /* The reason of a failure is text and may take two lines */
  .upload-queue-item.is-error .upload-queue-status {
    color: var(--danger);
    font-weight: var(--weight-medium);
  }

  /* Collapsed, phone layout: a bar on the line of the floating «+», to its left */
  .upload-panel.collapsed {
    right: calc(var(--gutter) + var(--fab-size) + var(--space-3) + var(--safe-right));
    bottom: calc(var(--bottom-bar-space) + var(--space-4) + var(--safe-bottom));
    left: auto;
    z-index: var(--z-float);
    width: calc(100% - 2 * var(--gutter) - var(--fab-size) - var(--space-3) - var(--safe-left) - var(--safe-right));
    max-width: 360px;
    height: var(--fab-size);
    margin: 0;
    padding: 0;
    border-radius: var(--radius-fab);
    background: var(--surface-raised);
    box-shadow: var(--shadow-outline), var(--shadow-md);
  }

  .upload-panel.collapsed::before {
    content: none;
  }

  .upload-panel.collapsed > .upload-panel-header {
    flex: 1;
    min-height: 0;
    padding: 0 var(--space-1) 0 var(--space-3);
  }

  .upload-panel.collapsed .upload-panel-title {
    gap: var(--space-2);
  }

  .upload-panel.collapsed .upload-panel-label {
    font-size: var(--font-size-lg);
    line-height: var(--leading-sm);
    font-weight: var(--weight-medium);
    letter-spacing: 0;
  }

  .upload-panel.collapsed .upload-panel-count > span {
    display: none;
  }

  /* The bar has room for the time alone: «меньше минуты», «3 мин» */
  .upload-panel.collapsed .upload-panel-eta-lead {
    display: none;
  }

  .upload-panel.collapsed .upload-panel-toggle > .icon {
    transform: rotate(180deg);
  }

  /* Collapsed: of the body only the progress line is left, on the bottom edge */
  .upload-panel.collapsed > .upload-panel-body {
    position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    display: block;
    overflow: visible;
  }

  .upload-panel.collapsed > .upload-panel-body > * {
    display: none;
  }

  .upload-panel.collapsed > .upload-panel-body > .bar {
    display: block;
    height: 3px;
    margin: 0;
    border-radius: 0;
  }

  /* A toast does not cover the collapsed bar (the toast is a later sibling). The open sheet sets body.modal-open,
     which sends the toast to the top of the screen */
  .upload-panel.collapsed:not([hidden]) ~ .toast {
    bottom: calc(var(--bottom-bar-space) + var(--space-4) + var(--fab-size) + var(--space-3) + var(--safe-bottom));
  }

  @media (max-height: 499px) {
    /* A short window: the sheet leaves the top strip to the toast */
    .upload-panel:not(.collapsed) {
      max-width: 480px;
      max-height: calc(100vh - 72px);
      max-height: calc(100dvh - 72px - var(--safe-top));
    }

    .upload-panel.collapsed {
      bottom: calc(var(--bottom-bar-space) + var(--space-3) + var(--safe-bottom));
      border-radius: var(--radius-lg);
    }
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .upload-panel,
    .upload-panel.collapsed {
      right: var(--space-6);
      bottom: var(--space-6);
      left: auto;
      z-index: var(--z-float);
      width: 400px;
      max-width: calc(100vw - 2 * var(--space-6));
      height: auto;
      max-height: min(480px, calc(100vh - 2 * var(--space-6)));
      margin: 0;
      padding: 0;
      border-radius: var(--radius-lg);
      background: var(--surface-raised);
      box-shadow: var(--shadow-outline), var(--shadow-md);
    }

    .upload-panel::before {
      content: none;
    }

    .upload-panel-header,
    .upload-panel.collapsed > .upload-panel-header {
      flex: none;
      min-height: 52px;
      padding: 0 var(--space-2) 0 var(--space-4);
    }

    .upload-panel-title,
    .upload-panel.collapsed .upload-panel-title {
      gap: var(--space-3);
    }

    .upload-panel-label,
    .upload-panel.collapsed .upload-panel-label {
      font-size: var(--font-size-lg);
      line-height: var(--leading-sm);
      font-weight: var(--weight-bold);
    }

    .upload-panel.collapsed .upload-panel-count > span,
    .upload-panel.collapsed .upload-panel-eta-lead {
      display: inline;
    }

    .upload-panel-body > .bar {
      margin: 0 var(--space-4);
    }

    .upload-panel-body > .context-help {
      padding: var(--space-3) var(--space-4) var(--space-1);
      font-size: var(--font-size-sm);
    }

    .upload-queue {
      padding-bottom: var(--space-2);
    }

    .upload-queue-item {
      min-height: 52px;
      padding: 6px var(--space-2) 6px var(--space-4);
    }

    .upload-queue-item + .upload-queue-item::before {
      left: calc(var(--space-4) + 24px + var(--space-3));
    }

    .upload-queue-ico > .icon {
      --icon-size: 20;
    }

    .upload-panel.collapsed > .upload-panel-body {
      position: static;
    }

    /* The card owns the bottom right corner and a toast is centred, so in a window under about 1360px they would
       overlap: while the card is on the screen, open or collapsed, the toast stands at the top. Not in the top
       bar — centred there it would cover the search field — but right under it, on the head of the list, which
       is as passive as the rows a bottom toast lies on */
    .upload-panel:not([hidden]) ~ .toast,
    .upload-panel.collapsed:not([hidden]) ~ .toast {
      top: calc(var(--topbar-height) + var(--space-2));
      bottom: auto;
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .upload-panel-toggle > .icon {
      transition: transform var(--duration-fast) var(--ease-standard);
    }
  }

  /* ------------------------------------------------------------------------
     Breadcrumb
     <nav class="breadcrumb" aria-label="Путь"><button class="crumb link" data-nav="…">Корень</button>
       <span class="crumb-sep">chevron</span><span class="crumb current">Договоры</span></nav>
     Every crumb but the last is a button. Drag-and-drop adds .drop-folder / .drop-target.
     A crumb is a --hit-size target, yet the row takes only its line of text in the layout (negative block
     margins): a path with links is exactly as high as a bare folder name. The visible pill of a crumb
     (hover, focus, drop target) is the ::before box, lower than the target.
     ------------------------------------------------------------------------ */
  .breadcrumb {
    --crumb-leading: var(--leading-sm);
    display: flex;
    align-items: center;
    min-width: 0;
    margin: calc((var(--crumb-leading) - var(--hit-size)) / 2) calc(var(--space-2) * -1);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    font-size: var(--font-size-lg);
    line-height: var(--crumb-leading);
    font-weight: var(--weight-bold);
    scrollbar-width: none;
  }

  .breadcrumb::-webkit-scrollbar {
    display: none;
  }

  /* A path longer than its box. The script scrolls it to its end and keeps .is-cut-start on it while
     scrollLeft > 0 and .is-cut-end while its end is out of view: the cut side fades out over 24px instead of
     ending in half a letter (the colours of a mask are its alpha only, not colours of the theme) */
  .breadcrumb.is-cut-start {
    -webkit-mask-image: linear-gradient(to right, transparent, black var(--space-6));
    mask-image: linear-gradient(to right, transparent, black var(--space-6));
  }

  .breadcrumb.is-cut-end {
    -webkit-mask-image: linear-gradient(to left, transparent, black var(--space-6));
    mask-image: linear-gradient(to left, transparent, black var(--space-6));
  }

  .breadcrumb.is-cut-start.is-cut-end {
    -webkit-mask-image: linear-gradient(to right, transparent, black var(--space-6), black calc(100% - var(--space-6)), transparent);
    mask-image: linear-gradient(to right, transparent, black var(--space-6), black calc(100% - var(--space-6)), transparent);
  }

  /* Crumbs never shrink: a long name is cut on its own, a long path scrolls sideways */
  .crumb {
    position: relative;
    z-index: 0;
    flex: none;
    max-width: 14em;
    padding: calc((var(--hit-size) - var(--crumb-leading)) / 2) var(--space-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .crumb::before {
    content: "";
    position: absolute;
    top: max(0px, calc((var(--hit-size) - var(--crumb-leading) - 8px) / 2));
    right: 0;
    bottom: max(0px, calc((var(--hit-size) - var(--crumb-leading) - 8px) / 2));
    left: 0;
    z-index: -1;
    border-radius: var(--radius-sm);
  }

  .crumb.link {
    min-width: var(--hit-size);
    color: var(--text-muted);
    font-weight: var(--weight-medium);
  }

  .crumb.link:focus-visible {
    outline: none;
  }

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

  .crumb.current {
    max-width: 20em;
    color: var(--text);
  }

  .crumb-sep {
    display: grid;
    place-items: center;
    flex: none;
    color: var(--text-subtle);
  }

  .crumb-sep > .icon {
    --icon-size: 16;
  }

  .crumb.drop-target {
    color: var(--accent);
  }

  .crumb.drop-target::before {
    background: var(--accent-soft);
    box-shadow: inset 0 0 0 1.5px var(--accent);
  }

  @media (hover: hover) {
    .crumb.link:hover {
      color: var(--text);
    }

    .crumb.link:hover::before {
      background: var(--state-hover);
    }
  }

  /* ------------------------------------------------------------------------
     Folder tree
     ul.folder-tree[role=tree] > li.tree-node[role=treeitem] > div.tree-row[.active][.drop-target][.dragging]
       > button.tree-toggle[.is-leaf][.expanded] + div.tree-label[role=button][tabindex=0] > span.tree-folder-icon + span
     children: ul[role=group] inside the li
     ------------------------------------------------------------------------ */
  .folder-tree ul {
    margin-left: calc(var(--hit-size) / 2 - 1px);
    border-left: 1px solid var(--border);
  }

  .tree-row {
    display: flex;
    align-items: center;
    height: var(--tree-row-height);
    border-radius: var(--radius-sm);
  }

  .tree-toggle {
    display: grid;
    place-items: center;
    flex: none;
    width: var(--hit-size);
    height: 100%;
    border-radius: var(--radius-sm);
    color: var(--text-subtle);
  }

  .tree-toggle > .icon {
    --icon-size: 16;
  }

  .tree-toggle.expanded > .icon {
    transform: rotate(90deg);
  }

  .tree-toggle.is-leaf {
    visibility: hidden;
  }

  .tree-toggle:focus-visible,
  .tree-label:focus-visible {
    outline-offset: -2px;
  }

  .tree-label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 1;
    min-width: 0;
    height: 100%;
    padding-right: var(--space-2);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-md);
    line-height: var(--leading-sm);
    cursor: pointer;
  }

  .tree-label > span:last-child {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .tree-folder-icon {
    display: grid;
    flex: none;
    color: var(--text-muted);
  }

  .tree-folder-icon > .icon {
    --icon-size: 18;
  }

  /* The open folder: besides the fill (under 1.5:1 against the column) a bar of the accent colour on its edge */
  .tree-row.active {
    background: var(--state-pressed);
    box-shadow: inset 3px 0 0 var(--accent);
    font-weight: var(--weight-bold);
  }

  .tree-row.active .tree-folder-icon {
    color: var(--accent);
  }

  .tree-row.drop-target {
    background: var(--accent-soft);
    box-shadow: inset 0 0 0 1.5px var(--accent);
  }

  .tree-row.dragging {
    opacity: 0.5;
  }

  @media (hover: hover) {
    .tree-row:hover {
      background: var(--state-hover);
    }

    .tree-row.active:hover {
      background: var(--state-pressed);
    }

    .tree-toggle:hover {
      color: var(--text);
    }
  }

  @media (min-width: 900px) and (min-height: 500px) and (hover: hover) and (pointer: fine) {
    .folder-tree ul {
      margin-left: 13px;
    }

    .tree-toggle {
      width: 28px;
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .tree-toggle > .icon {
      transition: transform var(--duration-fast) var(--ease-standard);
    }
  }

  /* ------------------------------------------------------------------------
     Section navigation: bottom bar in the phone layout, list in the desktop sidebar
     <nav class="side-tabs" role="tablist" aria-label="Разделы">
       <button class="side-tab [active]" role="tab" aria-selected="true" data-side-tab="folders">
         <span class="side-tab-icon"><svg class="icon">…</svg></span><span>Файлы</span></button>…</nav>
     ------------------------------------------------------------------------ */
  .side-tabs {
    position: fixed;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: var(--z-bar);
    display: flex;
    justify-content: center;
    height: calc(var(--bottomnav-height) + var(--safe-bottom));
    padding: 0 var(--safe-right) var(--safe-bottom) var(--safe-left);
    border-top: 1px solid var(--nav-edge);
    background: var(--nav-bg);
  }

  .side-tab {
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    min-width: 0;
    max-width: 168px;
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    line-height: var(--leading-xs);
    font-weight: var(--weight-medium);
  }

  .side-tab-icon {
    display: grid;
    place-items: center;
    width: 56px;
    height: 32px;
    border-radius: var(--radius-md);
  }

  .side-tab-icon > .icon {
    --icon-size: 24;
  }

  .side-tab.active {
    color: var(--accent);
    font-weight: var(--weight-bold);
  }

  .side-tab.active > .side-tab-icon {
    background: var(--brand);
    color: var(--nav-active-icon);
  }

  .side-tab:focus-visible {
    border-radius: var(--radius-md);
    outline-offset: -4px;
  }

  @media (max-height: 499px) {
    .side-tab {
      flex-direction: row;
      gap: var(--space-2);
    }

    .side-tab-icon {
      width: 44px;
      height: 28px;
      border-radius: var(--radius-sm);
    }

    .side-tab-icon > .icon {
      --icon-size: 20;
    }
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .side-tabs {
      position: static;
      flex-direction: column;
      justify-content: flex-start;
      gap: 2px;
      height: auto;
      padding: 0;
      border: 0;
      background: none;
    }

    .side-tab {
      flex: none;
      flex-direction: row;
      justify-content: flex-start;
      gap: var(--space-3);
      max-width: none;
      height: var(--nav-item-height);
      padding: 0 var(--space-3);
      border-radius: var(--radius-md);
      color: var(--text);
      font-size: var(--font-size-md);
      line-height: var(--leading-sm);
    }

    .side-tab-icon {
      width: auto;
      height: auto;
      border-radius: 0;
      color: var(--text-muted);
    }

    .side-tab-icon > .icon {
      --icon-size: 20;
    }

    .side-tab.active {
      background: var(--brand);
      color: var(--brand-contrast);
      font-weight: var(--weight-medium);
    }

    .side-tab.active > .side-tab-icon {
      background: none;
      color: inherit;
    }

    .side-tab:focus-visible {
      outline-offset: 2px;
    }
  }

  @media (min-width: 900px) and (min-height: 500px) and (hover: hover) {
    .side-tab:not(.active):hover {
      background: var(--state-hover);
    }
  }

  /* ------------------------------------------------------------------------
     App bar (phone layout): sticky title bar of a screen
     <header class="app-bar">[<button class="btn btn-icon">menu or back</button>]
       <h1 class="app-bar-title">Файлы</h1> <button class="btn btn-icon">…</button> <button class="app-bar-account"><span class="avatar">A</span></button></header>
     ------------------------------------------------------------------------ */
  .app-bar {
    position: sticky;
    top: 0;
    z-index: var(--z-bar);
    display: flex;
    align-items: center;
    flex: none;
    height: calc(var(--appbar-height) + var(--safe-top));
    padding: var(--safe-top) var(--appbar-pad-end) 0 var(--appbar-pad-start);
    background: var(--bg);
  }

  .app-bar .btn-icon {
    --btn-fg: var(--text);
  }

  .app-bar .btn-icon > .icon {
    --icon-size: 24;
  }

  .app-bar-title {
    flex: 1;
    min-width: 0;
    margin-left: 10px;
    overflow: hidden;
    font-size: var(--font-size-4xl);
    line-height: var(--leading-lg);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* After a leading button the title starts on the text edge of the rows below; a hidden one (a screen
     without it keeps the button in the markup with [hidden]) does not move the title */
  .app-bar > .btn-icon:not([hidden]) + .app-bar-title {
    margin-left: 18px;
  }

  /* The smallest phones: after the back arrow and before three buttons the title has about 148px — a name of
     twelve letters was cut for the sake of 6px. One step down in size and the text edge of the button */
  @media (max-width: 399px) {
    .app-bar > .btn-icon:not([hidden]) + .app-bar-title {
      margin-left: var(--space-2);
      font-size: var(--font-size-3xl);
    }
  }

  .app-bar-account {
    display: grid;
    place-items: center;
    flex: none;
    width: var(--icon-button-size);
    height: var(--icon-button-size);
    border-radius: 50%;
  }

  /* Page bar: the app bar of a service page that replaces the storage screen (administration, metrics). It stays
     on desktop — as high as the top bar of the storage screen, on the page column, with labelled buttons.
     header.app-bar.app-bar-page > a|button.btn.app-bar-back (icon + span) + h1.app-bar-title
                                   + [button.btn.app-bar-action[.btn-primary] (icon + span)]
     Phone layout: the buttons are glyphs, their names are in aria-label. */
  .app-bar-page > .btn {
    width: var(--icon-button-size);
    height: var(--icon-button-size);
    padding: 0;
  }

  .app-bar-page > .btn > span {
    display: none;
  }

  .app-bar-page > .btn > .icon {
    --icon-size: 24;
    margin: 0;
  }

  .app-bar-back,
  .app-bar-action:not(.btn-primary) {
    --btn-fg: var(--text);
  }

  /* The bar ends 4px outside the content column for the sake of glyph buttons; a filled button stays on the column */
  .app-bar-page > .btn-primary {
    margin-right: var(--space-1);
  }

  .app-bar-page > .app-bar-title {
    margin-left: 18px;
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .app-bar-page {
      height: var(--topbar-height);
      padding: 0 var(--page-pad-end) 0 var(--page-pad-start);
    }

    .app-bar-page > .btn {
      width: auto;
      height: var(--control-height);
      padding: 0 var(--space-3);
    }

    .app-bar-page > .btn > span {
      display: inline;
    }

    .app-bar-page > .btn > .icon {
      --icon-size: 18;
      margin: 0 -2px;
    }

    /* A text button whose glyph stands on the edge of the content column */
    .app-bar-page > .app-bar-back {
      --btn-fg: var(--text-muted);
      --btn-fg-hover: var(--text);
      margin-left: calc(var(--space-3) * -1);
    }

    .app-bar-page > .app-bar-action {
      padding: 0 var(--space-4);
    }

    .app-bar-page > .app-bar-action:not(.btn-primary) {
      --btn-bg: var(--control-bg);
      --btn-border: var(--border-strong);
    }

    .app-bar-page > .btn-primary {
      margin-right: 0;
    }

    .app-bar-page > .app-bar-title {
      margin-left: var(--space-2);
      padding-left: var(--space-4);
      border-left: 1px solid var(--border);
      font-size: var(--font-size-3xl);
    }
  }

  @media (max-height: 499px) {
    .app-bar-title {
      font-size: var(--font-size-3xl);
    }

    .app-bar-account > .avatar {
      width: 32px;
      height: 32px;
      font-size: var(--font-size-md);
    }
  }

  /* ------------------------------------------------------------------------
     Floating action button (phone layout): the one primary action of the screen
     <button class="fab" aria-label="Добавить"><svg class="icon">#i-plus</svg></button>
     ------------------------------------------------------------------------ */
  .fab {
    position: fixed;
    right: calc(var(--gutter) + var(--safe-right));
    bottom: calc(var(--bottom-bar-space) + var(--space-4) + var(--safe-bottom));
    z-index: var(--z-float);
    display: grid;
    place-items: center;
    width: var(--fab-size);
    height: var(--fab-size);
    border-radius: var(--radius-fab);
    background: var(--accent);
    box-shadow: var(--shadow-md);
    color: var(--accent-contrast);
  }

  .fab > .icon {
    --icon-size: 24;
  }

  .fab:active {
    background: var(--accent-pressed);
  }

  @media (max-height: 499px) {
    .fab {
      bottom: calc(var(--bottom-bar-space) + var(--space-3) + var(--safe-bottom));
      border-radius: var(--radius-lg);
    }
  }

  @media (hover: hover) {
    .fab:hover {
      background: var(--accent-hover);
    }
  }

  /* ------------------------------------------------------------------------
     Search field with a results list
     div.file-search[.is-open] > [button.file-search-back] + svg.file-search-ico + input[type=search]
                                 + [button.file-search-clear] + div.search-results[hidden]
       button.search-result[.is-nav] > span.search-result-ico[.entry-icon.type-…] + span.search-result-main(> .search-result-name + .search-result-path) + span.search-result-meta
       div.search-result.search-result-empty
     ------------------------------------------------------------------------ */
  .file-search {
    position: relative;
    display: flex;
    align-items: center;
    min-width: 0;
    height: var(--control-height);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--field-bg);
  }

  .file-search-ico {
    --icon-size: 18;
    margin-left: var(--space-3);
    color: var(--text-muted);
  }

  .file-search > input {
    flex: 1;
    min-width: 0;
    height: 100%;
    padding: 0 var(--space-3) 0 var(--space-2);
    border: 0;
    border-radius: calc(var(--radius-md) - 1px);
    background: none;
    font-size: var(--control-font);
    -webkit-appearance: none;
    appearance: none;
  }

  .file-search > input::-webkit-search-cancel-button {
    -webkit-appearance: none;
  }

  /* A placeholder longer than the field ends with an ellipsis instead of a cut letter */
  .file-search > input:placeholder-shown {
    text-overflow: ellipsis;
  }

  .file-search > input:focus-visible {
    outline-offset: -2px;
  }

  /* As in an input group: with the focus in the input the ring goes around the whole field, the glyph and the
     clear button included (the script keeps .is-focused on it). Not in the full-screen layer of the phone
     layout, where the box is the screen. */
  .file-search.is-focused:focus-within:not(.is-open) {
    border-color: var(--focus-ring);
    outline: 2px solid var(--focus-ring);
    outline-offset: -1px;
  }

  .file-search.is-focused:focus-within:not(.is-open) > input:focus-visible {
    outline: none;
  }

  .search-results {
    position: absolute;
    top: calc(100% + var(--space-1));
    right: 0;
    left: 0;
    z-index: var(--z-popover);
    max-height: min(420px, 60vh);
    padding: var(--space-1);
    overflow-y: auto;
    overscroll-behavior: contain;
    border-radius: var(--radius-lg);
    background: var(--surface-overlay);
    box-shadow: var(--shadow-outline), var(--shadow-lg);
  }

  .search-result {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    min-height: var(--control-height);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    text-align: left;
  }

  .search-result-ico {
    display: grid;
    flex: none;
    color: var(--text-muted);
  }

  .search-result-main {
    display: grid;
    flex: 1;
    min-width: 0;
  }

  .search-result-name {
    overflow: hidden;
    font-weight: var(--weight-medium);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .search-result-path,
  .search-result-meta {
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .search-result-meta {
    flex: none;
    font-variant-numeric: tabular-nums;
  }

  .search-result:focus-visible {
    outline-offset: -2px;
  }

  .search-result-empty {
    color: var(--text-muted);
  }

  /* On desktop the list may be wider than the field, so that names stay readable */
  @media (min-width: 900px) and (min-height: 500px) {
    .search-results {
      right: auto;
      width: max-content;
      min-width: 100%;
      max-width: 440px;
    }
  }

  @media (hover: hover) {
    .file-search:hover {
      border-color: var(--text-muted);
    }

    button.search-result:hover {
      background: var(--state-hover);
    }

    /* Highlighted by the arrow keys. A touch device has none, so the mark is not shown there */
    .search-result.is-nav {
      background: var(--state-pressed);
    }
  }

  /* Open state, back and clear buttons, note.
     div.file-search[.is-open] > button.file-search-back + svg.file-search-ico + input + button.file-search-clear
                                 + div.search-results (> … + p.search-results-note)
     Phone layout: .is-open turns the field into a full-screen layer — a bar in place of the app bar (back, query,
     clear) and the results as list rows on the sheet. Desktop: the field with its dropdown. */
  .file-search-back {
    display: none;
  }

  .file-search > .file-search-clear {
    width: calc(var(--control-height) - 8px);
    height: calc(var(--control-height) - 8px);
    margin-right: 3px;
    border-radius: 7px;
  }

  .file-search > .file-search-clear > .icon {
    --icon-size: 18;
  }

  /* What the search looks at — the last line of the list */
  .search-results-note {
    margin-top: var(--space-1);
    padding: var(--space-3) var(--space-3) var(--space-2);
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--leading-sm);
  }

  @media (max-width: 899px), (max-height: 499px) {
    .file-search.is-open {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      z-index: var(--z-popover);
      display: grid;
      grid-template-columns: auto minmax(0, 1fr) auto;
      grid-template-rows: var(--appbar-height) minmax(0, 1fr);
      align-items: center;
      height: auto;
      padding-top: var(--safe-top);
      border: 0;
      border-radius: 0;
      background: var(--bg);
    }

    .file-search.is-open > .file-search-back {
      --btn-fg: var(--text);
      display: inline-flex;
      margin-left: calc(var(--gutter) - 10px + var(--safe-left));
    }

    .file-search.is-open > .file-search-back > .icon {
      --icon-size: 24;
    }

    .file-search.is-open > .file-search-ico {
      display: none;
    }

    /* The query starts on the text edge of the rows below, like an app bar title */
    .file-search.is-open > input {
      grid-column: 2;
      height: var(--control-height-sm);
      padding: 0 var(--space-2) 0 18px;
      font-size: var(--font-size-2xl);
      font-weight: var(--weight-medium);
    }

    .file-search.is-open > .file-search-clear {
      width: var(--icon-button-size);
      height: var(--icon-button-size);
      margin-right: calc(var(--gutter) - 12px + var(--safe-right));
      border-radius: var(--radius-md);
    }

    .file-search.is-open > .file-search-clear > .icon {
      --icon-size: 20;
    }

    .file-search.is-open > .search-results {
      position: static;
      grid-column: 1 / -1;
      align-self: stretch;
      max-height: none;
      padding: var(--space-2) var(--safe-right) calc(var(--space-4) + var(--safe-bottom)) var(--safe-left);
      border-radius: var(--radius-xl) var(--radius-xl) 0 0;
      background: var(--surface);
      box-shadow: var(--shadow-outline);
    }

    /* Results are rows of the list */
    .file-search.is-open .search-result {
      position: relative;
      min-height: var(--row-height);
      padding: var(--space-2) var(--gutter);
      border-radius: 0;
    }

    .file-search.is-open .search-result + .search-result::before {
      content: "";
      position: absolute;
      top: 0;
      right: 0;
      left: calc(var(--gutter) + var(--tile-size) + var(--space-3));
      border-top: 1px solid var(--border);
      pointer-events: none;
    }

    .file-search.is-open .search-result-name {
      font-size: var(--body-font);
      line-height: var(--leading-sm);
    }

    .file-search.is-open .search-results-note {
      margin-top: 0;
      padding: var(--space-4) var(--gutter);
    }
  }

  /* ------------------------------------------------------------------------
     View toggle: ONE button that switches list and tiles (aria-pressed="true" means tiles)
     <button class="view-toggle" aria-pressed="false" aria-label="Вид: плитки">
       <span class="view-ico-list"><svg class="icon">#i-list</svg></span><span class="view-ico-tiles"><svg class="icon">#i-grid</svg></span></button>
     Phone layout: one glyph, the view a tap switches to. Desktop: two segments, the current one raised.
     ------------------------------------------------------------------------ */
  .view-toggle {
    display: inline-flex;
    align-items: center;
    flex: none;
    height: var(--icon-button-size);
    border-radius: var(--radius-md);
    color: var(--text);
  }

  .view-toggle > span {
    display: grid;
    place-items: center;
    width: var(--icon-button-size);
    height: 100%;
    border-radius: inherit;
  }

  .view-toggle[aria-pressed="false"] > .view-ico-list,
  .view-toggle[aria-pressed="true"] > .view-ico-tiles {
    display: none;
  }

  .view-toggle:active > span {
    background: var(--state-pressed);
  }

  @media (min-width: 900px) and (min-height: 500px) {
    .view-toggle {
      height: var(--control-height);
      padding: 3px;
      background: var(--control-track);
      box-shadow: inset 0 0 0 1px var(--border);
      color: var(--text-muted);
    }

    .view-toggle[aria-pressed] > .view-ico-list,
    .view-toggle[aria-pressed] > .view-ico-tiles {
      display: grid;
      width: calc(var(--control-height) - 4px);
      border-radius: 7px;
    }

    .view-toggle > span > .icon {
      --icon-size: 18;
    }

    .view-toggle[aria-pressed="false"] > .view-ico-list,
    .view-toggle[aria-pressed="true"] > .view-ico-tiles {
      background: var(--control-thumb);
      box-shadow: inset 0 0 0 1.5px var(--border-strong), var(--shadow-sm);
      color: var(--text);
    }
  }

  @media (hover: hover) {
    .view-toggle:hover {
      color: var(--text);
    }
  }
}
