@charset "UTF-8";

/* The @charset above must stay the first thing in this file - a comment or a
   blank line before it makes the browser ignore it. It is the stylesheet's own
   declaration of encoding, for hosts that serve .css without a charset (see
   web.config / .htaccess, which say the same thing at the server level). With
   neither, the browser guesses, and a guess of Latin-1 turns every non-ASCII
   character here into mojibake until the page is reloaded. */

:root {
  /* Theme independent tokens. Anything that keeps the same value in both
     themes lives here once; the theme blocks below only carry what flips. */

  /* Chrome geometry. The navbar height and the icon rail width are read by the
     grid, the sidebar, the backdrop and every element anchored under the bar,
     so they are declared once instead of being repeated as literals. */
  --pg-navbar-h: 50px;
  --pg-rail-w: 60px;
  /* Wide enough for the longest label the panel ships. In Turkish that is
     "Tüm Nakliye Yöntemleri" at 214px of text plus 27px of icon and padding;
     English is shorter. A label longer than this ellipsises rather than
     widening the rail -- see the width declaration in the lg block below for
     why the rail must never size itself to its contents. */
  --pg-rail-w-expanded: 250px;
  /* Radius of the pocket the content sits in, where the rail meets the bar. */
  --pg-corner: 16px;

  /* Minimum tappable size. #header declares its own below and overrides this;
     what this base is for is the controls that read the token from outside the
     header -- the section rail's toggle is one, and without a value here its
     min-height is dropped and the button collapses to the height of its text. */
  --pg-tap: 40px;

  --bs-body-bg: rgb(var(--bs-body-bg-rgb));
  --bs-card-bg: var(--pg-card-bg);
  --dt-row-selected-text: unset;
  --pg-logo-fallback-color: var(--pg-logo-color-1);

  /* Ink printed on every filled accent. The pastel fills below sit between
     0.30 and 0.70 relative luminance, where white text tops out near 2.9:1;
     near black clears 5.5:1 on all six and on their hover and active tones,
     so one ink serves the whole palette in both themes. */
  --pg-on-accent: #1a1c1e;

  /* Bootstrap accent bridge. Bootstrap derives .bg-*, .border-*, .text-bg-*,
     .alert-*, .list-group-item-* and the -subtle/-emphasis pairs from these
     five variables per colour, so binding them once re-tints every one of
     those. Buttons, form controls, pagination, nav-pills, progress and the
     range thumb are NOT token driven in Bootstrap - it writes literal hex for
     them - which is why they are re-declared further down. */
  --bs-primary: var(--pg-primary);
  --bs-primary-rgb: var(--pg-primary-rgb);
  --bs-primary-text-emphasis: var(--pg-primary-ink);
  --bs-primary-bg-subtle: var(--pg-primary-subtle);
  --bs-primary-border-subtle: var(--pg-primary-line);

  --bs-secondary: var(--pg-secondary);
  --bs-secondary-rgb: var(--pg-secondary-rgb);
  --bs-secondary-text-emphasis: var(--pg-secondary-ink);
  --bs-secondary-bg-subtle: var(--pg-secondary-subtle);
  --bs-secondary-border-subtle: var(--pg-secondary-line);

  --bs-success: var(--pg-success);
  --bs-success-rgb: var(--pg-success-rgb);
  --bs-success-text-emphasis: var(--pg-success-ink);
  --bs-success-bg-subtle: var(--pg-success-subtle);
  --bs-success-border-subtle: var(--pg-success-line);

  --bs-danger: var(--pg-danger);
  --bs-danger-rgb: var(--pg-danger-rgb);
  --bs-danger-text-emphasis: var(--pg-danger-ink);
  --bs-danger-bg-subtle: var(--pg-danger-subtle);
  --bs-danger-border-subtle: var(--pg-danger-line);

  --bs-warning: var(--pg-warning);
  --bs-warning-rgb: var(--pg-warning-rgb);
  --bs-warning-text-emphasis: var(--pg-warning-ink);
  --bs-warning-bg-subtle: var(--pg-warning-subtle);
  --bs-warning-border-subtle: var(--pg-warning-line);

  --bs-info: var(--pg-info);
  --bs-info-rgb: var(--pg-info-rgb);
  --bs-info-text-emphasis: var(--pg-info-ink);
  --bs-info-bg-subtle: var(--pg-info-subtle);
  --bs-info-border-subtle: var(--pg-info-line);

  /* Links and validation take the readable ink, never the pastel fill. */
  --bs-link-color: var(--pg-primary-ink);
  --bs-link-color-rgb: var(--pg-primary-ink-rgb);
  --bs-link-hover-color: var(--pg-primary-ink-hover);
  --bs-link-hover-color-rgb: var(--pg-primary-ink-hover-rgb);
  --bs-form-valid-color: var(--pg-success-ink);
  --bs-form-valid-border-color: var(--pg-success);
  --bs-form-invalid-color: var(--pg-danger-ink);
  --bs-form-invalid-border-color: var(--pg-danger);
  /* A pastel ring at Bootstrap's 0.25 alpha is close to invisible on white. */
  --bs-focus-ring-color: rgba(var(--pg-primary-rgb), 0.35);
  
}

:root,
[data-bs-theme="light"] {
  /*Pinegrap Color Scheme*/
  /* The dashboard is the one menu item that never had a colour. It asked for
     .dashboard-color and nothing answered, so its icon came out in the body
     text colour while every other item carried its own hue -- the top item in
     the list was the only grey one. Emerald: the gauge on that page is the same
     green when the installation is healthy, so the item and the thing it opens
     agree, and it is the one live hue the menu was not already using. Darkened
     for the light theme, like the other three that had to be. */
  --dashboard-color: #00875a;
  --dashboard-color-highlight: #34d399;
  --folders-color: #ffa000;
  --folders-color-highlight: #ffc107;
  --pages-color: #1976d2;
  --pages-color-highlight: #448aff;
  --files-color: #00796b;
  --files-color-highlight: #35d1c2;
  --calendars-color: #5d4037;
  --calendars-color-highlight: #b17e6b;
  /* Ads, forms and design are toned down against white for WCAG contrast and
     less eye strain; hue preserved. */
  --forms-color: #8e1f55;
  --forms-color-highlight: #dd61a1;
  --statistics-color: #0097a7;
  --statistics-color-highlight: #00bcd4;
  --contacts-color: #303f9f;
  --contacts-color-highlight: #7485e6;
  --users-color: #7b1fa2;
  --users-color-highlight: #b84dff;
  --campaigns-color: #e64a19;
  --campaigns-color-highlight: #ff8e6c;
  --ecommerce-color: #467919;
  --ecommerce-color-highlight: #b7ee78;
  --erp-color: #8a6d1f;
  --erp-color-highlight: #e6c04f;
  --workspace-color: #5e35b1;
  --workspace-color-highlight: #b39ddb;
  --ads-color: #b71c1c;
  --ads-color-highlight: #ff4a3d;
  --design-color: #a01551;
  --design-color-highlight: #ff4c87;
  --settings-color: #3f3f3f;
  --settings-color-highlight: #c5c5c5;

  /* ── Material pastel accent palette ──────────────────────────────────
     Six Material hues at tone 300 for fills, 400 and 500 for hover and
     active, 800 for text and 50/100 for the subtle surface pair. Amber has
     no Material stop dark enough to carry text on white (900 is #FF6F00,
     2.3:1), so its ink is a darkened amber rather than a palette tone. */
  --pg-primary: #64b5f6;
  --pg-primary-rgb: 100, 181, 246;
  --pg-primary-hover: #42a5f5;
  --pg-primary-active: #2196f3;
  --pg-primary-ink: #1565c0;
  --pg-primary-ink-rgb: 21, 101, 192;
  --pg-primary-ink-hover: #0d47a1;
  --pg-primary-ink-hover-rgb: 13, 71, 161;
  --pg-primary-subtle: #e3f2fd;
  --pg-primary-line: #bbdefb;

  --pg-secondary: #90a4ae;
  --pg-secondary-rgb: 144, 164, 174;
  --pg-secondary-hover: #78909c;
  --pg-secondary-active: #607d8b;
  --pg-secondary-ink: #455a64;
  --pg-secondary-ink-rgb: 69, 90, 100;
  --pg-secondary-subtle: #eceff1;
  --pg-secondary-line: #cfd8dc;

  --pg-success: #81c784;
  --pg-success-rgb: 129, 199, 132;
  --pg-success-hover: #66bb6a;
  --pg-success-active: #4caf50;
  --pg-success-ink: #2e7d32;
  --pg-success-ink-rgb: 46, 125, 50;
  --pg-success-subtle: #e8f5e9;
  --pg-success-line: #c8e6c9;

  --pg-danger: #e57373;
  --pg-danger-rgb: 229, 115, 115;
  --pg-danger-hover: #ef5350;
  --pg-danger-active: #f44336;
  --pg-danger-ink: #c62828;
  --pg-danger-ink-rgb: 198, 40, 40;
  --pg-danger-subtle: #ffebee;
  --pg-danger-line: #ffcdd2;

  --pg-warning: #ffd54f;
  --pg-warning-rgb: 255, 213, 79;
  --pg-warning-hover: #ffca28;
  --pg-warning-active: #ffc107;
  --pg-warning-ink: #8a6d00;
  --pg-warning-ink-rgb: 138, 109, 0;
  --pg-warning-subtle: #fff8e1;
  --pg-warning-line: #ffecb3;

  --pg-info: #4dd0e1;
  --pg-info-rgb: 77, 208, 225;
  --pg-info-hover: #26c6da;
  --pg-info-active: #00bcd4;
  --pg-info-ink: #006064;
  --pg-info-ink-rgb: 0, 96, 100;
  --pg-info-subtle: #e0f7fa;
  --pg-info-line: #b2ebf2;

  /* Access level colours used by the folder tree and the chart tables. */
  --pg-access-public: var(--bs-secondary-color);
  --pg-access-private: #ac2929;
  --pg-access-guest: #c66e28;
  --pg-access-registration: #2976ac;
  --pg-access-membership: #148340;

  /* Neutral surface tints shared by the dashboard widgets. They carry the only
     values that differ between themes, so the widget rules below need no
     theme specific duplicates. */
  --pg-tint: rgba(128, 128, 128, 0.08);
  --pg-tint-strong: rgba(128, 128, 128, 0.16);
  --pg-tint-hover: rgba(128, 128, 128, 0.18);
  --pg-tint-soft: rgba(128, 128, 128, 0.06);
  --pg-rule: rgba(128, 128, 128, 0.16);
  --pg-rule-strong: rgba(128, 128, 128, 0.18);
  --pg-rule-dashed: rgba(128, 128, 128, 0.35);
  --pg-scrollbar-thumb: #939393;

  --bs-body-bg-rgb: 255, 255, 255;
  --dt-row-selected: 0 102 255 / 24%;
  --bs-navbar-bg-rgb: 248, 248, 248;
  --clock-fallback-color: #0891b2;
  --clock-gradient: linear-gradient(100deg, #0891b2 0%, #6366f1 50%, #db2777 100%);
  /* The bloom is the neon; on white it stays small, because a wide one reads as
     a blurred glyph rather than a lit one. */
  --clock-glow: rgba(219, 39, 119, 0.28);
  --clock-bloom: 7px;
  /* Logo gradient stops. Single source of truth: the CSS text gradient and the
     inline SVG logo in the navbar both read these two colors. */
  --pg-logo-color-1: rgb(23 73 177);
  --pg-logo-color-2: rgb(207 105 0);
  --pg-card-bg: rgb(245, 245, 245);
}

[data-bs-theme="dark"] {
  /* Fills stay at tone 300 - a pastel chip reads as well on a dark surface as
     on a light one, and keeps one ink for both themes. Only the text tone and
     the subtle surface pair flip: tone 200 for ink, a deep tint of the hue
     for the surface. */
  --pg-primary-ink: #90caf9;
  --pg-primary-ink-rgb: 144, 202, 249;
  --pg-primary-ink-hover: #bbdefb;
  --pg-primary-ink-hover-rgb: 187, 222, 251;
  --pg-primary-subtle: #10243a;
  --pg-primary-line: #1e4976;

  --pg-secondary-ink: #cfd8dc;
  --pg-secondary-ink-rgb: 207, 216, 220;
  --pg-secondary-subtle: #1b2124;
  --pg-secondary-line: #3c4a52;

  --pg-success-ink: #a5d6a7;
  --pg-success-ink-rgb: 165, 214, 167;
  --pg-success-subtle: #122419;
  --pg-success-line: #2c5b33;

  --pg-danger-ink: #ef9a9a;
  --pg-danger-ink-rgb: 239, 154, 154;
  --pg-danger-subtle: #2a1414;
  --pg-danger-line: #6b2b2b;

  --pg-warning-ink: #ffe082;
  --pg-warning-ink-rgb: 255, 224, 130;
  --pg-warning-subtle: #2a2210;
  --pg-warning-line: #6b5518;

  --pg-info-ink: #80deea;
  --pg-info-ink-rgb: 128, 222, 234;
  --pg-info-subtle: #0d2428;
  --pg-info-line: #1c5a63;

  --pg-access-public: #c1c1c1;
  --pg-access-private: #ff4c4c;
  --pg-access-registration: #36b0ff;
  --pg-access-membership: #30f880;

  --pg-tint: rgba(255, 255, 255, 0.05);
  --pg-tint-strong: rgba(255, 255, 255, 0.1);
  --pg-tint-hover: rgba(255, 255, 255, 0.1);
  --pg-tint-soft: rgba(255, 255, 255, 0.04);
  --pg-scrollbar-thumb: #5e5e5e;

  --bs-body-bg-rgb: 16, 17, 20;
  --dt-row-selected: 41 62 141 / 26%;
  --bs-navbar-bg-rgb: 14, 14, 14;
  --clock-fallback-color: #22d3ee;
  --clock-gradient: linear-gradient(100deg, #22d3ee 0%, #818cf8 50%, #f472b6 100%);
  --clock-glow: rgba(244, 114, 182, 0.5);
  --clock-bloom: 14px;
  --pg-logo-color-1: rgb(142 179 255);
  --pg-logo-color-2: rgb(255 188 119);
  --pg-card-bg: rgb(12, 13, 15);
}

.card {
  --bs-card-border-radius: var(--bs-border-radius-lg);
  --bs-card-border-width: 1px;
  --bs-card-cap-padding-y: 1rem;
  --bs-card-cap-padding-x: 1rem;
  --bs-card-inner-border-radius: calc(
    var(--bs-border-radius-lg) - (var(--bs-border-width))
  );
  --bs-card-bg: var(--pg-card-bg);
}

.table {
  --bs-table-bg: var(--bs-card-bg);
}
.card .table {
  margin-bottom: 1.5rem !important;
}

.table > :not(caption) > * > * {
  background-color: var(--bs-card-bg);
}

/* ══ Material pastel accents - component bindings ═════════════════════════
   Bootstrap keeps its accent colours in two different places. Utilities and
   the -subtle/-emphasis components read the --bs-<colour>-* variables, so the
   bridge in :root already re-tints them. Buttons, form controls, pagination,
   nav-pills, dropdowns, progress bars and the range thumb are written as
   literal hex inside Bootstrap's own rules, so each of those needs an explicit
   re-declaration here. Everything below reads the --pg-* palette; no accent
   hex is repeated in this section.

   Filled controls carry --pg-on-accent (near black) rather than white: white
   on tone 300 sits near 2.5:1, near black clears 5.5:1 on every fill and on
   its hover and active tone. */

.btn-primary {
  --bs-btn-color: var(--pg-on-accent);
  --bs-btn-bg: var(--pg-primary);
  --bs-btn-border-color: var(--pg-primary);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-primary-hover);
  --bs-btn-hover-border-color: var(--pg-primary-hover);
  --bs-btn-focus-shadow-rgb: var(--pg-primary-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-primary-active);
  --bs-btn-active-border-color: var(--pg-primary-active);
  --bs-btn-disabled-color: var(--pg-on-accent);
  --bs-btn-disabled-bg: var(--pg-primary);
  --bs-btn-disabled-border-color: var(--pg-primary);
}

.btn-success {
  --bs-btn-color: var(--pg-on-accent);
  --bs-btn-bg: var(--pg-success);
  --bs-btn-border-color: var(--pg-success);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-success-hover);
  --bs-btn-hover-border-color: var(--pg-success-hover);
  --bs-btn-focus-shadow-rgb: var(--pg-success-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-success-active);
  --bs-btn-active-border-color: var(--pg-success-active);
  --bs-btn-disabled-color: var(--pg-on-accent);
  --bs-btn-disabled-bg: var(--pg-success);
  --bs-btn-disabled-border-color: var(--pg-success);
}

.btn-danger {
  --bs-btn-color: var(--pg-on-accent);
  --bs-btn-bg: var(--pg-danger);
  --bs-btn-border-color: var(--pg-danger);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-danger-hover);
  --bs-btn-hover-border-color: var(--pg-danger-hover);
  --bs-btn-focus-shadow-rgb: var(--pg-danger-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-danger-active);
  --bs-btn-active-border-color: var(--pg-danger-active);
  --bs-btn-disabled-color: var(--pg-on-accent);
  --bs-btn-disabled-bg: var(--pg-danger);
  --bs-btn-disabled-border-color: var(--pg-danger);
}

.btn-warning {
  --bs-btn-color: var(--pg-on-accent);
  --bs-btn-bg: var(--pg-warning);
  --bs-btn-border-color: var(--pg-warning);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-warning-hover);
  --bs-btn-hover-border-color: var(--pg-warning-hover);
  --bs-btn-focus-shadow-rgb: var(--pg-warning-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-warning-active);
  --bs-btn-active-border-color: var(--pg-warning-active);
  --bs-btn-disabled-color: var(--pg-on-accent);
  --bs-btn-disabled-bg: var(--pg-warning);
  --bs-btn-disabled-border-color: var(--pg-warning);
}

.btn-info {
  --bs-btn-color: var(--pg-on-accent);
  --bs-btn-bg: var(--pg-info);
  --bs-btn-border-color: var(--pg-info);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-info-hover);
  --bs-btn-hover-border-color: var(--pg-info-hover);
  --bs-btn-focus-shadow-rgb: var(--pg-info-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-info-active);
  --bs-btn-active-border-color: var(--pg-info-active);
  --bs-btn-disabled-color: var(--pg-on-accent);
  --bs-btn-disabled-bg: var(--pg-info);
  --bs-btn-disabled-border-color: var(--pg-info);
}

/* Ghost secondary. Transparent with a neutral hairline at rest, so it never
   competes with the primary action; the primary tint only appears on hover
   and press. The border is kept rather than dropped entirely because most of
   the 760 call sites are the Cancel button beside a Save, where a shape the
   pointer can aim at matters more than the lighter look. */
.btn-secondary {
  --bs-btn-color: var(--bs-body-color);
  --bs-btn-bg: var(--bs-body-bg);
  --bs-btn-border-color: var(--bs-border-color);
  --bs-btn-hover-color: var(--pg-primary-ink);
  --bs-btn-hover-bg: rgba(var(--pg-primary-rgb), 0.1);
  --bs-btn-hover-border-color: var(--pg-primary);
  --bs-btn-focus-shadow-rgb: var(--pg-primary-rgb);
  --bs-btn-active-color: var(--pg-primary-ink);
  --bs-btn-active-bg: rgba(var(--pg-primary-rgb), 0.18);
  --bs-btn-active-border-color: var(--pg-primary);
  --bs-btn-active-shadow: none;
  --bs-btn-disabled-color: var(--bs-secondary-color);
  --bs-btn-disabled-bg: var(--bs-body-bg);
  --bs-btn-disabled-border-color: var(--bs-border-color);
}

/* Outline variants take the readable ink for their resting label - the pastel
   fill is 2.2:1 on white and cannot carry text - and the fill itself only on
   hover, where the ink flips to --pg-on-accent. */
.btn-outline-primary {
  --bs-btn-color: var(--pg-primary-ink);
  --bs-btn-border-color: var(--pg-primary);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-primary);
  --bs-btn-hover-border-color: var(--pg-primary);
  --bs-btn-focus-shadow-rgb: var(--pg-primary-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-primary-hover);
  --bs-btn-active-border-color: var(--pg-primary-hover);
  --bs-btn-disabled-color: var(--pg-primary-ink);
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: var(--pg-primary);
}

.btn-outline-secondary {
  --bs-btn-color: var(--pg-secondary-ink);
  --bs-btn-border-color: var(--pg-secondary);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-bg: var(--bs-body-bg);
  --bs-btn-hover-bg: var(--pg-secondary);
  --bs-btn-hover-border-color: var(--pg-secondary);
  --bs-btn-focus-shadow-rgb: var(--pg-secondary-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-secondary-hover);
  --bs-btn-active-border-color: var(--pg-secondary-hover);
  --bs-btn-disabled-color: var(--pg-secondary-ink);
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: var(--pg-secondary);
}

.btn-outline-success {
  --bs-btn-color: var(--pg-success-ink);
  --bs-btn-border-color: var(--pg-success);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-success);
  --bs-btn-hover-border-color: var(--pg-success);
  --bs-btn-focus-shadow-rgb: var(--pg-success-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-success-hover);
  --bs-btn-active-border-color: var(--pg-success-hover);
  --bs-btn-disabled-color: var(--pg-success-ink);
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: var(--pg-success);
}

.btn-outline-danger {
  --bs-btn-color: var(--pg-danger-ink);
  --bs-btn-border-color: var(--pg-danger);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-danger);
  --bs-btn-hover-border-color: var(--pg-danger);
  --bs-btn-focus-shadow-rgb: var(--pg-danger-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-danger-hover);
  --bs-btn-active-border-color: var(--pg-danger-hover);
  --bs-btn-disabled-color: var(--pg-danger-ink);
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: var(--pg-danger);
}

.btn-outline-warning {
  --bs-btn-color: var(--pg-warning-ink);
  --bs-btn-border-color: var(--pg-warning);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-warning);
  --bs-btn-hover-border-color: var(--pg-warning);
  --bs-btn-focus-shadow-rgb: var(--pg-warning-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-warning-hover);
  --bs-btn-active-border-color: var(--pg-warning-hover);
  --bs-btn-disabled-color: var(--pg-warning-ink);
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: var(--pg-warning);
}

.btn-outline-info {
  --bs-btn-color: var(--pg-info-ink);
  --bs-btn-border-color: var(--pg-info);
  --bs-btn-hover-color: var(--pg-on-accent);
  --bs-btn-hover-bg: var(--pg-info);
  --bs-btn-hover-border-color: var(--pg-info);
  --bs-btn-focus-shadow-rgb: var(--pg-info-rgb);
  --bs-btn-active-color: var(--pg-on-accent);
  --bs-btn-active-bg: var(--pg-info-hover);
  --bs-btn-active-border-color: var(--pg-info-hover);
  --bs-btn-disabled-color: var(--pg-info-ink);
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: var(--pg-info);
}

/* Text and link utilities take the ink tone, not the fill. Bootstrap points
   .text-primary at --bs-primary-rgb, which is now a pastel that reads at
   2.2:1 on the page background; the ink tone clears 4.5:1 in both themes.
   The opacity variables are kept so .text-opacity-* and .link-opacity-* keep
   working. .text-secondary is included because Bootstrap 5.3 maps it to
   --bs-secondary-rgb, not to the neutral --bs-secondary-color. */
.text-primary {
  color: rgba(var(--pg-primary-ink-rgb), var(--bs-text-opacity, 1)) !important;
}
.text-secondary {
  color: rgba(var(--pg-secondary-ink-rgb), var(--bs-text-opacity, 1)) !important;
}
.text-success {
  color: rgba(var(--pg-success-ink-rgb), var(--bs-text-opacity, 1)) !important;
}
.text-danger {
  color: rgba(var(--pg-danger-ink-rgb), var(--bs-text-opacity, 1)) !important;
}
.text-warning {
  color: rgba(var(--pg-warning-ink-rgb), var(--bs-text-opacity, 1)) !important;
}
.text-info {
  color: rgba(var(--pg-info-ink-rgb), var(--bs-text-opacity, 1)) !important;
}

.link-primary {
  --pg-link-ink-rgb: var(--pg-primary-ink-rgb);
}
.link-secondary {
  --pg-link-ink-rgb: var(--pg-secondary-ink-rgb);
}
.link-success {
  --pg-link-ink-rgb: var(--pg-success-ink-rgb);
}
.link-danger {
  --pg-link-ink-rgb: var(--pg-danger-ink-rgb);
}
.link-warning {
  --pg-link-ink-rgb: var(--pg-warning-ink-rgb);
}
.link-info {
  --pg-link-ink-rgb: var(--pg-info-ink-rgb);
}
.link-primary,
.link-secondary,
.link-success,
.link-danger,
.link-warning,
.link-info {
  color: rgba(var(--pg-link-ink-rgb), var(--bs-link-opacity, 1)) !important;
  -webkit-text-decoration-color: rgba(
    var(--pg-link-ink-rgb),
    var(--bs-link-underline-opacity, 1)
  ) !important;
  text-decoration-color: rgba(
    var(--pg-link-ink-rgb),
    var(--bs-link-underline-opacity, 1)
  ) !important;
}

/* Bootstrap's .bg-* utilities deliberately set no text colour, which worked
   while the accents were dark enough for the inherited body colour to be
   wrong only in one theme. Every accent is now a light pastel in BOTH themes,
   so the inherited colour is right in light and unreadable in dark (1.7:1).
   The fills therefore carry the ink themselves. Backgrounds thinned with
   .bg-opacity-* are excluded - those sit close to the page colour and must
   keep inheriting it. A .text-white beside a pastel fill is overruled: it
   dates from the saturated palette and would read at 2.2:1 now. */
.bg-primary:not([class*="bg-opacity"]),
.bg-secondary:not([class*="bg-opacity"]),
.bg-success:not([class*="bg-opacity"]),
.bg-danger:not([class*="bg-opacity"]),
.bg-warning:not([class*="bg-opacity"]),
.bg-info:not([class*="bg-opacity"]) {
  color: var(--pg-on-accent);
}
.bg-primary:not([class*="bg-opacity"]).text-white,
.bg-secondary:not([class*="bg-opacity"]).text-white,
.bg-success:not([class*="bg-opacity"]).text-white,
.bg-danger:not([class*="bg-opacity"]).text-white,
.bg-warning:not([class*="bg-opacity"]).text-white,
.bg-info:not([class*="bg-opacity"]).text-white,
.bg-primary:not([class*="bg-opacity"]) .text-white,
.bg-secondary:not([class*="bg-opacity"]) .text-white,
.bg-success:not([class*="bg-opacity"]) .text-white,
.bg-danger:not([class*="bg-opacity"]) .text-white,
.bg-warning:not([class*="bg-opacity"]) .text-white,
.bg-info:not([class*="bg-opacity"]) .text-white {
  color: var(--pg-on-accent) !important;
}

/* A badge is a filled accent, so it takes the accent ink. Neutral badges
   (.bg-dark, .bg-light, .bg-body-*) are left on Bootstrap's own colour. */
.badge.bg-primary,
.badge.bg-secondary,
.badge.bg-success,
.badge.bg-danger,
.badge.bg-warning,
.badge.bg-info {
  --bs-badge-color: var(--pg-on-accent);
}
.text-bg-primary,
.text-bg-secondary,
.text-bg-success,
.text-bg-danger,
.text-bg-warning,
.text-bg-info {
  color: var(--pg-on-accent) !important;
}

/* Table variants. Bootstrap writes the striped, hover and active steps as
   opaque hex derived from the old accent, so they are re-derived from the
   accent's own alpha instead. */
.table-primary {
  --bs-table-color: var(--pg-primary-ink);
  --bs-table-bg: var(--pg-primary-subtle);
  --bs-table-border-color: var(--pg-primary-line);
  --bs-table-striped-color: var(--pg-primary-ink);
  --bs-table-striped-bg: rgba(var(--pg-primary-rgb), 0.14);
  --bs-table-active-color: var(--pg-primary-ink);
  --bs-table-active-bg: rgba(var(--pg-primary-rgb), 0.22);
  --bs-table-hover-color: var(--pg-primary-ink);
  --bs-table-hover-bg: rgba(var(--pg-primary-rgb), 0.18);
}
.table-secondary {
  --bs-table-color: var(--pg-secondary-ink);
  --bs-table-bg: var(--pg-secondary-subtle);
  --bs-table-border-color: var(--pg-secondary-line);
  --bs-table-striped-color: var(--pg-secondary-ink);
  --bs-table-striped-bg: rgba(var(--pg-secondary-rgb), 0.14);
  --bs-table-active-color: var(--pg-secondary-ink);
  --bs-table-active-bg: rgba(var(--pg-secondary-rgb), 0.22);
  --bs-table-hover-color: var(--pg-secondary-ink);
  --bs-table-hover-bg: rgba(var(--pg-secondary-rgb), 0.18);
}
.table-success {
  --bs-table-color: var(--pg-success-ink);
  --bs-table-bg: var(--pg-success-subtle);
  --bs-table-border-color: var(--pg-success-line);
  --bs-table-striped-color: var(--pg-success-ink);
  --bs-table-striped-bg: rgba(var(--pg-success-rgb), 0.14);
  --bs-table-active-color: var(--pg-success-ink);
  --bs-table-active-bg: rgba(var(--pg-success-rgb), 0.22);
  --bs-table-hover-color: var(--pg-success-ink);
  --bs-table-hover-bg: rgba(var(--pg-success-rgb), 0.18);
}
.table-danger {
  --bs-table-color: var(--pg-danger-ink);
  --bs-table-bg: var(--pg-danger-subtle);
  --bs-table-border-color: var(--pg-danger-line);
  --bs-table-striped-color: var(--pg-danger-ink);
  --bs-table-striped-bg: rgba(var(--pg-danger-rgb), 0.14);
  --bs-table-active-color: var(--pg-danger-ink);
  --bs-table-active-bg: rgba(var(--pg-danger-rgb), 0.22);
  --bs-table-hover-color: var(--pg-danger-ink);
  --bs-table-hover-bg: rgba(var(--pg-danger-rgb), 0.18);
}
.table-warning {
  --bs-table-color: var(--pg-warning-ink);
  --bs-table-bg: var(--pg-warning-subtle);
  --bs-table-border-color: var(--pg-warning-line);
  --bs-table-striped-color: var(--pg-warning-ink);
  --bs-table-striped-bg: rgba(var(--pg-warning-rgb), 0.14);
  --bs-table-active-color: var(--pg-warning-ink);
  --bs-table-active-bg: rgba(var(--pg-warning-rgb), 0.22);
  --bs-table-hover-color: var(--pg-warning-ink);
  --bs-table-hover-bg: rgba(var(--pg-warning-rgb), 0.18);
}
.table-info {
  --bs-table-color: var(--pg-info-ink);
  --bs-table-bg: var(--pg-info-subtle);
  --bs-table-border-color: var(--pg-info-line);
  --bs-table-striped-color: var(--pg-info-ink);
  --bs-table-striped-bg: rgba(var(--pg-info-rgb), 0.14);
  --bs-table-active-color: var(--pg-info-ink);
  --bs-table-active-bg: rgba(var(--pg-info-rgb), 0.22);
  --bs-table-hover-color: var(--pg-info-ink);
  --bs-table-hover-bg: rgba(var(--pg-info-rgb), 0.18);
}

/* Navigation and progress. All four of these keep literal hex in Bootstrap. */
.nav-pills {
  --bs-nav-pills-link-active-color: var(--pg-on-accent);
  --bs-nav-pills-link-active-bg: var(--pg-primary);
}
.pagination {
  --bs-pagination-active-color: var(--pg-on-accent);
  --bs-pagination-active-bg: var(--pg-primary);
  --bs-pagination-active-border-color: var(--pg-primary);
  --bs-pagination-focus-box-shadow: 0 0 0 var(--bs-focus-ring-width)
    var(--bs-focus-ring-color);
}
.dropdown-menu {
  --bs-dropdown-link-active-color: var(--pg-on-accent);
  --bs-dropdown-link-active-bg: var(--pg-primary);
}
.list-group {
  --bs-list-group-active-color: var(--pg-on-accent);
  --bs-list-group-active-bg: var(--pg-primary);
  --bs-list-group-active-border-color: var(--pg-primary);
}
.progress,
.progress-stacked {
  --bs-progress-bar-color: var(--pg-on-accent);
  --bs-progress-bar-bg: var(--pg-primary);
}
.btn-close {
  --bs-btn-close-focus-shadow: 0 0 0 var(--bs-focus-ring-width)
    var(--bs-focus-ring-color);
}
/* The chevron is a stroked SVG, so the colour has to be inlined; it is the
   only place in this section where an accent value is written literally. */
.accordion {
  --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%231565c0' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}
html[data-bs-theme="dark"] .accordion {
  --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2390caf9' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

/* Form controls. Bootstrap hard codes #86b7fe for the focus border and
   rgba(13,110,253,.25) for the ring on every control. */
.form-control:focus,
.form-select:focus,
.form-check-input:focus,
.tagin-wrapper.focus {
  border-color: var(--pg-primary);
  box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--bs-focus-ring-color);
}
.form-check-input:checked,
.form-check-input[type="checkbox"]:indeterminate {
  background-color: var(--pg-primary);
  border-color: var(--pg-primary);
}
/* The tick, the dot and the switch knob ship white; on a tone 300 fill that is
   2.2:1, under the 3:1 a graphical control needs, so all three take the ink. */
.form-check-input:checked[type="checkbox"],
.form-check-input[type="checkbox"]:indeterminate {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%231a1c1e' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
.form-check-input[type="checkbox"]:indeterminate {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%231a1c1e' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
}
.form-check-input:checked[type="radio"] {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%231a1c1e'/%3e%3c/svg%3e");
}
.form-switch .form-check-input:focus {
  --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2364b5f6'/%3e%3c/svg%3e");
}
.form-switch .form-check-input:checked {
  --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%231a1c1e'/%3e%3c/svg%3e");
}
.form-range::-webkit-slider-thumb {
  background-color: var(--pg-primary);
}
.form-range::-moz-range-thumb {
  background-color: var(--pg-primary);
}
.form-range::-webkit-slider-thumb:active {
  background-color: var(--pg-primary-line);
}
.form-range::-moz-range-thumb:active {
  background-color: var(--pg-primary-line);
}

/***********/
/**COLORS**/
/*********/
body .bg-software-navbar {
  background-color: rgba(
    var(--bs-navbar-bg-rgb),
    var(--bs-bg-opacity)
  ) !important;
  --bs-bg-opacity: 1;
}
body .bg-software-navbar .border-end {
  --bs-border-color: #b3b5b7;
}
html[data-bs-theme="dark"] body .bg-software-navbar .border-end {
  --bs-border-color: #495057;
}

/*DASHBOARD*/
body .dashboard-color {
  color: var(--dashboard-color) !important;
}
html[data-bs-theme="dark"] body .dashboard-color {
  color: var(--dashboard-color-highlight) !important;
}
/*SETTINGS*/
body .settings-color {
  color: var(--settings-color) !important;
}
html[data-bs-theme="dark"] body .settings-color {
  color: var(--settings-color-highlight) !important;
}
/*FOLDERS*/
body .folders-color {
  color: var(--folders-color) !important;
}
html[data-bs-theme="dark"] body .folders-color {
  color: var(--folders-color-highlight) !important;
}
/*PAGES*/
body .pages-color {
  color: var(--pages-color) !important;
}
html[data-bs-theme="dark"] body .pages-color {
  color: var(--pages-color-highlight) !important;
}
/*FILES*/
body .files-color {
  color: var(--files-color) !important;
}
html[data-bs-theme="dark"] body .files-color {
  color: var(--files-color-highlight) !important;
}
/*CONTACTS*/
body .contacts-color {
  color: var(--contacts-color) !important;
}
html[data-bs-theme="dark"] body .contacts-color {
  color: var(--contacts-color-highlight) !important;
}
/*FORMS*/
body .forms-color {
  color: var(--forms-color) !important;
}
html[data-bs-theme="dark"] body .forms-color {
  color: var(--forms-color-highlight) !important;
}
/*CALENDARS*/
body .calendars-color {
  color: var(--calendars-color) !important;
}
html[data-bs-theme="dark"] body .calendars-color {
  color: var(--calendars-color-highlight) !important;
}
/*CAMPAIGNS*/
body .campaigns-color {
  color: var(--campaigns-color) !important;
}
html[data-bs-theme="dark"] body .campaigns-color {
  color: var(--campaigns-color-highlight) !important;
}
/*ECOMMERCE*/
body .ecommerce-color {
  color: var(--ecommerce-color) !important;
}
html[data-bs-theme="dark"] body .ecommerce-color {
  color: var(--ecommerce-color-highlight) !important;
}
/*ERP*/
body .erp-color {
  color: var(--erp-color) !important;
}
html[data-bs-theme="dark"] body .erp-color {
  color: var(--erp-color-highlight) !important;
}
/*USERS*/
body .users-color {
  color: var(--users-color) !important;
}
html[data-bs-theme="dark"] body .users-color {
  color: var(--users-color-highlight) !important;
}
/*STATISTICS*/
body .statistics-color {
  color: var(--statistics-color) !important;
}
html[data-bs-theme="dark"] body .statistics-color {
  color: var(--statistics-color-highlight) !important;
}
/*DESIGN*/
body .design-color {
  color: var(--design-color) !important;
}
html[data-bs-theme="dark"] body .design-color {
  color: var(--design-color-highlight) !important;
}

/* The Visual Page Editor's menu icon is a neon sign: the frame glows in the
   module colour, the cursor and spark in cyan. On hover the sign buzzes on,
   the cursor darts in and clicks the block, the block lights up where it is
   placed and the spark flares. */
.pg-neon-icon {
  flex: 0 0 auto;
  overflow: visible;
  vertical-align: -0.3em;
}
.pg-neon-icon .pg-neon-accent {
  color: #0891b2;
}
html[data-bs-theme="dark"] .pg-neon-icon .pg-neon-accent {
  color: #22d3ee;
}
/* The cursor turns on its tip, the block and the spark on their centres. */
.pg-neon-icon .pg-neon-cursor {
  transform-origin: 14px 11.6px;
}
.pg-neon-icon .pg-neon-block,
.pg-neon-icon .pg-neon-spark {
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
@media (prefers-reduced-motion: no-preference) {
  #menu .list-group-item:is(:hover, :focus-visible) .pg-neon-icon {
    animation: pg-neon-flicker 0.45s linear 1;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-neon-icon .pg-neon-cursor {
    animation: pg-neon-click 0.9s ease-in-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-neon-icon .pg-neon-block {
    animation: pg-neon-place 0.9s ease-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-neon-icon .pg-neon-spark {
    animation: pg-neon-flare 0.9s ease-out;
  }
}
@keyframes pg-neon-flicker {
  0%, 30%, 52%, 100% { opacity: 1; }
  38%, 46% { opacity: 0.45; }
}
@keyframes pg-neon-click {
  35% { transform: translate(-3px, -0.6px); }
  48% { transform: translate(-3px, -0.6px) scale(0.78); }
  60% { transform: translate(-3px, -0.6px) scale(1.05); }
  72% { transform: translate(-3px, -0.6px); }
}
@keyframes pg-neon-place {
  0%, 46% { transform: none; fill-opacity: 0; }
  58% { transform: scale(1.35); fill-opacity: 0.7; }
  74% { transform: scale(0.94); fill-opacity: 0.45; }
  100% { transform: none; fill-opacity: 0; }
}
@keyframes pg-neon-flare {
  0%, 50% { transform: none; }
  70% { transform: rotate(90deg) scale(1.8); }
  100% { transform: rotate(180deg); }
}

/* ── Main menu icons ───────────────────────────────────────────────────────────
   Two-colour line icons, drawn by pg_menu_icon() (includes/fn/output.php).

   First colour: the module's. Its colour class (.pages-color and the rest)
   sets `color` on the <svg>, which draws the outline, and the module's other
   shade tints the body (.pg-mi-2). The themes swap the two shades: light
   draws the dark base colour over the pale highlight, dark the highlight
   over the deep base colour.

   Second colour: one hue per icon (--pg-mi-a), picked to stand apart from
   the module colour, for the detail the icon is about -- the clock's hands,
   the tick, the sun, the per cent sign. .pg-mi-a sets it as `color`, so
   everything inside draws with it; .pg-mi-at tints with it.

   Hovering a row plays one movement per icon, under a second, with a spring
   at the end. All of it sits in a prefers-reduced-motion: no-preference
   block, so a device asking for less motion gets none. Moving parts are
   placed on the icon's own 24-unit grid (the SVG default transform-box,
   view-box): an origin such as "12px 20px" is a point on that grid. */
#menu .pg-mi {
  flex: 0 0 auto;
  overflow: visible;
  vertical-align: -0.3em;
  --pg-mi-2-opacity: 0.55;
  --pg-mi-at-opacity: 0.35;
  --pg-mi-spring: cubic-bezier(0.34, 1.8, 0.64, 1);
  --pg-mi-amber: #d97706;
  --pg-mi-orange: #ea580c;
  --pg-mi-rose: #e11d48;
  --pg-mi-pink: #db2777;
  --pg-mi-sky: #0284c7;
  --pg-mi-teal: #0d9488;
  --pg-mi-lime: #65a30d;
  --pg-mi-violet: #7c3aed;
  --pg-mi-yellow: #ca8a04;
}
html[data-bs-theme="dark"] #menu .pg-mi {
  --pg-mi-2-opacity: 0.7;
  --pg-mi-at-opacity: 0.4;
  --pg-mi-amber: #fbbf24;
  --pg-mi-orange: #fb923c;
  --pg-mi-rose: #fb7185;
  --pg-mi-pink: #f472b6;
  --pg-mi-sky: #38bdf8;
  --pg-mi-teal: #2dd4bf;
  --pg-mi-lime: #a3e635;
  --pg-mi-violet: #a78bfa;
  --pg-mi-yellow: #facc15;
}

/* The module's second shade. */
#menu .pg-mi.dashboard-color { --pg-mi-2: var(--dashboard-color-highlight); --pg-mi-2-dark: var(--dashboard-color); }
#menu .pg-mi.workspace-color { --pg-mi-2: var(--workspace-color-highlight); --pg-mi-2-dark: var(--workspace-color); }
#menu .pg-mi.folders-color { --pg-mi-2: var(--folders-color-highlight); --pg-mi-2-dark: var(--folders-color); }
#menu .pg-mi.pages-color { --pg-mi-2: var(--pages-color-highlight); --pg-mi-2-dark: var(--pages-color); }
#menu .pg-mi.files-color { --pg-mi-2: var(--files-color-highlight); --pg-mi-2-dark: var(--files-color); }
#menu .pg-mi.calendars-color { --pg-mi-2: var(--calendars-color-highlight); --pg-mi-2-dark: var(--calendars-color); }
#menu .pg-mi.forms-color { --pg-mi-2: var(--forms-color-highlight); --pg-mi-2-dark: var(--forms-color); }
#menu .pg-mi.statistics-color { --pg-mi-2: var(--statistics-color-highlight); --pg-mi-2-dark: var(--statistics-color); }
#menu .pg-mi.contacts-color { --pg-mi-2: var(--contacts-color-highlight); --pg-mi-2-dark: var(--contacts-color); }
#menu .pg-mi.users-color { --pg-mi-2: var(--users-color-highlight); --pg-mi-2-dark: var(--users-color); }
#menu .pg-mi.campaigns-color { --pg-mi-2: var(--campaigns-color-highlight); --pg-mi-2-dark: var(--campaigns-color); }
#menu .pg-mi.erp-color { --pg-mi-2: var(--erp-color-highlight); --pg-mi-2-dark: var(--erp-color); }
#menu .pg-mi.ecommerce-color { --pg-mi-2: var(--ecommerce-color-highlight); --pg-mi-2-dark: var(--ecommerce-color); }
#menu .pg-mi.ads-color { --pg-mi-2: var(--ads-color-highlight); --pg-mi-2-dark: var(--ads-color); }
#menu .pg-mi.design-color { --pg-mi-2: var(--design-color-highlight); --pg-mi-2-dark: var(--design-color); }

/* The second colour of each icon. */
#menu .pg-mi-dashboard { --pg-mi-a: var(--pg-mi-orange); }
#menu .pg-mi-workspace { --pg-mi-a: var(--pg-mi-teal); }
#menu .pg-mi-notes { --pg-mi-a: var(--pg-mi-teal); }
#menu .pg-mi-folders { --pg-mi-a: var(--pg-mi-sky); }
#menu .pg-mi-pages { --pg-mi-a: var(--pg-mi-orange); }
#menu .pg-mi-files { --pg-mi-a: var(--pg-mi-amber); }
#menu .pg-mi-calendars { --pg-mi-a: var(--pg-mi-rose); }
#menu .pg-mi-forms { --pg-mi-a: var(--pg-mi-lime); }
#menu .pg-mi-statistics { --pg-mi-a: var(--pg-mi-pink); }
#menu .pg-mi-contacts { --pg-mi-a: var(--pg-mi-amber); }
#menu .pg-mi-users { --pg-mi-a: var(--pg-mi-sky); }
#menu .pg-mi-campaigns { --pg-mi-a: var(--pg-mi-sky); }
#menu .pg-mi-erp { --pg-mi-a: var(--pg-mi-teal); }
#menu .pg-mi-orders { --pg-mi-a: var(--pg-mi-orange); }
#menu .pg-mi-products { --pg-mi-a: var(--pg-mi-amber); }
#menu .pg-mi-catalog { --pg-mi-a: var(--pg-mi-sky); }
#menu .pg-mi-offers { --pg-mi-a: var(--pg-mi-rose); }
#menu .pg-mi-shipping { --pg-mi-a: var(--pg-mi-amber); }
#menu .pg-mi-ads { --pg-mi-a: var(--pg-mi-yellow); }
/* Page Styles: the paint on the palette is every colour but its own. */
#menu .pg-mi-styles .pg-mi-c1 { color: var(--pg-mi-sky); }
#menu .pg-mi-styles .pg-mi-c2 { color: var(--pg-mi-amber); }
#menu .pg-mi-styles .pg-mi-c3 { color: var(--pg-mi-lime); }
#menu .pg-mi-styles .pg-mi-c4 { color: var(--pg-mi-violet); }

#menu .pg-mi .pg-mi-2 {
  fill: var(--pg-mi-2, currentColor);
  fill-opacity: var(--pg-mi-2-opacity);
}
html[data-bs-theme="dark"] #menu .pg-mi .pg-mi-2 {
  fill: var(--pg-mi-2-dark, currentColor);
}
#menu .pg-mi .pg-mi-a {
  color: var(--pg-mi-a, currentColor);
}
#menu .pg-mi .pg-mi-at {
  fill: currentColor;
  fill-opacity: var(--pg-mi-at-opacity);
}
#menu .pg-mi .pg-mi-solid {
  fill-opacity: 0.92;
}
#menu .pg-mi .pg-mi-date {
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}
#menu .pg-mi .pg-mi-check,
#menu .pg-mi .pg-mi-trend {
  stroke-dasharray: 1;
  stroke-dashoffset: 0;
}
#menu .pg-mi .pg-mi-speed {
  opacity: 0;
}
#menu .pg-mi :is(.pg-mi-bar, .pg-mi-sun, .pg-mi-tip, .pg-mi-spark, .pg-mi-date,
  .pg-mi-keys circle, .pg-mi-dots circle, .pg-mi-lights circle) {
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
#menu .pg-mi .pg-mi-bar {
  transform-origin: 50% 100%;
}

@media (prefers-reduced-motion: no-preference) {
  /* Parts that stay moved while the row is hovered spring into place and
     back. */
  #menu .pg-mi :is(.pg-mi-flap, .pg-mi-sheet, .pg-mi-back, .pg-mi-front, .pg-mi-sun,
    .pg-mi-rings, .pg-mi-lid, .pg-mi-l1, .pg-mi-l2, .pg-mi-l3, .pg-mi-truck) {
    transition: transform 0.45s var(--pg-mi-spring);
  }
  #menu .pg-mi .pg-mi-speed {
    transition: opacity 0.2s ease;
  }

  /* Dashboard: the dial bounces and the minute hand sweeps twice round,
     landing where it was, so the clock still tells the time. */
  #menu .pg-mi-dashboard .pg-mi-face { transform-origin: 12px 12px; }
  #menu .pg-mi-dashboard .pg-mi-hand-m { transform-origin: 12px 12px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-dashboard .pg-mi-face {
    animation: pg-mi-bounce 0.6s ease-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-dashboard .pg-mi-hand-m {
    animation: pg-mi-spin2 0.9s cubic-bezier(0.65, 0, 0.35, 1);
  }

  /* Workspace: the bubble hops off its tail, then the tick is drawn. */
  #menu .pg-mi-workspace .pg-mi-bubble { transform-origin: 6.5px 19.8px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-workspace .pg-mi-bubble {
    animation: pg-mi-hop 0.7s ease-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-workspace .pg-mi-check {
    animation: pg-mi-draw 0.45s 0.2s ease-out backwards;
  }

  /* File Manager: the front drops open and the sheet inside comes up. */
  #menu .pg-mi-folders .pg-mi-flap { transform-origin: 12px 20px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-folders .pg-mi-flap {
    transform: skewX(-14deg) scaleY(0.8);
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-folders .pg-mi-sheet {
    transform: translateY(-3px);
  }

  /* Pages: the lights blink in turn and the content scrolls. */
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-pages .pg-mi-lights circle {
    animation: pg-mi-blink 0.5s ease-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-pages .pg-mi-lights circle + circle {
    animation-delay: 0.12s;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-pages .pg-mi-content {
    animation: pg-mi-scroll 0.75s ease-in-out;
  }

  /* Files: the two photos fan apart and the sun comes up. */
  #menu .pg-mi-files .pg-mi-back { transform-origin: 14px 9px; }
  #menu .pg-mi-files .pg-mi-front { transform-origin: 3px 20px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-files .pg-mi-back {
    transform: translate(1.2px, -1.2px) rotate(7deg);
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-files .pg-mi-front {
    transform: rotate(-5deg);
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-files .pg-mi-sun {
    transform: translateY(-1.8px) scale(1.25);
  }

  /* Calendars: the page swings on its rings and the date pops. */
  #menu .pg-mi-calendars .pg-mi-page { transform-origin: 12px 3px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-calendars .pg-mi-page {
    animation: pg-mi-hang 0.8s ease-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-calendars .pg-mi-rings {
    transform: translateY(-1.2px);
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-calendars .pg-mi-date {
    animation: pg-mi-pop 0.5s 0.15s ease-out;
  }

  /* Forms: the sheet wiggles and the tick is drawn again. */
  #menu .pg-mi-forms .pg-mi-sheet { transform-origin: 12px 21px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-forms .pg-mi-sheet {
    animation: pg-mi-wiggle 0.6s ease-in-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-forms .pg-mi-check {
    animation: pg-mi-draw 0.45s 0.15s ease-out backwards;
  }

  /* Visitors: the bars shoot up one after the other, then the trend line is
     drawn to its tip. */
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-statistics .pg-mi-bar {
    animation: pg-mi-grow 0.55s ease-out backwards;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-statistics .pg-mi-bar:nth-of-type(2) { animation-delay: 0.08s; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-statistics .pg-mi-bar:nth-of-type(3) { animation-delay: 0.16s; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-statistics .pg-mi-trend {
    animation: pg-mi-draw 0.5s 0.15s ease-out backwards;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-statistics .pg-mi-tip {
    animation: pg-mi-pop 0.4s 0.55s ease-out backwards;
  }

  /* Contacts: the card flips over. */
  #menu .pg-mi-contacts .pg-mi-card { transform-origin: 12px 12px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-contacts .pg-mi-card {
    animation: pg-mi-flip 0.6s ease-in-out;
  }

  /* Users: the one behind steps up, the one in front bobs. */
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-users .pg-mi-back {
    transform: translate(1.2px, -2.4px);
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-users .pg-mi-front {
    animation: pg-mi-bob 0.55s 0.1s ease-out;
  }

  /* Campaigns: the megaphone kicks back and the sound rolls out twice. */
  #menu .pg-mi-campaigns .pg-mi-horn { transform-origin: 4px 12px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-campaigns .pg-mi-horn {
    animation: pg-mi-jolt 0.6s ease-in-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-campaigns .pg-mi-waves path {
    animation: pg-mi-emit 0.5s ease-out 2;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-campaigns .pg-mi-waves path + path {
    animation-delay: 0.12s;
  }

  /* ERP: a wave of key presses runs across the calculator and the screen
     lights up with the answer. */
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-erp .pg-mi-keys circle {
    animation: pg-mi-press 0.35s ease-in-out backwards;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-erp .pg-mi-keys circle:is(:nth-child(2), :nth-child(4)) { animation-delay: 0.06s; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-erp .pg-mi-keys circle:is(:nth-child(3), :nth-child(5), :nth-child(7)) { animation-delay: 0.12s; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-erp .pg-mi-keys circle:is(:nth-child(6), :nth-child(8)) { animation-delay: 0.18s; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-erp .pg-mi-keys circle:nth-child(9) { animation-delay: 0.24s; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-erp .pg-mi-screen .pg-mi-at {
    animation: pg-mi-glow 0.5s 0.35s ease-out;
  }

  /* Orders: the bag is picked up by its handle and swings. */
  #menu .pg-mi-orders .pg-mi-bag { transform-origin: 12px 4px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-orders .pg-mi-bag {
    animation: pg-mi-carry 0.8s ease-out;
  }

  /* Products: the lid swings open on its left corner. */
  #menu .pg-mi-products .pg-mi-lid { transform-origin: 4px 7.5px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-products .pg-mi-lid {
    transform: translateY(-2.2px) rotate(-14deg);
  }

  /* Catalog Manager: the layers fan apart. */
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-catalog .pg-mi-l1 { transform: translateY(-3px); }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-catalog .pg-mi-l2 { transform: translateY(-1.4px); }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-catalog .pg-mi-l3 { transform: translateY(0.6px); }

  /* Offers: the tag swings on its hole. */
  #menu .pg-mi-offers .pg-mi-tag { transform-origin: 7.6px 7.6px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-offers .pg-mi-tag {
    animation: pg-mi-swing 0.85s ease-out;
  }

  /* Shipping: the truck pulls forward with speed lines streaming behind. */
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-shipping .pg-mi-truck {
    transform: translateX(2.2px);
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-shipping .pg-mi-speed {
    opacity: 0.85;
    animation: pg-mi-stream 0.35s linear 3;
  }

  /* Ads: the board shakes and its spark flares. */
  #menu .pg-mi-ads .pg-mi-board { transform-origin: 12px 15.5px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-ads .pg-mi-board {
    animation: pg-mi-wiggle 0.5s ease-in-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-ads .pg-mi-spark {
    animation: pg-mi-flare 0.7s ease-out;
  }

  /* Page Styles: the palette tips and the paint pops dot by dot. */
  #menu .pg-mi-styles .pg-mi-palette { transform-origin: 12px 12px; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-styles .pg-mi-palette {
    animation: pg-mi-tip 0.7s ease-out;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-styles .pg-mi-dots circle {
    animation: pg-mi-pop 0.4s ease-out backwards;
  }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-styles .pg-mi-dots circle:nth-child(2) { animation-delay: 0.08s; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-styles .pg-mi-dots circle:nth-child(3) { animation-delay: 0.16s; }
  #menu .list-group-item:is(:hover, :focus-visible) .pg-mi-styles .pg-mi-dots circle:nth-child(4) { animation-delay: 0.24s; }
}

@keyframes pg-mi-spin2 { to { transform: rotate(720deg); } }
@keyframes pg-mi-bounce { 35% { transform: scale(1.12); } 65% { transform: scale(0.95); } 85% { transform: scale(1.03); } }
@keyframes pg-mi-hop { 30% { transform: translateY(-2.6px) rotate(-9deg) scale(1.08); } 60% { transform: translateY(0) rotate(5deg); } 80% { transform: rotate(-2deg); } }
@keyframes pg-mi-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes pg-mi-blink { 40% { opacity: 0.2; transform: scale(1.5); } }
@keyframes pg-mi-scroll { 35% { transform: translateY(-2.4px); } 70% { transform: translateY(0.7px); } }
@keyframes pg-mi-hang { 25% { transform: rotate(-10deg); } 50% { transform: rotate(7deg); } 72% { transform: rotate(-3deg); } 88% { transform: rotate(1deg); } }
@keyframes pg-mi-pop { 45% { transform: scale(1.45); } 75% { transform: scale(0.92); } }
@keyframes pg-mi-wiggle { 20% { transform: rotate(-7deg); } 45% { transform: rotate(6deg); } 70% { transform: rotate(-3deg); } 88% { transform: rotate(1deg); } }
@keyframes pg-mi-grow { from { transform: scaleY(0.1); } 60% { transform: scaleY(1.18); } 80% { transform: scaleY(0.94); } }
@keyframes pg-mi-flip { 45% { transform: scaleX(0.08); } 70% { transform: scaleX(1.06); } }
@keyframes pg-mi-bob { 40% { transform: translateY(-1.6px); } 70% { transform: translateY(0.4px); } }
@keyframes pg-mi-jolt { 20% { transform: rotate(-15deg) scale(1.06); } 45% { transform: rotate(9deg); } 70% { transform: rotate(-4deg); } 88% { transform: rotate(1.5deg); } }
@keyframes pg-mi-emit { 50% { opacity: 0; transform: translateX(2px); } 51% { opacity: 0; transform: translateX(-1.5px); } }
@keyframes pg-mi-press { 50% { transform: scale(0.35); } }
@keyframes pg-mi-glow { 40% { fill-opacity: 0.95; } }
@keyframes pg-mi-carry { 25% { transform: translateY(-2.6px) rotate(-11deg); } 50% { transform: translateY(-2.6px) rotate(8deg); } 72% { transform: translateY(-1.2px) rotate(-4deg); } 90% { transform: rotate(1.5deg); } }
@keyframes pg-mi-swing { 18% { transform: rotate(-22deg); } 42% { transform: rotate(14deg); } 64% { transform: rotate(-7deg); } 84% { transform: rotate(3deg); } }
@keyframes pg-mi-stream { from { transform: translateX(1.2px); } to { transform: translateX(-1.2px); } }
@keyframes pg-mi-flare { 45% { transform: rotate(90deg) scale(1.7); } 75% { transform: rotate(160deg) scale(0.9); } to { transform: rotate(180deg); } }
@keyframes pg-mi-tip { 30% { transform: rotate(-16deg); } 60% { transform: rotate(8deg); } 82% { transform: rotate(-3deg); } }
/*ADS*/
body .ads-color {
  color: var(--ads-color) !important;
}
html[data-bs-theme="dark"] body .ads-color {
  color: var(--ads-color-highlight) !important;
}

.status_enabled {
  color: var(--bs-success-text-emphasis);
}

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

/* Access level colours. The value flips with the theme through the tokens in
   the theme blocks, so there is one rule per level instead of one per level
   per theme. */
.public,
.public * {
  color: var(--pg-access-public);
}

.private,
.private * {
  color: var(--pg-access-private);
}

.guest,
.guest * {
  color: var(--pg-access-guest);
}

.registration,
.registration * {
  color: var(--pg-access-registration);
}

.membership,
.membership * {
  color: var(--pg-access-membership);
}

.archived {
  font-style: italic;
}

.design {
  color: var(--bs-tertiary-color);
}

#product_group_tree .row .material-icons {
  color: var(--bs-tertiary-color);
}

#product_group_tree .row .icon .material-icons {
  color: var(--bs-secondary-color);
}

.card.uploaded,
tr.uploaded td {
  background-color: rgba(var(--bs-success-rgb), 30%) !important;
}

/*Ends Of Colors*/
/*Layouts*/
/* The content track is minmax(0, 1fr), not 1fr. A bare 1fr keeps an automatic minimum
   of min-content, so one wide child - a data table, a long unbroken string - grows the
   track past the viewport. Everything sized in percentages inside it (Bootstrap
   columns, w-100) then resolves against that inflated width and the whole page stops
   fitting the screen. */
.software-container {
  display: grid;
  grid-template-columns: var(--pg-rail-w) minmax(0, 1fr);
  grid-template-rows: var(--pg-navbar-h) 1fr;
  gap: 0px 0px;
  grid-template-areas:
    "navbar navbar"
    "sidebar content";
  transition: grid-template-columns 0.3s ease;
}
.software-sidebar {
  grid-area: sidebar;
}
.software-navbar {
  grid-area: navbar;
  height: var(--pg-navbar-h);
}
/* The separator sits on #header, not on .software-navbar: the nav fills its
   parent and paints an opaque background over it, which swallowed the rule
   when it lived on the wrapper. Inset rather than a real border, because the
   grid reserves exactly --pg-navbar-h and a border would add a pixel to it. */
#header {
  box-shadow: inset 0 -1px 0 var(--bs-border-color);
}
.software-content {
  grid-area: content;
  /* No max-width. The grid track this sits in is minmax(0, 1fr) beside a fixed
     rail column, so it already resolves to exactly the space left over.
     `calc(100vw - 65px)` used to stand here and was wrong twice: 100vw counts
     the vertical scrollbar and the grid does not, so the two disagreed by the
     scrollbar's width on any screen tall enough to scroll; and 65px was the
     60px rail plus five, a hand-tuned number with nothing keeping it in step
     with --pg-rail-w. The expanded state carried the same construction with
     different magic numbers -- see the lg block below for how that one showed
     itself. */
  /* Grid items default to min-width:auto, which is the other half of the blowout
     described above - the track can be told to shrink, the item must be allowed to. */
  min-width: 0;
}

.dt-container {
  --bs-gutter-x: 0;
}
body:not(.toolbar) .software-content {
  padding: 0rem;
}

.software-sidebar {
  top: calc(var(--pg-navbar-h) - 1px);
  height: calc(100vh - var(--pg-navbar-h) + 1px);
  transition: width 0.3s ease;
  z-index: 1030;
}

/* The bar and the rail read as one continuous surface - the sidebar sits a pixel
   high on purpose and its opaque background already swallows the bar's hairline
   over the rail - so the only corner on screen is the pocket the content sits in.
   ::after carries that corner: it reaches one --pg-corner past the rail to hide
   the rest of the straight hairline, fills the notch in the chrome colour and
   draws the arc from a hard radial stop, which is the one way to get a concave
   hairline out of a single box. ::before picks the straight run up below it. */
.software-sidebar::before,
.software-sidebar::after {
  content: "";
  position: absolute;
  pointer-events: none;
}
.software-sidebar::before {
  top: var(--pg-corner);
  right: 0;
  bottom: 0;
  width: var(--bs-border-width);
  background: var(--bs-border-color);
}
.software-sidebar::after {
  top: 0;
  left: calc(100% - var(--bs-border-width));
  width: var(--pg-corner);
  height: var(--pg-corner);
  background: radial-gradient(
    circle at 100% 100%,
    transparent calc(var(--pg-corner) - var(--bs-border-width)),
    var(--bs-border-color) calc(var(--pg-corner) - var(--bs-border-width)),
    var(--bs-border-color) var(--pg-corner),
    rgb(var(--bs-navbar-bg-rgb)) var(--pg-corner)
  );
}
.software-container:not(.expanded) .software-sidebar {
  width: var(--pg-rail-w);
}

.breadcrumb {
  margin-top: 30px;
}
#menu {
  height: inherit;
  display: flex;
  flex-direction: column;
  transition: box-shadow 0.3s ease;
}
#menu .menu-body {
  overflow-x: hidden;
  overflow-y: auto;
  flex: 1 1 auto;
  padding-top: 0.5rem;
  padding-bottom: 100px;
}
#menu .menu-body .bi{
  font-size: 1.3rem;
}
#menu .menu-footer {
  position: sticky;
  top: 100%;
  bottom: 0;
}

/* The rail is 50px wide and the sidebar carries a hairline on its right, so an
   item that spans the full width has its background clipped by that line. The
   list is padded instead and every item is a rounded pill inside it, which
   also gives the active state room to read as a filled shape. */
#menu .list-group {
  padding: 0 6px;
}
#menu .list-group .list-group-item {
  margin-bottom: 2px;
  padding: 0.5rem 0.8rem;
  border-radius: var(--bs-border-radius-lg);
  white-space: nowrap;
}
/* Collapsed, the 6px list padding plus 8px item padding lands the 1.3rem icon
   within half a pixel of the rail's centre line. */
#menu.expanded .list-group .list-group-item {
  padding-left: 0.625rem;
  padding-right: 0.625rem;
}
#menu .list-group-item-action:hover,
#menu .list-group-item-action:focus-visible {
  --bs-list-group-action-hover-bg: var(--pg-tint-hover);
}
#menu.expanded .list-group .list-group-item {
  text-overflow: ellipsis;
  overflow: hidden;
}
#menu:not(.expanded) .list-group .list-group-item .list-group-text {
  visibility: hidden;
}

/* A count on a menu entry (the ERP's overdue receivables). Expanded, it sits
   at the row's right edge over the label, which the item already clips with
   an ellipsis; collapsed to the rail, where the label is hidden, it shrinks
   onto the icon's corner so the rail still says how many. */
#menu .list-group .list-group-item {
  position: relative;
}
#menu .list-group .list-group-item .pg-menu-badge {
  position: absolute;
  right: 0.6rem;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1;
  padding: 0.3em 0.55em;
  pointer-events: none;
}
#menu:not(.expanded) .list-group .list-group-item .pg-menu-badge {
  right: 2px;
  top: 2px;
  transform: none;
  font-size: 0.6rem;
  padding: 0.25em 0.4em;
}
@media only screen and (max-width: 991px) {
  #menu.expanded {
    box-shadow: 3px 3px 3px #00000040;
    transition: box-shadow 0.3s ease;
  }
}

/* A 3px tick against the rail edge was easy to miss and was the thing getting
   clipped. The whole row carries the state now; the module colour stays on the
   icon, which keeps the rail readable at a glance. */
#menu .list-group-item.list-group-item-action.active {
  --bs-list-group-active-color: var(--pg-primary-ink);
  --bs-list-group-active-bg: rgba(var(--pg-primary-rgb), 0.18);
  --bs-list-group-active-border-color: transparent;
  --bs-list-group-border-width: 0px;
  font-weight: 600;
}

@keyframes slideInFromTop {
  0% {
    transform: translateY(-200%);
  }
  100% {
    transform: translateY(-50%);
  }
}
@keyframes slideInFromBottom {
  0% {
    transform: translateY(100%);
  }
  100% {
    transform: translateY(-50%);
  }
}

/* ── The foot of the menu: how it shows ──────────────────────────────────
   pg_menu_mode_switch(). One button, opening the three states in a small
   menu of its own -- expanded, mini, hidden -- the way the theme is chosen in
   the account menu rather than by a control that flips on every click. The
   button's picture is the state the menu is in; on the expanded menu it
   carries its name as well, like the rows above it. The old caret that
   rotated in place had no name and only two answers. */
#menu .pg-railfoot {
  padding: 0.375rem 6px;
}
#menu .pg-railmode-btn {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  min-height: 2.5rem;
  padding: 0 0.8rem;
  overflow: hidden;
  border: 0;
  border-radius: var(--bs-border-radius-lg);
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: 0.875rem;
  white-space: nowrap;
}
#menu.expanded .pg-railmode-btn {
  padding: 0 0.625rem;
}
#menu .pg-railmode-btn:is(:hover, :focus-visible),
#menu .pg-railmode-btn[aria-expanded="true"] {
  background: var(--pg-tint-hover);
  color: var(--bs-body-color);
}
#menu .pg-railmode-btn > .bi {
  flex: 0 0 auto;
  font-size: 1.15rem;
  line-height: 1;
}
#menu .pg-railmode-when-expanded,
#menu.expanded .pg-railmode-when-collapsed,
#menu:not(.expanded) .pg-railmode-label {
  display: none;
}
#menu.expanded .pg-railmode-when-expanded {
  display: inline-block;
}
#menu .pg-railmode-label {
  overflow: hidden;
  text-overflow: ellipsis;
}
#menu .pg-railmode-menu {
  --bs-dropdown-min-width: 15rem;
  --bs-dropdown-padding-x: 0.375rem;
  --bs-dropdown-padding-y: 0.375rem;
  --bs-dropdown-header-padding-x: 0.625rem;
  --bs-dropdown-header-padding-y: 0.375rem;
  --bs-dropdown-item-border-radius: var(--bs-border-radius-lg);
  --bs-dropdown-link-hover-bg: var(--pg-tint-hover);
  --bs-dropdown-link-active-bg: var(--pg-tint-strong);
  --bs-dropdown-link-active-color: var(--bs-body-color);
  border-radius: 0.875rem;
  box-shadow: 0 0.75rem 2rem rgba(0, 0, 0, 0.16), 0 0.125rem 0.375rem rgba(0, 0, 0, 0.08);
}
#menu .pg-railmode-menu .dropdown-header {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}
#menu .pg-railmode-menu .dropdown-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.4375rem 0.625rem;
  white-space: normal;
}
#menu .pg-railmode-menu .dropdown-item > .bi:first-child {
  flex: 0 0 1.25rem;
  font-size: 1.0625rem;
  text-align: center;
  color: var(--bs-secondary-color);
}
#menu .pg-railmode-text {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-width: 0;
  font-size: 0.875rem;
  line-height: 1.25;
}
#menu .pg-railmode-text > small {
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
#menu .pg-railmode-check {
  flex: 0 0 auto;
  visibility: hidden;
  color: var(--pg-primary-ink);
}
#menu .pg-railmode-menu [aria-checked="true"] > .bi:first-child,
#menu .pg-railmode-menu [aria-checked="true"] .pg-railmode-check {
  visibility: visible;
  color: var(--pg-primary-ink);
}

.dropdown-item-sm {
  font-size: 85%;
}

/* The collapsed icon rail is 50px wide and its menu list scrolls. A scrollbar
   there would take an eighth of the rail and sit over the icons, so this is the
   one place in the panel where the bar is hidden rather than styled.

   The widget card bodies used to be hidden here too, and that is what left the
   dashboard with a scrollbar on some cards and not on others. Whether a card
   showed one came down to which utility class its scroller happened to carry:
   .overflow-y-auto matched this rule and vanished, while .overflow-auto,
   .pg-split-half and the flex-column bodies fell through to the styled 6px bar
   further down. Nine cards ended up hiding the fact that they had more to show
   -- one of them with nine hundred pixels of list out of reach and nothing on
   screen to say so. They are styled with everything else now. */
#menu:not(.expanded) .menu-body::-webkit-scrollbar {
  width: 0px;
  height: 0px;
  background: transparent; /* make scrollbar transparent */
}
/* ── Off-canvas menu drawer (below lg) ────────────────────────────────────────
   On phones and small tablets the 50px icon rail was reserved permanently, which
   together with the 65px content cap cost roughly a quarter of the viewport and
   pushed everything visibly to the right. Below lg the rail is removed from the
   layout entirely and the menu becomes a drawer that slides over the content.

   The open state uses its own .drawer-open class rather than .expanded on purpose:
   .expanded is rendered server side from the saved desktop preference, so reusing it
   would flash the drawer open on every mobile page load. */
@media only screen and (max-width: 991.98px) {
  .software-container {
    grid-template-columns: 0 minmax(0, 1fr);
  }

  .software-content {
    max-width: 100%;
    min-width: 0;
  }

  .software-container .software-sidebar,
  .software-container:not(.expanded) .software-sidebar {
    width: min(280px, 84vw);
    left: 0;
    transform: translateX(-100%);
    transition: transform 0.3s ease;
  }

  .software-container.drawer-open .software-sidebar {
    transform: translateX(0);
    box-shadow: inset -1px 0 0 var(--bs-border-color), 0 0 24px #00000059;
  }

  /* No rail below lg means no pocket to round; the drawer keeps the flat edge
     it already draws for itself. */
  .software-container .software-sidebar::before,
  .software-container .software-sidebar::after {
    display: none;
  }

  /* The drawer is always full width while open, so labels never hide down here
     regardless of the saved desktop rail state. */
  #menu:not(.expanded) .list-group .list-group-item .list-group-text {
    visibility: visible;
  }
  /* And a count goes back to the row's edge with them. */
  #menu:not(.expanded) .list-group .list-group-item .pg-menu-badge {
    right: 0.6rem;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.7rem;
    padding: 0.3em 0.55em;
  }

  /* Labels are nowrap by default and only get ellipsis in the expanded rail state;
     without this a long entry would just be clipped by menu-body's overflow. */
  #menu .list-group .list-group-item {
    text-overflow: ellipsis;
    overflow: hidden;
  }

  /* How the menu shows on a wide screen means nothing to a drawer; the button
     in the bar, the scrim and Escape are the ways in and out. */
  #menu .pg-railfoot {
    display: none;
  }

  .software-menu-backdrop {
    position: fixed;
    top: var(--pg-navbar-h);
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1029;
    background: #00000059;
    opacity: 0;
    /* Hidden state must not swallow taps meant for the page underneath. */
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.3s ease, visibility 0s linear 0.3s;
    /* Stops a drag on the scrim from scrolling the page behind the drawer. */
    touch-action: none;
  }

  .software-menu-backdrop.show {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 0.3s ease, visibility 0s linear 0s;
  }
}

/* Selectors are kept at least as specific as the ones they need to beat above -
   media queries add no specificity of their own. */
@media (prefers-reduced-motion: reduce) {
  .software-container,
  .software-container .software-sidebar,
  .software-container:not(.expanded) .software-sidebar,
  .software-menu-backdrop.show,
  .software-menu-backdrop {
    transition: none;
  }
}
@media only screen and (min-width: 992px) {
  .software-container.expanded {
    grid-template-columns: var(--pg-rail-w-expanded) minmax(0, 1fr);
  }

  .software-content {
    max-width: 100%;
  }

  /* The rail is position: fixed, so it is out of the grid's flow and the grid
     column above only RESERVES its space -- it does not size it. Declaring the
     width here is what makes the two agree.

     Without this declaration nothing sized the expanded rail at all: the
     collapsed width lives on :not(.expanded), so in the expanded state a fixed
     box with width:auto shrink-to-fit its own longest menu label. In Turkish
     that came to 241px against a 200px column, and the 41px of overhang lay on
     top of the content -- the clock and the greeting were clipped by the menu
     from the left. The `left: 21px` nudge and `max-width: calc(100vw - 236px)`
     that used to sit here were compensation for exactly that: 21px of the 41
     pushed back, and a hand-tuned subtraction to stop the rest overflowing on
     the right.

     Both are gone, because both were wrong in ways that only showed sometimes.
     100vw counts the vertical scrollbar and the grid column does not, so the
     two disagreed by the scrollbar's width whenever the dashboard was long
     enough to scroll -- which changes as widgets reflow, which is why resizing
     the OS window could take the layout in and out of the fault while resizing
     the viewport at a fixed content height never did. And 236px was a magic
     number that only held while the rail was 200px and its labels were the
     ones shipped that day.

     Now the rail is the width it says it is, the grid reserves that width, and
     the content fills its own column with no correction at all. */
  .software-container.expanded .software-sidebar {
    width: var(--pg-rail-w-expanded);
  }

  /* Hidden: the column goes, and the rail slides out with it rather than
     being cut off by it. visibility, delayed until the slide is over, keeps
     its links out of the tab order; coming back, the rail is visible at once
     and slides in. The pocket corner belongs to the rail and goes with it. */
  .software-container .software-sidebar {
    transition: width 0.3s ease, transform 0.3s ease;
  }
  .software-container.menu-hidden {
    grid-template-columns: 0 minmax(0, 1fr);
  }
  .software-container.menu-hidden .software-sidebar {
    transform: translateX(-100%);
    visibility: hidden;
    transition: width 0.3s ease, transform 0.3s ease, visibility 0s linear 0.3s;
  }
  .software-container.menu-hidden .software-sidebar::before,
  .software-container.menu-hidden .software-sidebar::after {
    display: none;
  }

  /* The button in the bar sits over the rail, centred on it, so its picture
     lines up with the icons of the menu below. */
  #header .pg-menu-toggle-item {
    display: flex;
    justify-content: center;
    width: var(--pg-rail-w);
  }
}
@media only screen and (min-width: 992px) and (prefers-reduced-motion: reduce) {
  .software-container .software-sidebar,
  .software-container.menu-hidden .software-sidebar {
    transition: none;
  }
}
.navbar .vh {
  height: 28px;
}

/* ══ Navbar chrome ════════════════════════════════════════════════════════
   Every control in the right hand cluster is sized from one token instead of
   inheriting whatever padding .btn or .nav-link happens to carry. Mixing the
   two is what made the row look ragged - the site link came out 29px tall,
   search 31, the bell 40, the account pill 34 - and left every one of them
   under the 44px a finger needs. Below lg the token grows to 44px; on a mouse
   40px is enough and keeps the 50px bar from feeling stuffed. */
#header {
  --pg-tap: 40px;
  --pg-tap-icon: 1.0625rem;
}
/* Bootstrap Icons gives .bi::before a vertical-align of -0.125em. Inside a
   flex box that shifts the glyph below the centre line, which is what put the
   arrow and the word "Siteye Git" on two different heights. */
#header .bi::before {
  vertical-align: 0;
  line-height: 1;
}
@media (hover: none), (pointer: coarse), (max-width: 991.98px) {
  :root,
  #header {
    --pg-tap: 44px;
  }
}

#header .pg-rightcluster {
  display: flex;
  align-items: center;
  gap: 0.125rem;
  margin-left: auto;
}
/* One rule for both element families, so a .btn and a .nav-link beside each
   other end up the same box. */
#header .pg-rightcluster .nav-link,
#header .pg-rightcluster .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--pg-tap);
  min-height: var(--pg-tap);
  padding: 0 0.5rem;
  border: 0;
  line-height: 1;
}
/* The drawer toggle lives in the left group, so the rule above never reached
   it and Bootstrap's own .btn padding sized it: 46px against the 44px of every
   control opposite, sitting a pixel higher on the row. It is the same kind of
   control, so it reads the same token. */
#header #menu_drawer_toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--pg-tap);
  height: var(--pg-tap);
  padding: 0;
  border: 0;
  line-height: 1;
}
/* .btn-link underlines its label, and when the label is an icon that underline
   is just a stray fourth line under the burger. Nothing in this bar is a
   run-of-text link, so none of them wants one. */
#header .btn-link {
  text-decoration: none;
}
/* Four hairlines in a row at this size read as clutter, so the surface does
   the work instead: named actions sit on a tint, icon-only utilities stay
   transparent until the pointer is on them. */
#header .pg-rightcluster .nav-link {
  border-radius: 50%;
  color: var(--bs-body-color);
}
#header .pg-rightcluster .nav-link:hover,
#header .pg-rightcluster .nav-link:focus-visible {
  background: var(--pg-tint-hover);
}
/* Only the control's own icon, never a nested badge - an unscoped selector
   here blew the licence dot up to 17px. */
#header .pg-rightcluster > ul > li > .nav-link > .bi,
#header .pg-rightcluster > ul > li > .btn > .bi,
#header .pg-rightcluster > ul > li > .nav-link.bi::before,
#header #menu_drawer_toggle > .bi {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--pg-tap-icon);
  line-height: 1;
}
/* The left group is a plain .navbar-nav, whose items stretch to the row rather
   than centring in it: that is why the toggle sat a pixel above the controls
   opposite even once both were the same size. */
#header > ul.navbar-nav {
  align-items: center;
}
/* The two pictures of the menu button (pg_menu_toggle_button()): the side
   panel filled while the menu is on screen, empty while it is not -- the
   drawer below lg, the rail above. Both are printed, so the right one is
   there before the script runs; the ids are repeated to outrank the rule
   above that makes every icon of the button a flex box. */
#header #menu_drawer_toggle > .pg-mt-open {
  display: none;
}
@media (min-width: 992px) {
  .software-container:not(.menu-hidden) #header #menu_drawer_toggle > .pg-mt-shut {
    display: none;
  }
  .software-container:not(.menu-hidden) #header #menu_drawer_toggle > .pg-mt-open {
    display: inline-flex;
  }
}
@media (max-width: 991.98px) {
  .software-container.drawer-open #header #menu_drawer_toggle > .pg-mt-shut {
    display: none;
  }
  .software-container.drawer-open #header #menu_drawer_toggle > .pg-mt-open {
    display: inline-flex;
  }
}

/* ══ Page action bar ═══════════════════════════════════════════════════════
   The strip under the heading on seventy-odd screens. It is the same object as
   the file explorer's toolbar -- a row of actions above the thing they act on
   -- so it is the same component: the geometry comes from .pg-toolbar, which
   #button_bar joins in the selector lists further down this file rather than
   restating here.

   Two things it does need of its own. The markup is legacy and cannot be
   rewritten across seventy files: the actions arrive wrapped in .btn-group and
   dressed as .btn-link, where the toolbar's own recipe would be a bare button
   with .btn-ghost. Both are mapped onto the toolbar's language below, so the
   two bars read the same without a single page being touched.

   A group is also the wrong component for what these are: it welds its children
   together with a -1px margin and rewrites their corner radii so only the ends
   are round. These are unrelated actions -- check for updates, purge cache, view
   the log -- and the moment the strip wraps, the welds land mid-row and the
   radii come out inside out. display:contents dissolves the group boxes so
   their buttons join one wrapping row directly. */

/* A bar with nothing in it is a stray rule across the page. Several screens
   build the strip unconditionally and fill it only for some roles. */
#button_bar:not(:has(.btn)) {
  display: none;
}

#button_bar .btn-group,
#button_bar .btn-group-vertical {
  display: contents;
}

/* Shape and spacing for everything on the bar; the colour is decided by the
   variant below, not here.

   flex: 0 0 auto is the important one. Bootstrap gives .btn-group > .btn a
   flex of 1 1 auto so the members of a group can share a row between them --
   and that selector still matches once the group is dissolved, except that the
   buttons are now items of the bar itself. That is what spread two actions
   across two thousand pixels of empty screen.

   !important only against the py-0, m-1 and mb-2 the markup carries on these
   buttons: those are utilities, they already win on !important, and they are
   what kept the target 17px tall and the gaps uneven. Scoped to this bar, so
   nothing outside it is affected.

   The flex rule is for every button in the bar, not only its direct
   children: a dissolved group's buttons are items of the bar while still
   matching .btn-group > .btn, and a child selector misses them. */
#button_bar .btn {
  flex: 0 0 auto;
}

/* The same shape for the buttons a dissolved group or a wrapping form hands
   to the bar. Not every .btn in it: a button inside an input group keeps the
   group's corners. */
#button_bar > .btn,
#button_bar .btn-group > .btn,
#button_bar > form > .btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin: 0 !important;
  border-radius: var(--bs-border-radius-pill);
  white-space: nowrap;
}

/* A form (export, filter, search) or an inline wrapper in the bar lays its
   controls out on the bar's own line instead of being one inline box.
   inline-flex rather than display:contents keeps the form a box, so submit
   and the hidden token fields behave as before. !important only against the
   d-inline-block / d-flex utilities the markup carries. A form that is
   itself an input group (the round search box) keeps its own layout. */
#button_bar > form:not(.input-group):not(.d-none):not([hidden]),
#button_bar > span:not(.d-none):not([hidden]),
#button_bar > .d-inline-block:not(.d-none):not([hidden]) {
  display: inline-flex !important;
  align-items: center;
  gap: inherit;
  min-width: 0;
  margin: 0;
}

/* Only a wrapper of buttons wraps. A form with fields stays on one line -
   its fields carry w-auto - or a date range splits from its label. */
#button_bar > form:not(.input-group):not(:has(.form-control, .form-select)),
#button_bar > span,
#button_bar > .d-inline-block {
  flex-wrap: wrap;
}

/* The real actions - Create and its siblings - keep the variant they were given
   and take the shape the explorer's New button has: small, pill, and roomy
   enough across to read as the one thing on the bar worth pressing. Set through
   the component's own variables so padding and type scale together. */
#button_bar .btn:not(.btn-link) {
  --bs-btn-padding-y: 0.25rem;
  --bs-btn-padding-x: 0.9rem;
  --bs-btn-font-size: 0.875rem;
  --bs-btn-line-height: 1.5;
}

/* The helper links are the quiet half, and quiet here means exactly what it
   means on the explorer's toolbar: no border until hovered, secondary ink,
   the theme's own surface underneath. Same declarations as .btn-ghost, reached
   through the class the legacy markup happens to carry. */
#button_bar .btn-link {
  --bs-btn-color: var(--bs-secondary-color);
  --bs-btn-hover-color: var(--bs-body-color);
  --bs-btn-hover-bg: var(--bs-secondary-bg);
  --bs-btn-active-color: var(--bs-body-color);
  --bs-btn-active-bg: var(--bs-secondary-bg);
  --bs-btn-border-color: transparent;
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-border-color: transparent;
  padding: 0.3rem 0.7rem !important;
  border: 0;
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: 0.8125rem;
  line-height: 1.3;
  text-decoration: none;
}
#button_bar .btn-link:hover,
#button_bar .btn-link:focus-visible {
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
}
/* The icons carry their own me-1 from the markup, which doubles the gap. */
#button_bar .btn .material-icons,
#button_bar .btn .bi {
  margin: 0 !important;
  font-size: 1rem;
  line-height: 1;
}
/* A finger needs more than the 26px a mouse is happy with. Only the links need
   the extra reach; the actions already clear one. */
@media (hover: none), (pointer: coarse) {
  #button_bar .btn-link {
    padding: 0.5rem 0.85rem !important;
  }
}

/* Page name over an optional one line description. min-width:0 is what lets
   both lines ellipsis instead of pushing the cluster off the end of the bar. */
#header .pg-navhead {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  max-width: 26rem;
  padding: 0 0.25rem;
  line-height: 1.2;
}



/* Expanded search, in the middle of the header.
   Absolutely positioned rather than dropped into the flow, because the flow
   has no middle to drop it into: .pg-rightcluster takes an auto margin of its
   own, so the free space either side of the centring spacer splits three ways
   and the "centre" lands some two hundred pixels left of the real one.
   Hidden until the JS that owns the search reveals it, same as the compact
   button, so a build without search never shows an empty field. */
#header .pg-searchbar {
  display: none;
}
#header .pg-searchbar-field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  min-height: calc(var(--pg-tap) - 0.5rem);
  padding: 0 0.5rem 0 0.75rem;
  border: var(--bs-border-width) solid var(--bs-border-color-translucent);
  border-radius: 999px;
  background: var(--pg-tint);
  color: var(--bs-secondary-color);
  font-size: 0.8125rem;
  text-align: left;
  /* A text caret rather than a pointer: it opens a dialog, but what it offers
     is somewhere to type, and that is what the shape promises. */
  cursor: text;
}
#header .pg-searchbar-field:hover,
#header .pg-searchbar-field:focus-visible {
  background: var(--pg-tint-hover);
  color: var(--bs-body-color);
}
#header .pg-searchbar-icon {
  flex: 0 0 auto;
  font-size: 0.875rem;
}
#header .pg-searchbar-text {
  flex: 1 1 auto;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#header .pg-searchbar-kbd {
  flex: 0 0 auto;
  padding: 1px 6px;
  border-radius: var(--bs-border-radius-sm);
  background: var(--pg-tint-strong);
  font-size: 0.6875rem;
  line-height: 1.4;
  opacity: 0.75;
}
/* 1400px is where a centred field stops costing the heading its title. Below
   it the compact button in the right cluster is the search. */
@media (min-width: 87.5rem) {
  #header {
    --pg-searchbar-w: clamp(18rem, 24vw, 28rem);
  }
  #header .pg-searchbar {
    position: absolute;
    left: 50%;
    display: block;
    width: var(--pg-searchbar-w);
    transform: translateX(-50%);
  }
  /* The heading gives way instead of the field sliding off centre: half the
     viewport, less half the field, less the logo group and a gap. Where the
     screen is wide enough the plain 26rem wins and nothing is given up. */
  #header:has(.pg-searchbar) .pg-navhead {
    max-width: min(26rem, calc(50vw - (var(--pg-searchbar-w) / 2) - 12rem));
  }
  /* One search per screen. */
  #header:has(.pg-searchbar) #EnableSearch {
    display: none;
  }
}
#header .pg-navhead-title,
#header .pg-navhead-desc {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#header .pg-navhead-title {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
}
/* Only the toolbar's heading is a record, and only there does it link to
   where that record is edited. The colour stays whatever the heading was --
   the icon span next to the name carries a text utility and would win the
   cascade anyway -- so the underline is the whole affordance. */
#header .pg-navhead-title .pg-navhead-link {
  color: inherit;
  text-decoration: none;
}
#header .pg-navhead-title .pg-navhead-link:hover,
#header .pg-navhead-title .pg-navhead-link:focus-visible {
  text-decoration: underline;
}
#header .pg-navhead-desc {
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
/* Below lg the bar also carries the drawer button, so the description is the
   first thing to give up its space. */
@media only screen and (max-width: 991.98px) {
  #header .pg-navhead {
    max-width: 45vw;
  }
  #header .pg-navhead-desc {
    display: none;
  }
}

#header .pg-sitebtn,
#header .pg-acctbtn {
  --bs-btn-bg: var(--pg-tint);
  --bs-btn-color: var(--bs-body-color);
  --bs-btn-border-color: transparent;
  --bs-btn-hover-bg: rgba(var(--pg-primary-rgb), 0.14);
  --bs-btn-hover-border-color: transparent;
  --bs-btn-hover-color: var(--pg-primary-ink);
  --bs-btn-active-bg: rgba(var(--pg-primary-rgb), 0.22);
  --bs-btn-active-border-color: transparent;
  --bs-btn-active-color: var(--pg-primary-ink);
  --bs-btn-border-radius: var(--bs-border-radius-pill);
}
#header .pg-sitebtn {
  gap: 0.45rem;
  padding: 0 0.875rem;
  font-size: 0.8125rem;
  font-weight: 500;
  white-space: nowrap;
}
/* Last in the row and the only link that leaves the panel, so it gets more air
   than the 0.125rem the rest of the cluster shares - two pills at that gap read
   as one wide control. */
#header .pg-rightcluster .pg-siteout {
  margin-left: 0.5rem;
}

/* The avatar on its own never read as a control; the name and the chevron say
   it opens something, and the ghost button ties it to the rest of the palette. */
#header .pg-acctbtn {
  gap: 0.45rem;
  padding: 0 0.625rem 0 0.3125rem;
}
#header .pg-acctname {
  max-width: 9rem;
  overflow: hidden;
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#header .pg-chev {
  font-size: 0.6rem;
  opacity: 0.55;
}
#header .pg-avwrap {
  position: relative;
  display: block;
  flex: 0 0 auto;
}
#header .pg-avatar {
  display: block;
  width: 28px;
  height: 28px;
  overflow: hidden;
  border-radius: 50%;
}
#header .pg-avatar img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.1);
}
/* Licence state rides on the avatar rather than on a control of its own, so it
   survives the breakpoint that hides the wordmark and the account name.
   Everything else about it moved, though: a plain dot in the bottom-right
   corner is the shape, size and position every product uses for presence, and
   that is exactly how it was read. This one carries the gem the licence row
   carries, sits in the opposite corner where presence never does, and is two
   pixels wider so the glyph inside it is legible. The ring is a box-shadow
   rather than a border so it does not eat into those two pixels. */
#header .pg-lic-badge {
  position: absolute;
  top: -3px;
  right: -3px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  box-shadow: 0 0 0 2px var(--bs-body-bg);
  font-size: 9px;
  line-height: 1;
}
#header .pg-lic-badge .bi::before {
  display: block;
}

/* ══ Breadcrumb bar ═══════════════════════════════════════════════════════
   Part of the chrome rather than of the content column, so it spans the full
   width and sits flush under the header while <main> stays centred. The old
   markup was a .container with pt-2 wrapped around an <ol> that picked up a
   30px top margin from the global .breadcrumb rule - that margin, not the
   crumbs, was the empty band under the header. The global rule is left alone
   because a dozen pages render their own breadcrumb inside forms and cards. */
.pg-breadcrumb {
  display: flex;
  align-items: center;
  min-height: 34px;
  padding: 0 1rem;
  border-bottom: var(--bs-border-width) solid var(--bs-border-color);
}
.pg-breadcrumb .breadcrumb {
  --bs-breadcrumb-divider: "\203A";   /* › — escaped so no encoding can break it */
  --bs-breadcrumb-item-padding-x: 0.4rem;
  margin: 0;
  flex-wrap: nowrap;
  /* A deep trail scrolls sideways instead of wrapping onto a second line and
     doubling the height of the bar. */
  overflow-x: auto;
  font-size: 0.75rem;
  white-space: nowrap;
  scrollbar-width: none;
}
.pg-breadcrumb .breadcrumb::-webkit-scrollbar {
  display: none;
}
.pg-breadcrumb .breadcrumb-item + .breadcrumb-item::before {
  color: var(--bs-tertiary-color);
}
.pg-breadcrumb .breadcrumb-item a {
  color: var(--bs-secondary-color);
  text-decoration: none;
}
.pg-breadcrumb .breadcrumb-item a:hover,
.pg-breadcrumb .breadcrumb-item a:focus-visible {
  color: var(--pg-primary-ink);
  text-decoration: underline;
}
.pg-breadcrumb .breadcrumb-item.active {
  color: var(--bs-body-color);
  font-weight: 500;
}
/* The trail is the only thing allowed to shrink; the back control keeps its
   footprint so it never collapses on a deep crumb list. */
.pg-breadcrumb > nav {
  display: flex;
  min-width: 0;
}
.pg-breadcrumb .pg-backbtn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  margin: 0 0.35rem 0 -0.35rem;
  padding: 0;
  border: 0;
  border-radius: var(--bs-border-radius);
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: 0.8125rem;
  line-height: 1;
}
/* Bootstrap Icons ship a -0.125em baseline nudge that pushes the glyph off
   centre once the box is a flex container. */
.pg-breadcrumb .pg-backbtn .bi::before {
  vertical-align: 0;
  line-height: 1;
}
.pg-breadcrumb .pg-backbtn:hover,
.pg-breadcrumb .pg-backbtn:focus-visible {
  background: var(--pg-tint-hover);
  color: var(--pg-primary-ink);
}
/* A finger needs more than the 26px the strip gives a mouse, so the bar grows
   with the control rather than the control overflowing the bar. */
@media (hover: none), (pointer: coarse) {
  .pg-breadcrumb {
    min-height: 44px;
  }

  .pg-breadcrumb .pg-backbtn {
    width: 36px;
    height: 36px;
    font-size: 0.9375rem;
  }
}

/* ══ Account menu ═════════════════════════════════════════════════════════
   pg_account_menu() in includes/fn/output.php.

   Fixed to the viewport, never hung from its button. Bootstrap does not hand
   a dropdown inside a .navbar to Popper, so a menu placed by its toggle has
   nothing that keeps it on the screen: the one before this hung from the
   button, ran off the left edge of a phone as soon as it grew, and could not
   be scrolled at all, because the overflow:hidden of #header .pg-usermenu (an
   id and a class) beat the overflow-y:auto of #user_menu (an id alone) -- on
   a phone the rows below the fold, logout among them, were out of reach.

   Above the phone breakpoint it is a card under the right edge of the bar,
   capped at the height of the screen and scrolling inside; on a phone it is
   a sheet over everything below the bar. One scroller in both shapes, the
   menu itself, and its overscroll is contained so the page underneath stays
   where it was. */
#header .pg-usermenu {
  --pg-um-pad: 0.375rem;
  --pg-um-row: 2.25rem;
  --bs-dropdown-min-width: 0;
  --bs-dropdown-padding-x: var(--pg-um-pad);
  --bs-dropdown-padding-y: var(--pg-um-pad);
  --bs-dropdown-font-size: 0.875rem;
  --bs-dropdown-border-radius: 1rem;
  --bs-dropdown-item-padding-x: 0.625rem;
  --bs-dropdown-item-padding-y: 0.375rem;
  --bs-dropdown-item-border-radius: var(--bs-border-radius-lg);
  --bs-dropdown-divider-margin-y: var(--pg-um-pad);
  --bs-dropdown-divider-bg: var(--bs-border-color-translucent);
  --bs-dropdown-link-hover-bg: var(--pg-tint-hover);
  --bs-dropdown-link-hover-color: var(--bs-body-color);
  --bs-dropdown-link-active-bg: var(--pg-tint-strong);
  --bs-dropdown-link-active-color: var(--bs-body-color);
  /* With the acrylic effect on (.advanced-visuals ... .backdrop) the glass is
     kept but thickened: the 60% the other menus use left the dashboard's
     figures legible through the form fields and the rows of this one. */
  --bs-bg-opacity: 0.86;
  position: fixed;
  top: calc(var(--pg-navbar-h) + 0.375rem);
  right: 0.5rem;
  left: auto;
  width: 20rem;
  max-width: calc(100vw - 1rem);
  max-height: calc(100vh - var(--pg-navbar-h) - 1rem);
  margin: 0;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.16), 0 0.125rem 0.375rem rgba(0, 0, 0, 0.08);
  transform-origin: top right;
}
/* What the browser's own bars leave of a phone's screen, where it is known. */
@supports (height: 100dvh) {
  #header .pg-usermenu {
    max-height: calc(100dvh - var(--pg-navbar-h) - 1rem);
  }
}
/* A finger gets the tap height the bar's own controls have. */
@media (hover: none), (pointer: coarse) {
  #header .pg-usermenu {
    --pg-um-row: var(--pg-tap);
  }
}
@media (prefers-reduced-motion: no-preference) {
  #header .pg-usermenu.show {
    animation: pg-um-in 0.14s ease-out;
  }
}
@keyframes pg-um-in {
  from {
    opacity: 0;
    transform: translateY(-0.25rem) scale(0.98);
  }
}

#header .pg-usermenu .dropdown-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: var(--pg-um-row);
  white-space: nowrap;
}
#header .pg-usermenu .dropdown-item > .bi:first-child {
  flex: 0 0 1.25rem;
  font-size: 1.0625rem;
  text-align: center;
  color: var(--bs-secondary-color);
}
#header .pg-usermenu .dropdown-item:is(:hover, :focus-visible) > .bi:first-child {
  color: var(--bs-body-color);
}
#header .pg-usermenu .pg-um-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* A row that leaves for another tab says so after its label. */
#header .pg-usermenu .pg-um-ext {
  flex: 0 0 auto;
  font-size: 0.6875rem;
  color: var(--bs-tertiary-color);
}
/* The dividers run edge to edge, through the card's own padding. */
#header .pg-usermenu .dropdown-divider {
  margin-right: calc(-1 * var(--pg-um-pad));
  margin-left: calc(-1 * var(--pg-um-pad));
}
#header .pg-um-section {
  padding: 0.125rem 0.625rem 0.25rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}
/* A private label link that has a name and no address. */
#header .pg-usermenu .pg-um-note {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: var(--pg-um-row);
  color: var(--bs-secondary-color);
}

/* Who is signed in. Only an administrator's opens their user record; for
   everybody else it is a plain heading. */
#header .pg-um-head {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}
#header .pg-um-identity {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
  padding: 0.5rem 0.625rem;
  border-radius: var(--bs-border-radius-lg);
  color: var(--bs-body-color);
  text-decoration: none;
}
#header a.pg-um-identity:is(:hover, :focus-visible) {
  background: var(--pg-tint-hover);
  color: var(--bs-body-color);
}
#header .pg-um-avatar {
  flex: 0 0 auto;
  width: 2.75rem;
  height: 2.75rem;
  overflow: hidden;
  border-radius: 50%;
  box-shadow: 0 0 0 1px var(--bs-border-color-translucent);
}
#header .pg-um-avatar img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
#header .pg-um-idtext {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
  line-height: 1.25;
}
#header .pg-um-name {
  overflow: hidden;
  font-size: 0.9375rem;
  font-weight: 600;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#header .pg-um-sub {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  min-width: 0;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
#header .pg-um-login {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* The role as a quiet pill. The label is stored lower case for use inside
   sentences, so its first letter is raised here, by the browser, which knows
   the page's language and so a Turkish dotted i. */
#header .pg-um-role {
  flex: 0 0 auto;
  padding: 0.0625rem 0.4375rem;
  border-radius: 999px;
  background: var(--pg-tint-strong);
  color: var(--bs-body-color);
  font-size: 0.6875rem;
  font-weight: 500;
}
#header .pg-um-role::first-letter {
  text-transform: uppercase;
}
#header .pg-um-idgo {
  flex: 0 0 auto;
  font-size: 0.75rem;
  color: var(--bs-tertiary-color);
}
/* The phone sheet covers the screen, so it carries a way out of its own
   beside the button that opened it. Hidden on the card. */
#header .pg-um-close {
  display: none;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: var(--pg-tap);
  height: var(--pg-tap);
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: 1rem;
}
#header .pg-um-close:is(:hover, :focus-visible) {
  background: var(--pg-tint-hover);
  color: var(--bs-body-color);
}

/* The address book form, while the name or the picture is missing. */
#header .pg-um-card {
  margin: 0.25rem 0 0.125rem;
  padding: 0.75rem;
  border-radius: var(--bs-border-radius-lg);
  background: var(--pg-tint);
  white-space: normal;
}
#header .pg-um-cardtitle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
  font-size: 0.8125rem;
  font-weight: 600;
}
#header .pg-um-cardtext {
  margin-bottom: 0.625rem;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
#header .pg-um-cardpic {
  flex: 0 0 auto;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  object-fit: cover;
}

/* Appearance: one setting, so one control -- a track of three segments where
   elevation and ink mark the one that is on. */
#header .pg-um-theme {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: var(--pg-um-row);
  padding: 0.125rem 0.125rem 0.125rem 0.625rem;
}
#header .pg-um-label {
  flex: 1 1 auto;
  min-width: 0;
}
#header .pg-um-themeswitch {
  display: inline-flex;
  flex: 0 0 auto;
  gap: 2px;
  padding: 2px;
  border-radius: 999px;
  background: var(--pg-tint-strong);
}
#header .pg-um-themebtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 1.75rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: 0.875rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
#header .pg-um-themebtn:is(:hover, :focus-visible) {
  color: var(--bs-body-color);
}
#header .pg-um-themebtn.active {
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}
@media (hover: none), (pointer: coarse) {
  #header .pg-um-themebtn {
    width: 2.5rem;
    height: 2.25rem;
  }
}
@media (prefers-reduced-motion: reduce) {
  #header .pg-um-themebtn {
    transition: none;
  }
}

/* Version, edition and environment: one quiet line at the foot. */
#header .pg-um-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
  padding: 0.25rem 0.625rem 0.125rem;
  font-size: 0.6875rem;
  color: var(--bs-tertiary-color);
}
#header .pg-um-metaitem {
  white-space: nowrap;
}
#header .pg-um-metaitem + .pg-um-metaitem::before {
  content: "\00B7";
  margin-right: 0.5rem;
}
#header .pg-um-env {
  color: var(--pg-warning-ink);
  font-weight: 600;
}

/* Licence summary (license_check(), output 'bar'), with the countdown, the
   progress bar and the buttons collapsed underneath it. A tinted card of its
   own, so its state colour never bleeds into the rows around it. */
#header .pg-lic-item {
  --pg-lic-ink: var(--bs-secondary-color);
  --pg-lic-bg: var(--bs-secondary-bg);
  margin: 0.25rem 0 0.125rem;
  overflow: hidden;
  border-radius: var(--bs-border-radius-lg);
  background: var(--pg-lic-bg);
}
#header .pg-lic-success {
  --pg-lic-ink: var(--pg-success-ink);
  --pg-lic-bg: var(--pg-success-subtle);
}
#header .pg-lic-warning {
  --pg-lic-ink: var(--pg-warning-ink);
  --pg-lic-bg: var(--pg-warning-subtle);
}
#header .pg-lic-danger {
  --pg-lic-ink: var(--pg-danger-ink);
  --pg-lic-bg: var(--pg-danger-subtle);
}
#header .pg-lic-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  min-height: var(--pg-um-row);
  padding: 0.375rem 0.625rem;
  border: 0;
  background: transparent;
  color: var(--pg-lic-ink);
  font-size: 0.75rem;
  font-weight: 600;
  text-align: left;
}
#header .pg-lic-row > .bi:first-child {
  flex: 0 0 1.25rem;
  font-size: 0.9375rem;
  text-align: center;
}
/* An inset wash rather than a brightness filter: the card is pale in the
   light theme and near black in the dark one, so a filter would move the
   wrong way in one of them. */
#header .pg-lic-row:is(:hover, :focus-visible) {
  box-shadow: inset 0 0 0 9999px var(--pg-tint);
}
#header .pg-lic-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#header .pg-lic-meta {
  flex: 0 0 auto;
  font-weight: 400;
  opacity: 0.85;
}
#header .pg-lic-chev {
  flex: 0 0 auto;
  font-size: 0.6rem;
  transition: transform 0.2s ease;
}
#header .pg-lic-row[aria-expanded="true"] .pg-lic-chev {
  transform: rotate(180deg);
}
#header .pg-lic-panel {
  border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
}
@media (prefers-reduced-motion: reduce) {
  #header .pg-lic-chev {
    transition: none;
  }
}

/* ── On a phone: a sheet over the screen below the bar ───────────────────
   Anchored to all four edges instead of given a height, so whatever the
   browser's bars leave is what it fills. */
@media (max-width: 575.98px) {
  #header .pg-usermenu {
    --pg-um-pad: 0.5rem;
    --pg-um-row: var(--pg-tap);
    --bs-dropdown-font-size: 0.9375rem;
    /* Solid, glass or not: a sheet over the whole screen has nothing behind
       it worth seeing, and through the 60% the glass leaves the figures of
       the dashboard read straight through the menu. Important only against
       the important of the acrylic rule (.advanced-visuals ... .backdrop). */
    --bs-dropdown-bg: var(--bs-body-bg) !important;
    top: var(--pg-navbar-h);
    right: 0;
    bottom: 0;
    left: 0;
    width: auto;
    max-width: none;
    max-height: none;
    padding: var(--pg-um-pad) 0.75rem calc(1.5rem + env(safe-area-inset-bottom, 0px));
    border: 0;
    border-radius: 0;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    transform-origin: top center;
  }
  #header .pg-um-close {
    display: inline-flex;
  }
  /* The page underneath holds still, and the chat bubble, which sits above
     every layer of the panel, steps out of the way of the bottom rows. */
  html:has(#user_menu.show) {
    overflow: hidden;
  }
  body:has(#user_menu.show) #pg-chat-root {
    display: none;
  }
}

/* ══ Keyboard shortcuts ═══════════════════════════════════════════════════
   pg_shortcuts_dialog() in includes/fn/output.php, opened with ? and from the
   search box. Two groups of keys and what they do, a <dl> each; the keys are
   drawn as caps rather than as Bootstrap's dark code pill. */
.pg-keys-modal .modal-dialog {
  max-width: 30rem;
}
.pg-keys-modal .modal-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
}
.pg-keys-title {
  margin: 0 0 0.25rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}
.pg-keys + .pg-keys-title {
  margin-top: 1.25rem;
}
.pg-keys {
  margin: 0;
}
.pg-keys-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.4375rem 0;
  border-bottom: var(--bs-border-width) solid var(--bs-border-color-translucent);
}
.pg-keys-row:last-child {
  border-bottom: 0;
}
.pg-keys-row dt {
  display: flex;
  flex: 0 0 auto;
  order: 2;
  gap: 0.25rem;
  font-weight: 400;
}
.pg-keys-row dd {
  order: 1;
  margin: 0;
  font-size: 0.875rem;
}
.pg-keys kbd {
  min-width: 1.75rem;
  padding: 0.125rem 0.4375rem;
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-bottom-width: 2px;
  border-radius: 0.375rem;
  background: var(--bs-tertiary-bg);
  color: var(--bs-body-color);
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
}

#bd-themes {
  --bs-dropdown-link-active-bg: rgba(var(--bs-primary-rgb), 10%);
  --bs-dropdown-link-active-color: var(--bs-body-color);
}
#bd-themes .dropdown-item.active:before {
  content: "\F633";
  display: inline-block;
  font-family: bootstrap-icons !important;
  font-style: normal;
  font-weight: normal !important;
  font-variant: normal;
  text-transform: none;
  line-height: 1;
  vertical-align: -0.125em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.btn:not(.btn-primary):not(.btn-secondary):not(.btn-success):not(
    .btn-danger
  ):not(.btn-warning):not(.btn-info):not(.btn-light):not(.btn-dark):not(
    .btn-outline-primary
  ):not(.btn-outline-secondary):not(.btn-outline-success):not(
    .btn-outline-danger
  ):not(.btn-outline-warning):not(.btn-outline-info):not(
    .btn-outline-light
  ):not(.btn-outline-dark):not(.btn-close):not(.btn-ghost) {
  --bs-btn-bg: rgba(var(--bs-body-bg-rgb), 0%);
  --bs-btn-hover-bg: rgba(var(--bs-tertiary-bg-rgb), 100%);
  --bs-btn-active-bg: rgba(var(--bs-tertiary-bg-rgb), 50%);
  --bs-btn-border-color: rgba(var(--bs-tertiary-bg-rgb), 5%);
  --bs-btn-hover-border-color: rgba(var(--bs-tertiary-bg-rgb), 25%);
  --bs-btn-active-border-color: rgba(var(--bs-tertiary-bg-rgb), 50%);
}

.btn-circle {
  position: relative;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-radius: 50%;
  height: auto; /* fallback height */
}
.bi-me-2:before {
  margin-right: 0.5rem !important;
}

.fs-smaller {
  font-size: smaller;
}

.widget {
  width: 100%;
  max-width: 100%;
}

.contextmenu.popover .popover-body {
  max-height: 74vh;
  overflow: auto;
  --bs-popover-body-padding-x: 0.3rem;
  --bs-popover-body-padding-y: 0.3rem;
  display: block;
  flex-direction: column;
}

.contextmenu.popover .popover-body a{
  display:block
}

.popover-info.popover{
  --bs-popover-header-bg:var(--bs-body-bg);
  margin-left: 1rem !important;
}
.popover-info.popover .popover-body{
  --bs-popover-body-padding-x: 1rem;
  --bs-popover-body-padding-y: 1rem;
}

.contextmenu.popover .divider {
  opacity: 0.1;
}

/* Long pressing a menu entry opens our own context menu, so suppress the iOS
   callout (copy / share sheet) and the text selection that fights with it. */
#menu a.list-group-item-action[data-bs-toggle="context"] {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

.pointer,
.cursor-pointer {
  cursor: pointer;
}
/*END OF 24.1*/
#content {
  padding: 1rem;
}

table.chart tr th {
  white-space: nowrap;
  padding-right: 1rem;
}
a.link-top-row,
table.chart thead th {
  color: var(--bs-secondary-color);
}

/*NOTIFICATIONS*/
#notifications {
  width: 350px;
  right: 2px !important;
  left: auto;
  top: calc(var(--pg-navbar-h) + 2px) !important;

  max-height: calc(100vh - 60px);
  max-width: calc(100% - 57px);
}

/* Quick note beside the bell (ws_notes_quick_markup()): placed like the
   notifications, under the header's right edge. */
#pg_quick_note {
  width: 350px;
  right: 2px !important;
  left: auto;
  top: calc(var(--pg-navbar-h) + 2px) !important;
  max-width: calc(100% - 4px);
}

#pg_quick_note textarea {
  resize: vertical;
  min-height: 6rem;
}

#notifications .list-group-item .notification-description {
  line-height: 15px;
  font-size: 13px;
  padding: 0 5px;
  margin: 0;
  margin-bottom: 0.3rem;
  border-radius: var(--bs-border-radius-sm);
  display: -webkit-box;
  max-width: 100%;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

#notifications .list-group-item .notification-title {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}

#notifications .list-group-item.success .notification-title,
#notifications .list-group-item.new.success .notification-title,
#notifications .list-group-item.new .notification-title {
  color: var(--bs-success-text-emphasis);
}

#notifications .list-group-item.warning .notification-title,
#notifications .list-group-item.new.warning .notification-title {
  color: var(--bs-warning-text-emphasis);
}

#notifications .list-group-item.error .notification-title,
#notifications .list-group-item.new.error .notification-title {
  color: var(--bs-danger-text-emphasis);
}

#notifications .notification-card {
  position: relative;
}

#notifications .list-group-item.new .notification-card:before,
#notifications .list-group-item.new.warning .notification-card:before,
#notifications .list-group-item.new.error .notification-card:before {
  content: "";
  width: 5px;
  height: 1rem;
  background-color: var(--bs-success);
  display: block;
  position: absolute;
  top: 1rem;
  border-radius: var(--bs-border-radius-sm);
  left: 2px;
}

#notifications .list-group-item.new.warning .notification-card:before {
  background-color: var(--bs-warning);
}

#notifications .list-group-item.new.error .notification-card:before {
  background-color: var(--bs-danger);
}

#notifications .list-group-item.new.viewed .notification-card:before {
  opacity: 0.3;
}

/*Material-icons*/
.material-icons {
  vertical-align: middle;
  line-height: 20px;
  font-size: 20px;
  font-family: "Material Icons" !important;
  font-weight: normal !important;
}

#notifications .menu-scroll-area {
  max-height: inherit;
  border-radius: var(--bs-dropdown-border-radius);
  width: 100%;
}

/* The dropdown is capped at the viewport, and the list inside it used to be
   free to take that whole height - which pushed anything sitting after the list
   past the bottom of the screen. On a phone that is every row after the third
   notification. Laying the open dropdown out as a column, with the list as the
   only part allowed to shrink, keeps the footer reachable however long the list
   gets. min-height:0 is what actually permits the shrink: a flex item defaults
   to min-height:auto and refuses to be smaller than its content. */
#notifications.show {
  display: flex;
  flex-direction: column;
}

#notifications.show .menu-scroll-area {
  flex: 1 1 auto;
  min-height: 0;
}

#notifications #push_row,
#notifications #push_hint {
  flex: 0 0 auto;
}

.alert p.notice,
.alert p.warning,
.alert p.error {
  border-bottom: 2px solid;
  padding: 0.5em;
  margin-bottom: 0.5em;
  border-color: inherit;
}

.alert p.notice:last-child,
.alert p.warning:last-child,
.alert p.error:last-child {
  border-bottom: 2px solid transparent !important;
  margin-bottom: 0px;
  padding-bottom: 0px;
}
#product_group_tree {
  min-height: 200px;
  overflow-x: auto;
  white-space: nowrap;
  padding-bottom: 2rem;
}

#product_group_tree ul {
  list-style: none;
  padding: 0 0 0 1.5em;
}

#product_group_tree li {
  white-space: nowrap;
  padding: 0 0 0 1.5em;
}

#product_group_tree .row {
  display: block;
  background: transparent;
  padding-top: 0.3em;
  padding-bottom: 0.3em;
  width: 100%;
  -webkit-user-select: none; user-select: none;
  transition: background-color 0.2s ease;
  position: relative;
  margin-left:-1.2rem;
}

#product_group_tree .row span {
  cursor: pointer;
}

#product_group_tree .row:hover {
  background: #95959521;
  transition: background-color 0.1s ease;
}

#product_group_tree .row .status_button {
  visibility: hidden;
  width: auto;
  opacity: 0;
}

#product_group_tree .row:hover .status_button {
  visibility: visible;
  opacity: 1;
}

#product_group_tree .row .status_button:hover {
  visibility: visible;
}

/*tagin*/
.tagin {
  display: none;
}

.tagin-wrapper {
  display: block;
  height: auto;
  padding: calc(0.375rem - 2px) calc(0.75rem - 2px);
  position: relative;
  overflow: hidden;
  cursor: text;
  /* One unbreakable tag (a pasted URL, a blob of code) must not leak its
     intrinsic width into the surrounding grid: the percentage max-width on
     .tagin-tag_text clips it visually but is ignored during min-content
     sizing, so without this a single long tag pushes the whole column onto
     its own flex line. inline-size containment makes the wrapper's
     intrinsic width independent of its contents; its real width still
     comes from the column, so nothing else changes. */
  contain: inline-size;
}

.tagin-wrapper.focus {
  outline: 0;
}

.tagin.is-valid + .tagin-wrapper,
.was-validated .tagin:valid + .tagin-wrapper {
  border-color: var(--bs-form-valid-border-color);
}

.tagin.is-invalid + .tagin-wrapper,
.was-validated .tagin:invalid + .tagin-wrapper {
  border-color: var(--bs-form-invalid-border-color);
}

.tagin-tag {
  display: inline-flex;
  border-radius: var(--bs-border-radius-sm);
  border: var(--bs-border-width) solid var(--bs-border-color);
  padding: 4px 2px 4px 12px;
  min-width: 60px;
  max-width: 100%;
  align-items: center;
  min-height: 24px;
  margin: 2px;
  font-weight: 300;
  transition: transform 0.1s;
  justify-content: space-between;
}

.tagin-tag_text {
  max-width: calc(100% - 30px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tagin-tag-remove {
  margin-left: 10px;
  min-width: 20px;
  min-height: 20px;
  cursor: pointer;
  background-repeat: no-repeat;
  background-position: right;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23a0aec0' width='18px' height='18px'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z'/%3E%3C/svg%3E");
}

.tagin-tag-remove:hover {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23d80505' width='18px' height='18px'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z'/%3E%3C/svg%3E");
}

.tagin-input {
  margin-left: 2px;
  border-color: transparent;
  outline: 0;
  border-width: 1px 0;
  padding: 0 2px 0 0;
  height: 28px;
  color: var(--bs-body-color);
  background-color: inherit;
}

.tagin-input:not(.tagin-input-hidden) {
  width: 4px;
  min-width: 4px;
}

.tagin-input-hidden {
  position: absolute;
  top: 0;
  left: -9999px;
  overflow: hidden;
  visibility: hidden;
  white-space: nowrap;
}

.min-height-tagin {
  min-height: 66px;
}

.min-width-tagin {
  min-width: 150px !important;
}
textarea.select2-search__field:focus-visible,
input.tagin-input:focus-visible {
    outline: none !important;
    box-shadow: none !important;
}

input[readonly=true], input[readonly=true]:focus {
    background: transparent !important;
    box-shadow: none !important;
    border: var(--bs-border-width) solid var(--bs-border-color) !important;
}

.collapse.popover {
  margin-left: 0.3rem;
  width: max-content;
  max-width: calc(100% - 0.3rem) !important;
  z-index: auto;
  position: relative;
}
.pg-settings-modal .collapse.popover{
  margin-top:.5rem;
}
body.toolbar {
  background-color: transparent;
  background: linear-gradient(180deg, #0000008c, transparent);
  background-size: 100%;
  background-repeat: no-repeat;
  height: 100vh;
}

@media only screen and (max-width: 891px) {
  .toolbar-nav {
    position: absolute;
    --bs-bg-opacity: 1;
    background-color: rgba(
      var(--bs-body-bg-rgb),
      var(--bs-bg-opacity)
    ) !important;
    width:100%;
    top: var(--pg-navbar-h);
    border-radius: 0;
    padding-left: 20px;
    padding-right: 20px;
    justify-content: space-evenly;
  }
}

.select2-selection.select2-selection--multiple {
  min-height: 75px;
}

.bg-reset {
  background-color: transparent;
}

.buttons {
  z-index: 99;
}

/* ══ Selected-row action bar ═══════════════════════════════════════════════
   The strip that appears under a list once rows are ticked, on 140 screens.
   Same mistake as the page action bar and worse here: a .btn-group welds its
   children with a -1px margin and rewrites their radii, and these three are a
   primary, a warning and a danger - three different meanings fused into one
   object. On a phone the labels are long enough that the strip wraps, and the
   welds then land in the middle of a row with the corners inside out.

   It is also sticky with a z-index and no surface, so the buttons floated over
   table rows and the text of both fought for the same pixels. */
nav.buttons.navigation {
  /* The chat launcher is fixed in the bottom-right corner at 58px plus a 24px
     inset, and it sits at z-index 1080 against this bar's 99 - so wherever the
     two meet, the bubble wins and the last action vanishes under it. Raising
     the bar over the bubble would bury the chat button instead, so the bar
     keeps out of that corner rather than fighting for it. */
  --pg-chat-clearance: 5.125rem;
  display: flex;
  justify-content: center;
  margin-top: 1rem;
}
/* No surface of its own: the buttons carry their own shape and a panel behind
   them only adds a box to a page that already has plenty. */
nav.buttons.navigation > .container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: auto;
  padding: 0;
}
/* Centred and short, the bar clears the corner on its own; the cap is for the
   case where enough actions are enabled to grow it that far. */
body:has(#pg-chat-root) nav.buttons.navigation > .container {
  max-width: calc(100% - 2 * var(--pg-chat-clearance));
}
nav.buttons.navigation .btn-group {
  display: contents;
}
/* The mt-1/mb-1 the markup puts on each button is what broke the weld in the
   first place; the gap above spaces them now. */
nav.buttons.navigation .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  margin: 0 !important;
  border-radius: var(--bs-border-radius-pill) !important;
}
nav.buttons.navigation .btn .material-icons,
nav.buttons.navigation .btn .bi {
  margin: 0 !important;
  font-size: 1.125rem;
}

/* Nothing ticked means nothing to do, so the row actions are absent rather than
   greyed. chart_checkbox_state_change() toggles the .disabled class on them as
   the select-all box changes, so this follows the selection for free. A
   permanent row of dead buttons under every list promises an action the page
   cannot perform, and greyed over table rows they were unreadable besides.
   Scoped to .enable-on-selected: a Save button that is disabled for some other
   reason still has to be visible to explain itself. */
nav.buttons.navigation .enable-on-selected .btn.disabled {
  display: none;
}
/* With every action hidden the bar is an empty box; drop its margin so the list
   ends where the list ends. */
nav.buttons.navigation:not(:has(.btn:not(.disabled))) {
  margin-top: 0;
}

/* .btn-secondary is a ghost everywhere else by design, but the bar has no
   surface of its own, so a ghost here floats over table rows with nothing
   behind it. On this bar it gets a fill. */
nav.buttons.navigation .btn-secondary {
  --bs-btn-bg: var(--bs-body-bg);
  --bs-btn-border-color: var(--bs-border-color);
  --bs-btn-color: var(--bs-body-color);
  --bs-btn-hover-bg: var(--pg-tint-hover);
  --bs-btn-hover-border-color: var(--bs-border-color);
  --bs-btn-hover-color: var(--pg-primary-ink);
}

/* One action per row on a phone. Three long labels side by side cannot fit, and
   wrapping them inside a welded group is what made this unreadable. */
@media (max-width: 575.98px) {
  nav.buttons.navigation {
    margin-right: 0.5rem;
    margin-left: 0.5rem;
  }

  nav.buttons.navigation > .container {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }

  nav.buttons.navigation .btn {
    width: 100%;
    padding-top: 0.5rem !important;
    padding-bottom: 0.5rem !important;
  }

  /* Full width leaves no side gutter to keep clear, so the bar stacks above the
     bubble instead of beside it. The inline bottom:.5rem on 140 pages is what
     the !important is for. */
  body:has(#pg-chat-root) nav.buttons.navigation {
    bottom: calc(0.5rem + var(--pg-chat-clearance)) !important;
  }

  body:has(#pg-chat-root) nav.buttons.navigation > .container {
    max-width: 100%;
  }
}

#linklists {
  min-height: 130px;
}

.disabled-checkbox {
  color: var(--pg-primary-ink);
  opacity: 0.5;
  cursor: default;
  -webkit-user-select: none; user-select: none;
}

.border-auto-color {
  border-color: unset !important;
}

.ui-datepicker {
  z-index: 999 !important;
}

.action-buttons {
  width: 70px;
  max-width: 100%;
}

.badge.text-wrap {
  max-width: 100%;
  white-space: normal;
  text-align: unset;
  word-break: break-all;
}

.dropzone_previews {
  border: 2px dashed var(--files-color);
  border-image: none;
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
  padding: 20px 20px;
}

.dropzone .dz-preview.dz-image-preview {
  background: rgba(var(--bs-body-bg-rgb), 10%) !important;
}

.dropzone_previews.design {
  border: 2px dashed var(--design-color);
}

.dz-progress {
  height: 10px !important;
  margin-top: 32px !important;
  opacity: 0.8 !important;
}

.dz-remove {
  opacity: 0;
}

.dz-preview:hover .dz-remove {
  opacity: 1;
}

/* A code field drawn by pg_code_editor_attach(): the facade's editor in a
   textarea's place. Without a height it grows with the text up to most of
   the window and scrolls inside after that, so the buttons below a long
   file stay within reach; F11 fills the window with it. */
.pg-code-field {
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  overflow: hidden;
}

.pg-code-field .cm-editor {
  min-height: 12rem;
  max-height: 80vh;
}

/* A read-only field (a command to copy, a source to read) is as tall as its
   text: a one-line cron command is one line. */
.pg-code-field.pg-code-field-ro .cm-editor {
  min-height: 0;
}

.pg-code-field.pg-code-field-full .cm-editor {
  max-height: none;
}

.pg-code-field.pg-code-field-full {
  position: fixed;
  inset: 0;
  z-index: 1080;
  height: auto !important;
  border: 0;
  border-radius: 0;
}

body.pg-code-field-open {
  overflow: hidden;
}

table.chart .text-truncate.overflow-hidden * {
  text-overflow: ellipsis;
  overflow: hidden;
}

table.dataTable.dt-checkboxes-select tbody tr,
table.dataTable thead th.dt-checkboxes-select-all,
table.dataTable tbody td.dt-checkboxes-cell {
  cursor: pointer;
}

table.dataTable thead th.dt-checkboxes-select-all,
table.dataTable tbody td.dt-checkboxes-cell {
  text-align: center;
}

div.dataTables_wrapper span.select-info,
div.dataTables_wrapper span.select-item {
  margin-left: 0.5em;
}

@media screen and (max-width: 640px) {
  div.dataTables_wrapper span.select-info,
  div.dataTables_wrapper span.select-item {
    margin-left: 0;
    display: block;
  }
}

table.dataTable > tbody > tr:not(.selected) .fakeselectallcheckbox:before {
  content: "\f28a";
}
table.dataTable > tbody > tr.selected .fakeselectallcheckbox:before {
  content: "\f26b";
}
table.dataTable > tbody > tr:not(.selected) .fakeselectallcheckbox {
  visibility: hidden;
}
table.dataTable > tbody > tr:hover .fakeselectallcheckbox {
  visibility: visible;
}
table.table.dataTable.table-striped
  > tbody
  > tr:nth-of-type(2n + 1):not(.selected)
  > .sorting_1 {
  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.07);
}
table.table.dataTable > :not(caption) > *:not(.selected) > .sorting_1 {
  box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.02);
}
table.table.dataTable > tbody > tr.selected > * {
  color: rgb(255, 255, 255);
  color: rgb(var(--dt-row-selected-text));
}

.object-fit,
.img-thumbnail {
  object-fit: cover;
}

.object-fit {
  width: 100%;
  height: 100%;
}

.advanced-filter-body {
  height: calc(100% - 47px);
  overflow-x: hidden;
  overflow-y: auto;
  margin-top: 0;
  background-color: rgba(var(--bs-body-bg-rgb), 100%);
  border-left: var(--bs-border-width) var(--bs-border-style)
    var(--bs-border-color);
  border-right: var(--bs-border-width) var(--bs-border-style)
    var(--bs-border-color);
  border-bottom: var(--bs-border-width) var(--bs-border-style)
    var(--bs-border-color);
}

@media only screen and (min-width: 1200px) {
  .advanced-filter-bar {
    top: var(--pg-navbar-h);
    height: calc(100vh - 56px);
    width: 300px;
    overflow: auto;
    position: sticky;
    z-index: 999;
    float: right;
    display: block;
    right:6px;
  }
}

.table.dataTable {
  margin-bottom: 0px !important;
}
.table,
.dataTables_wrapper + .navigation.buttons {
  margin-top: 3rem;
  margin-bottom: 2rem;
}
a.dt-button {
  position: relative;
}
.dt-button-collection.fixed.two-column .dropdown-menu {
  padding: 1rem;
  height: 300px;
  overflow-x: auto;
}

.dt-button-collection.fixed.two-column
  .dropdown-menu
  .buttons-columnVisibility
  span {
  padding-right: 1rem;
}
.btn-data-control {
  padding: 0 4px;
}

.btn-group-sm > .btn,
.btn-sm,
.btn {
  --bs-btn-border-radius: var(--bs-border-radius-lg);
}
.dropdown-menu {
  --bs-dropdown-border-radius: var(--bs-border-radius-lg);
}
.pagination {
  --bs-pagination-border-radius: var(--bs-border-radius-lg);
}

.software_notice,
.software_error,
.software_warning,
.modal-content,
.cke_chrome,
:root[data-bs-theme="dark"] div.dropdown-menu.dt-button-collection.fixed,
.dropzone_previews,
.advanced-filter-bar,
.input-group-sm > .btn,
.input-group-sm > .form-control,
.input-group-sm > .form-select,
.input-group-sm > .input-group-text,
.btn-group,
.popover,
.form-select,
.form-control,
.tagin-tag,
.input-group-text,
.select2-container--bootstrap-5 .select2-selection,
.select2-container--bootstrap-5
  .select2-selection--multiple
  .select2-selection__rendered
  .select2-selection__choice {
  border-radius: var(--bs-border-radius-lg);
}
.modal-header,
.popover .popover-header,
.advanced-filter-bar > *:first-child {
  border-top-left-radius: var(--bs-border-radius-lg);
  border-top-right-radius: var(--bs-border-radius-lg);
}
.modal-footer,
.popover .popover-body,
.advanced-filter-bar > *:last-child {
  border-bottom-left-radius: var(--bs-border-radius-lg);
  border-bottom-right-radius: var(--bs-border-radius-lg);
}
.no-arrow.dropdown-toggle::after,
.dropend.no-arrow .dropdown-toggle::after,
.dropup.no-arrow .dropdown-toggle::after,
.dropstart.no-arrow .dropdown-toggle::after,
.dropdown.no-arrow .dropdown-toggle::after,
.dropend.no-arrow .dropdown-toggle::before,
.dropup.no-arrow .dropdown-toggle::before,
.dropstart.no-arrow .dropdown-toggle::before,
.dropdown.no-arrow .dropdown-toggle::before {
  display: none;
}

.form-check-input.danger:checked {
  background-color: var(--bs-danger);
  border-color: var(--bs-danger);
}

.form-switch .form-check-input.danger {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23dc3545'/%3e%3c/svg%3e");
}

.form-switch .form-check-input.danger:checked {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%231a1c1e'/%3e%3c/svg%3e");
}
.show-on-hovered:not(:checked) {
  visibility: hidden;
}
.card.hoverable:hover {
  outline: 2px dotted;
}
.hoverable:hover .show-on-hovered {
  visibility: visible;
}

.monthly {
  overflow: auto;
}

.software_calendar .monthly {
  background-color: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
}
.calendar .software_calendar .monthly {
  margin-bottom: 3rem;
}
table.monthly_calendar {
  border-collapse: collapse;
  margin: 0 auto;
  max-width: 100%;
  padding: 0;
  border-color: inherit;
  background-color: inherit;
}

table.monthly_calendar tbody {
  border-color: inherit;
  background-color: inherit;
}

table.monthly_calendar td,
table.monthly_calendar th {
  line-height: 1em;
  padding: 1em;
  vertical-align: top;
  min-width: 100px;
  width: 200px;
}

table.monthly_calendar th {
  text-align: center;
}

table.monthly_calendar td:not(.inactive) {
  background-color: var(--bs-body-bg);
  border: var(--bs-border-width) solid;
  border-color: inherit;
  height: 100px;
}

table.monthly_calendar td.inactive {
  background-color: inherit;
  height: 100px;
}

table.monthly_calendar tr.heading {
  background-color: inherit;
}

table.monthly_calendar .calendar-day-wrapper {
  font-size: 20px;
}

table.monthly_calendar .calendar-day-wrapper span.today {
  background-color: #ffe1ce;
  width: 30px;
  height: 30px;
  display: inline-block;
  line-height: 30px;
  text-align: center;
  border-radius: 50%;
}

.popover{
  --bs-popover-bg:var(--bs-body-bg);
}

.popover .popover{
  --bs-popover-bg:var(--pg-card-bg);
}

.popover .popover .popover{
  --bs-popover-bg:var(--bs-body-bg);
}

table.monthly_calendar td.today {
  border: 2px solid var(--calendars-color);
}

table.monthly_calendar
.calendar-event
  a[href*="edit_calendar_event.php"]:before {
  font-family: "Material Icons";
  font-weight: normal;
  font-style: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-smoothing: antialiased;
  content: "event";
  padding-right: 5px;
  vertical-align: middle;
  content: "event_note";
}

table.monthly_calendar .calendar-event a {
  color: #4c4c4c;
  text-decoration: none;
  white-space: nowrap;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: inline-block;
}

table.monthly_calendar .calendar-event a:hover {
  text-decoration: underline;
}

table.chart th,
table.chart td {
  white-space: nowrap;
}

table.dataTable td.dataTables_empty,
table.dataTable th.dataTables_empty {
  text-align: left;
}

.dataTables_wrapper .dtable-button-container,
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate,
.dataTables_wrapper .dataTables_filter {
  padding: 1rem;
}

.advanced_filters .header {
  height: 47px;
}

.tagin-input:not(.tagin-input-hidden),
.tagin-wrapper.focus .tagin-input:not(.tagin-input-hidden),
.tagin-wrapper.focus,
.tagin-tag {
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
}


html[data-bs-theme="dark"] table.monthly_calendar td:not(.inactive) {
  background-color: #00000025;
}

html[data-bs-theme="dark"] table.monthly_calendar td.today {
  border: 2px solid rgb(255, 148, 116);
}

html[data-bs-theme="dark"]
  table.monthly_calendar
  .calendar-day-wrapper
  span.today {
  background: #231c17;
}

html[data-bs-theme="dark"] table.monthly_calendar .calendar-event a {
  color: #c3c3c3;
}

html[data-bs-theme="dark"] #product_group_tree ul {
  border-left: 1px solid #0000006c;
}

html[data-bs-theme="dark"] #product_group_tree span.row:before {
  border-bottom: 1px solid #0000006c;
}

html[data-bs-theme="dark"] .select2-search,
html[data-bs-theme="dark"] .select2-search input,
html[data-bs-theme="dark"] .select2-results {
  background-color: var(--bs-body-bg);
}

html[data-bs-theme="dark"] .select2-container--bootstrap-5 .select2-selection {
  color: var(--bs-secondary-color);
  background-color: var(--bs-body-bg);
  border: var(--bs-border-width) solid var(--bs-border-color);
}

html[data-bs-theme="dark"]
  .select2-container--bootstrap-5
  .select2-selection--single
  .select2-selection__rendered,
html[data-bs-theme="dark"]
  .select2-container--bootstrap-5
  .select2-selection--multiple
  .select2-selection__rendered
  .select2-selection__choice {
  color: #ffffff;
}

html[data-bs-theme="dark"] .cke_bottom {
  border-top: unset !important;
  box-shadow: unset !important;
  background: #1e1e23 !important;
  background-image: unset !important;
}

html[data-bs-theme="dark"] .cke_dialog_title {
  background: #1f1f1f !important;
  color: white !important;
}

html[data-bs-theme="dark"] .cke_chrome {
  border: 1px solid #282828 !important;
}

html[data-bs-theme="dark"] .cke_path_item,
html[data-bs-theme="dark"] .cke_path_empty {
  color: #ffffff !important;
  text-shadow: 0 1px 0 #000 !important;
}

html[data-bs-theme="dark"] .cke .cke_top {
  background: #e1e1dc;
  filter: invert(1);
}

html[data-bs-theme="dark"] .cke_inner {
  background: #000 !important;
}

html[data-bs-theme="dark"] .doka-image-editor {
  --color-background: 0, 0, 0;
  --color-foreground: 255, 255, 255;
}

div#software_image_picker_container {
  min-height: 40px;
}

.dt-paging ul.pagination {
  justify-content: center;
  margin-top: 1rem !important;
  margin-bottom: 1rem !important;
}

.advanced-visuals:not(.toolbar) .dropdown-menu.backdrop,
.advanced-visuals:not(.toolbar) .popover.backdrop {
  --bs-dropdown-bg: rgba(
    var(--bs-body-bg-rgb),
    var(--bs-bg-opacity)
  ) !important;
  --bs-popover-bg: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;
  --bs-bg-opacity: 0.6;
  -webkit-backdrop-filter: brightness(1) saturate(1.5) blur(20px); backdrop-filter: brightness(1) saturate(1.5) blur(20px);
}


[data-bs-theme="dark"] .advanced-visuals:not(.toolbar) .dropdown-menu.backdrop,
[data-bs-theme="dark"] .advanced-visuals:not(.toolbar) .popover.backdrop {
  -webkit-backdrop-filter: brightness(0.7) saturate(1.7) blur(100px); backdrop-filter: brightness(0.7) saturate(1.7) blur(100px);
}

.list-group.order-list-group {
  border-radius: var(--bs-border-radius-xxl);
  --bs-list-group-border-width: 2px;
  --bs-list-group-item-padding-x: 1rem;
  --bs-list-group-item-padding-y: 1rem;
}

@media print {

  .list-group.order-list-group {
    --bs-list-group-border-width: 0px;
  }
  a{
    text-decoration: none !important;
  }
}

/* ══ Dashboard widget grid ═════════════════════════════════════════════════
   Column classes tie the card size to breakpoints, so it jumps rather than
   scales: at 1399px the row held four cards of ~311px, at 1400px six of ~199px,
   and past 1920px those same six grew to 400px and beyond. auto-fill with a
   minimum fits as many tracks as the width allows and lets them share what is
   left, which keeps the card inside one narrow band at every width instead of
   stepping between two bad ones.

   The minimum is the only knob: raise it for fewer, larger cards. */
.pg-widgets {
  /* 15.5rem, not 16.5: the tracks stretch to fill whatever the column count
     leaves over, so a minimum that just misses fitting one more column is what
     produces an oversized card. At 831px of content 16.5rem fitted two tracks
     and stretched them to 404px; 15.5rem fits three at 261px. Across 520px to
     3400px of content the card now stays between 248px and 382px, against
     264px to 548px before. */
  --pg-widget-min: 15.5rem;
  --pg-widget-wide-ratio: 2.08;
  display: grid;
  /* min() so a viewport narrower than the minimum still gets one full-width
     track rather than a track that overflows it. */
  grid-template-columns: repeat(auto-fill, minmax(min(var(--pg-widget-min), 100%), 1fr));
  /* dense, because a two-track widget that meets a single free track at the end
     of a row leaves that track empty and drops to the next one. Dense pulls a
     later one-track widget up into the hole instead. The cost is that visual
     order can differ from DOM order in exactly that case, which is also the
     order drag-to-reorder saves. */
  grid-auto-flow: row dense;
  gap: 2rem;
  /* So the double-width rule below can ask how wide the grid is rather than how
     wide the window is. The two disagree whenever the rail is expanded, and a
     span of two granted while only one track fits pushes the grid off screen. */
  container-type: inline-size;
  container-name: pgwidgets;
}

/* Opt-in double width. Add .pg-widget-wide beside .pg-widget on the wrapper.
   Guarded at two tracks plus the gap, because a span of two in a single-track
   grid conjures an implicit second column and pushes the whole grid off screen.
   Where container queries are missing the rule is skipped and the widget simply
   stays square, which is the right thing to fall back to. */
@container pgwidgets (min-width: 32.5rem) {
  .pg-widget-wide {
    grid-column: span 2;
  }

  /* The card must not square its own doubled width - that is what produced the
     tall card with the empty half. A wide card is two tracks plus the gap
     across and one track down, so its ratio is 2 + gap/track. The track is
     260-370px in practice, which puts the term between 0.065 and 0.092, and
     2.08 lands within three pixels of a square row anywhere in that range.

     Deliberately a fixed ratio rather than height:100%: with no ratio the card
     contributes its own content height to the auto row, which is exactly how a
     396px card ended up in a 286px row. */
  .pg-widget-wide > .card.widget {
    aspect-ratio: var(--pg-widget-wide-ratio);
  }
}

/* Each card is its own query container, so a body that can lay out as two
   panels asks how wide the card is rather than how wide the window is. A wide
   widget in a narrow grid and a normal widget on a huge screen then both get
   the right answer. */
.card.widget {
  container-type: inline-size;
  container-name: pgwidget;
}

/* Two panels in one card, and the scrolling changes with the arrangement.
   Stacked, they read as one column: the card scrolls once and each panel takes
   the height its content needs. Two scrollbars down a phone-width card meant the
   lower panel was squeezed to its heading with its list out of reach, and a
   pane you have to find before you can scroll it is not a list you read.
   Side by side there is no single reading order, so each column scrolls on its
   own and the card itself does not. */
.pg-split {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
}
.pg-split > .pg-split-half {
  min-width: 0;
}
.pg-split > .pg-split-half + .pg-split-half {
  border-top: var(--bs-border-width) solid var(--pg-rule);
}
@container pgwidget (min-width: 30rem) {
  .pg-split {
    flex-direction: row;
    overflow: hidden;
  }

  .pg-split > .pg-split-half {
    flex: 1 1 0;
    min-height: 0;
    overflow-x: hidden;
    overflow-y: auto;
  }

  .pg-split > .pg-split-half + .pg-split-half {
    border-top: 0;
    border-left: var(--bs-border-width) solid var(--pg-rule);
  }

  /* A panel with a headline above a list scrolls the list, not itself.
     Otherwise the first thing to leave the card is the thing the panel is
     about -- the System Status gauge slid out of view behind its own chips,
     and the heading naming the list went with it, so what remained was a
     column of unlabelled pills.

     Only side by side. Stacked, the card is one column with one reading order
     and one scrollbar, and pinning anything inside it would put a second
     scroller in the middle of that column. That is why this lives in the
     container query rather than beside it. */
  .pg-split > .pg-split-half.pg-split-pinned {
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  /* min-height:0 is what actually makes it scroll: a flex item's minimum is
     its content, so without this the list stretches the panel instead of
     overflowing inside it. */
  .pg-split-pinned > .pg-split-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-x: hidden;
    overflow-y: auto;
  }
}

.card.widget {
  aspect-ratio: 1/1;
  --bs-card-border-radius: var(--bs-border-radius-lg);
  --bs-card-inner-border-radius: calc(var(--bs-border-radius-xxl) - (var(--bs-border-width)));
  --bs-card-cap-padding-y: 0.5rem;
  --bs-card-cap-padding-x: 0.5rem;
  --bs-bg-opacity: 1;
  overflow: hidden;
}

.card.widget .card-header {
  text-align: center;
}
.card.widget .card-body {
  position: relative;
}
.ui-draggable-handle,
.card.widget .ui-sortable-handle {
  cursor: grab;
}

.card.widget #editor {
  color: inherit;
  height: inherit;
  overflow: auto;
  width: 100%;
  padding: 0;
  outline: none;
}

.card #editor.ql-container,
.card .ql-toolbar.ql-snow {
  border: none;
}

.card .ql-toolbar ~ #editor.ql-container.ql-snow {
  height: 182px;
}

/* Calendar */
.widget table.monthly_calendar td,
.widget table.monthly_calendar th {
  line-height: 20px;
  padding: 5px;
  vertical-align: top;
  min-width: 30px;
  width: 36px;
}

.widget table.monthly_calendar td:not(.inactive),
.widget table.monthly_calendar td.inactive {
  aspect-ratio: 1/1;
  position: relative;
  overflow: hidden;
  border-radius: var(--bs-border-radius-sm);
  white-space: nowrap;
  height: 33px;
}

.widget table.monthly_calendar th {
  max-width: 33px !important;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 10px;
}


.widget table.monthly_calendar span.today,
.widget table.monthly_calendar tr.heading,
.widget .software_calendar .monthly {
  background-color: unset !important;
}

.widget table.monthly_calendar td.inactive {
  background-color: #e3e3e302;
}
.widget table.monthly_calendar td:not(.inactive) {
  background-color: #adadad0d;
}


.widget table.monthly_calendar .calendar-day-wrapper {
  text-align: center !important;
  margin-top: 2px;
  margin-bottom: 0 !important;
}

.widget span.calendar-events {
  display: flex;
  position:absolute;
  top: 0;
  left: 2px;
}

.widget table.monthly_calendar .calendar-day-wrapper span.today {
  background: #e3e3e300;
  width: 20px;
  height: 20px;
  display: inline-block;
  line-height: 20px;
}


.this-after-top-30::after {
  top: 30px;
}

/* Widget specific */
/* Taller than the three-panel version it replaced: that one kept its top-page
   line inside each panel, this one has a strip of period cells below a
   full-width chart, which is a row the old layout did not have. Measured
   against the content rather than guessed -- at 350px the strip was clipped. */
.widget[widget-id="5"] {
  --pg-accent: #3b82f6; 
  --pg-accent-ink: #18181b;
}
.widget[widget-id="5"] .card-body {
  margin: 0 !important;
}

.card.widget .card-body .row.pointer:hover {
  background: var(--pg-tint);
}
.card.widget .card-body .row.pointer:not(:first-child):not(:last-child) {
  border-bottom: var(--bs-border-width) solid var(--pg-rule);
}
.card.widget .card-body .row.pointer:first-child,
.card.widget .card-body .row.pointer:nth-child(2) {
  border-top: var(--bs-border-width) solid var(--pg-rule);
}
.card.widget .card-body .row.pointer:last-child {
  margin-bottom: 1rem !important;
}

/* Dashboard list row ────────────────────────────────────────────────────────
   One row shape for every widget that renders a list, so the cards read as one
   surface instead of a dozen dialects. The tile takes its colour from
   --pg-accent, which each widget already declares for its header icon, so
   api.php never has to know a colour. Geometry lives here rather than in the
   markup api.php returns, so the loading skeletons in welcome.php can reuse
   the same boxes and the content does not slide into place when data lands. */
.pg-list {
  padding: 0.25rem 0.5rem 0;
}
.pg-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 0.25rem;
  padding: 0.5rem;
  border-radius: var(--bs-border-radius-lg);
  background: var(--pg-tint);
  color: inherit;
  text-decoration: none;
}
.pg-row:hover,
.pg-row:focus-visible {
  background: var(--pg-tint-hover);
  color: inherit;
}
.pg-row-badge {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  width: 34px;
  height: 34px;
  border-radius: var(--bs-border-radius-lg);
  background: var(--pg-accent, #64748b);
  /* The glyph is white on a dark tile and near-black on a light one. Several
     accents (amber, emerald, sky) are far too light to carry white: the tile
     content is sometimes a person's initials or a currency symbol, which is
     text and wants 4.5:1, and white on #f59e0b is 2.15:1. Each widget declares
     whichever of the two reads better against its own accent. */
  color: var(--pg-accent-ink, #fff);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
}
/* Product thumbnails fill the tile instead of sitting on the accent. */
.pg-row-badge img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pg-row-text {
  flex: 1 1 auto;
  min-width: 0;
}
/* min-width:0 on the name is what lets it ellipsis instead of shoving the
   trailing value out of the card -- a flex item defaults to min-width:auto. */
.pg-row-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
}
.pg-row-name {
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
}
.pg-row-meta {
  font-size: 11px;
}
.pg-row-name,
.pg-row-meta {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pg-row-aside {
  flex: 0 0 auto;
  font-size: 11px;
}
.pg-row-go {
  flex: 0 0 auto;
  font-size: 14px;
}
/* Compact variant. Widgets 6, 15 and 3 list one line per entry and fit ten of
   them in a card; giving those the full row would show four. Same shape --
   rounded, tinted, accent tile, arrow -- at half the height. */
.pg-row-sm {
  gap: 8px;
  margin-bottom: 0.125rem;
  padding: 0.25rem 0.375rem;
}
.pg-row-sm .pg-row-badge {
  width: 22px;
  height: 22px;
  border-radius: var(--bs-border-radius);
  font-size: 11px;
}
.pg-row-sm .pg-row-name {
  font-size: 12px;
  font-weight: 500;
}
.pg-row-sm .pg-row-go {
  font-size: 12px;
}
/* Online engagement counters. Two stacked rows instead of two tall centred
   tiles: the icon, the figure, what it counts and the window it counts over all
   sit on one line, which is 26px a row against the 95px the pair used to take
   out of the panel that holds the conversations. */
.pg-eg-stat {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 8px;
  border: var(--bs-border-width) solid color-mix(in srgb, var(--pg-eg-ink) 22%, transparent);
  border-radius: var(--bs-border-radius);
  background: color-mix(in srgb, var(--pg-eg-ink) 8%, transparent);
  line-height: 1.2;
}
.pg-eg-stat .bi {
  flex: 0 0 auto;
  color: var(--pg-eg-ink);
  font-size: 15px;
}
.pg-eg-stat .bi::before {
  vertical-align: 0;
}
.pg-eg-stat-value {
  flex: 0 0 auto;
  font-size: 15px;
  font-weight: 700;
}
.pg-eg-stat-label {
  overflow: hidden;
  min-width: 0;
  font-size: 11px;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pg-eg-stat-window {
  flex: 0 0 auto;
  margin-left: auto;
  opacity: 0.6;
  font-size: 9px;
  white-space: nowrap;
}

/* Section heading inside a list -- "Top Pages", "Expiring Soon". */
.pg-row-heading {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0.25rem 0.375rem;
  font-size: 11px;
  font-weight: 600;
}

/* One control is allowed in a widget header, flush right and with no surface of
   its own, so the header still reads as a label rather than a toolbar. The
   header is already position:relative, and the AJAX refresh only replaces the
   body, so the button survives every poll. */
.widget .card-header .pg-widget-action {
  position: absolute;
  top: 50%;
  right: 0.5rem;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: var(--bs-border-radius);
  background: transparent;
  color: inherit;
  opacity: 0.65;
  font-size: 0.875rem;
  line-height: 1;
}
.widget .card-header .pg-widget-action .bi::before {
  vertical-align: 0;
  line-height: 1;
}
/* Colour alone marks the hover: a fill here would be the background the header
   is meant not to have. */
.widget .card-header .pg-widget-action:hover,
.widget .card-header .pg-widget-action:focus-visible {
  background: transparent;
  color: var(--pg-primary-ink);
  opacity: 1;
}

/* A form inside a left-menu context popover. The popover body pads its own
   links; a form needs the padding on itself and a width that does not collapse
   onto the number input. */
.contextmenu .pg-ctx-form {
  min-width: 12.5rem;
  padding: 0.25rem 0.5rem 0.375rem;
}
.pg-row-heading .pg-row-aside {
  margin-left: auto;
  font-weight: 400;
}

/* List widget headline ──────────────────────────────────────────────────────
   Today's count, which way it moved, the seven day total and a sparkline, on
   one line above a list. One line and not a block: the widgets that carry this
   have lists of 52px rows, and the taller headline the shipments card uses
   would cost two of them. */
.pg-head {
  /* A rule under it, because what follows is a list or a tab bar and without
     one the sparkline's tail and the first row below it read as the same
     block. */
  margin-bottom: 4px;
  padding: 6px 12px 5px;
  border-bottom: var(--bs-border-width) solid var(--pg-rule);
}
.pg-head-line {
  display: flex;
  align-items: baseline;
  gap: 5px;
}
.pg-head-num {
  font-size: 22px;
  font-weight: 700;
  line-height: 1;
}
.pg-head-unit {
  font-size: 10px;
}
.pg-head-dir {
  font-size: 9px;
}
/* Pushed to the far end rather than following the unit, so the eye finds the
   period total in the same place on every widget. */
.pg-head-total {
  margin-left: auto;
  font-size: 10px;
}
/* The box owns the height and the canvas fills it. Sizing the canvas itself
   let Chart.js measure the padded parent instead and the line spilled out
   under the tabs of the widget below it. */
.pg-head-spark {
  position: relative;
  display: block;
  width: 100%;
  height: 20px;
  margin-top: 2px;
}
.pg-head-spark canvas {
  display: block;
}

/* Visitor summaries widget (widget-id 5) ────────────────────────────────────
   One chart with the current period drawn over the previous one, and a strip
   of periods underneath that promote themselves into it when clicked. */
/* The three bands share one column and the chart takes what the other two do
   not need, so the whole widget fits the card instead of scrolling: at 288px of
   body the stacked version needed 352px and clipped the week/month strip out of
   sight entirely. */
.card-body:has(> .pg-vs) {
  display: flex;
  flex-direction: column;
}
.pg-vs {
  --pg-vs-accent: #3b82f6;
  --pg-vs-accent-rgb: 59, 130, 246;
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
  padding: 8px 14px 6px;
}
.pg-vs-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex: 0 0 auto;
  gap: 16px;
}
/* Two rows of two, in DOM order: label beside figure, then the two comparisons
   beside each other. Four stacked lines cost 65px of a 211px body; a grid puts
   the same four blocks in 42px without dropping any of them. Flex-wrap cannot
   do it - there is no way to force a break after the second item. */
.pg-vs-head > div:first-child {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  min-width: 0;
  column-gap: 10px;
  row-gap: 1px;
}
.pg-vs-eyebrow {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.7;
}
.pg-vs-big {
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-size: 22px;
  font-weight: 700;
  line-height: 1.15;
  color: var(--pg-vs-accent);
}
.pg-vs-unit {
  font-size: 11px;
  font-weight: 500;
  opacity: 0.7;
}
.pg-vs-compare {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 0;
  min-height: 16px;
}
/* The badge carries the direction, the sentence beside it says what the
   comparison is against. "dün 119" on its own never said which 119. */
.pg-vs-pill {
  padding: 2px 7px;
  border-radius: var(--bs-border-radius-pill);
  font-size: 10px;
  font-weight: 700;
  line-height: 1.5;
}
.pg-vs-pill.up {
  background: rgba(16, 185, 129, 0.16);
  color: #10b981;
}
.pg-vs-pill.down {
  background: rgba(248, 113, 113, 0.16);
  color: #f87171;
}
.pg-vs-cmp {
  font-size: 11.5px;
}
.pg-vs-sub {
  margin-top: 0;
  font-size: 10px;
  opacity: 0.7;
  min-height: 16px;
}
.pg-vs-legend {
  display: flex;
  gap: 14px;
  padding-top: 4px;
  font-size: 11px;
  opacity: 0.75;
}
.pg-vs-legend.no-prev .pg-vs-key:last-child {
  display: none;
}
.pg-vs-key {
  display: flex;
  align-items: center;
  gap: 6px;
}
.pg-vs-sw {
  display: inline-block;
  width: 16px;
  height: 0;
  border-top: 2px solid var(--pg-vs-accent);
}
.pg-vs-sw.prev {
  border-top: 2px dashed #94a3b8;
}
/* The elastic band. It gives up height first, down to a floor where the shape
   of the line is still readable, so the strip below always lands on screen. */
.pg-vs-chart {
  position: relative;
  flex: 1 1 auto;
  min-height: 4rem;
  margin-top: 2px;
}
.pg-vs-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)) 1.45fr;
  flex: 0 0 auto;
  gap: 6px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: var(--bs-border-width) solid var(--pg-rule-strong);
}
/* Four periods across a narrow card leave each one too thin to read its own
   number, so they pair up instead. The chart pays for the second row. */
@container pgwidget (max-width: 26rem) {
  .pg-vs-strip {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.pg-vs-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  overflow: hidden;
  padding: 4px 8px;
  line-height: 1.2;
  border: var(--bs-border-width) solid transparent;
  border-radius: var(--bs-border-radius-lg);
  background: var(--pg-tint);
  color: inherit;
  font: inherit;
  text-align: left;
}
button.pg-vs-cell {
  cursor: pointer;
}
button.pg-vs-cell:hover,
button.pg-vs-cell:focus-visible {
  background: var(--pg-tint-strong);
}
/* The cell whose period is currently in the big chart. */
button.pg-vs-cell.is-active {
  border-color: rgba(var(--pg-vs-accent-rgb), 0.55);
  background: rgba(var(--pg-vs-accent-rgb), 0.1);
}
.pg-vs-cell-label {
  font-size: 10px;
  opacity: 0.7;
}
.pg-vs-cell-value {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: 14px;
  font-weight: 700;
}
.pg-vs-spark {
  position: absolute;
  right: 9px;
  bottom: 7px;
  display: block;
  width: 72px;
  height: 22px;
  opacity: 0.85;
}
.pg-vs-spark canvas {
  display: block;
}
.pg-vs-top {
  gap: 1px;
}
.pg-vs-page {
  overflow: hidden;
  max-width: 100%;
  font-size: 12px;
  font-weight: 600;
  color: var(--pg-vs-accent);
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pg-vs-views {
  font-size: 10px;
  opacity: 0.7;
}
/* Today's busiest page under the period's. Two different questions, so a rule
   between them rather than a fourth line of the same block. */
.pg-vs-today {
  width: 100%;
  margin-top: 1px;
  padding-top: 2px;
  border-top: var(--bs-border-width) dashed var(--pg-rule-dashed);
  overflow: hidden;
  font-size: 10px;
  opacity: 0.75;
  white-space: nowrap;
  text-overflow: ellipsis;
}
html[data-bs-theme="dark"] button.pg-vs-cell.is-active {
  background: rgba(var(--pg-vs-accent-rgb), 0.16);
}

/* System status widget (widget-id 24) ───────────────────────────────────────
   A gauge over a grid of one tile per check. The arc is a stroked circle with
   a dash gap rather than a path: the filled length is then just a dasharray,
   so api.php sets one number instead of computing arc endpoints in PHP. */
/* The top padding is for the glow, not for the arc. The two blurred copies
   under the bar reach about nine user units past it -- some fourteen pixels at
   the size the dial is drawn -- and the panel around it clips (it has to: the
   list below it is what scrolls). At six pixels the arc came out of the card
   header with its own light cut off square, which reads as the gauge being too
   big for the card rather than as a clipped shadow. */
.pg-health {
  position: relative;
  padding: 0.875rem 1rem 0.125rem;
  text-align: center;
}
.pg-health-gauge circle,
.pg-health-gauge {
  overflow: visible;
}
/* The gauge, the matrix inside it and the readout over it are one object, so
   they share one box. The percentages the matrix is placed with only hold
   against a box whose shape is the SVG's -- 124 by 72 user units, fixed by the
   viewBox -- which is why the width cap moved here from the SVG itself. */
.pg-health-dial {
  position: relative;
  width: 100%;
  /* The one thing on this half of the card that is read from across a desk.
     The room comes from the chips below, which gave up a point of type and a
     couple of pixels of padding each and lost nothing -- they are read one at
     a time, up close. */
  max-width: 200px;
  margin: 0 auto;
}
.pg-health-gauge {
  display: block;
  width: 100%;
}
/* No transform here any more. The half turn that puts the drawn half on top is
   folded into the coordinates api.php writes, because a clip on a transformed
   element is resolved in the space the element was WRITTEN in, not the space it
   ends up in -- and the squares are clipped by the bar now. The gradient is
   mirrored there for the same reason. */
.pg-health-gauge circle {
  fill: none;
  stroke-linecap: round;
  /* A circle path starts at 3 o'clock and runs clockwise, so half a turn puts
     the start of the dash at 9 o'clock and the drawn half over the top. The
     undrawn half is the opening the score sits in. */
  transform: rotate(180deg);
  transform-origin: 70px 70px;
}
/* The unfilled half is a groove, not a second arc, and it is drawn ONLY on the
   unfilled half -- api.php walks the dash past the fill rather than laying a
   full half turn down and painting over it. Under the fill it was doing nothing
   on a dark card and real harm on a light one; see the note there.
   ---------------------------------------------------------------------------
   It was currentColor, which on a dark card is the body text colour -- so the
   empty half came out a pale grey band running right beside the neon, and the
   eye read the two as one object of two colours. That is what took the life out
   of the gradient: nothing looks saturated next to a light grey of the same
   width.

   So it goes the other way from the ink in each theme. Black on a dark card,
   which reads as a channel cut into it; the theme's own pale grey on a light
   one, where black at any strength would be the loudest thing in the widget.
   Either way it recedes and the bar is the only thing with colour in it. */
.pg-health-track {
  stroke: #000000;
  opacity: 0.5;
}
/* Two blurred copies under the sharp arc, at two radii. One alone draws an
   outline around the bar; the pair gives the falloff that reads as light coming
   off it. Both stay well under 1 -- a glow at full strength stops being a glow
   and becomes a second, fatter arc.

   Down from .85 and .5. These sit directly UNDER the squares, and a blurred
   copy of the bar is at nearly full strength along the middle of the bar --
   which is exactly where the squares are. Whatever light is under a square is
   light that stays lit while the square dims, so the twinkle was being read
   against a lamp rather than against the card. */
/* Each carries the two ends of its own breath as well as the value it rests
   at. The still value is what a reduced-motion visitor sees, and it sits
   between the two rather than at either end. */
.pg-health-glow {
  --pg-glow-lo: 0.52;
  --pg-glow-hi: 0.72;
  opacity: 0.62;
}
.pg-health-bloom {
  --pg-glow-lo: 0.31;
  --pg-glow-hi: 0.5;
  opacity: 0.4;
}
html[data-bs-theme="light"] .pg-health-track {
  stroke: var(--bs-secondary-bg);
  opacity: 1;
}

/* Light theme: the bar has to be drawn, not lit.
   ---------------------------------------------------------------------------
   Every one of these is the same trade in a different place. On a dark card
   the arc is read as LIGHT -- blurred copies, a wash under the cells, a cell
   that can fade almost to nothing and still be legible against black. On white
   there is nothing for light to spill into: each of those turns to haze, the
   haze fills the gaps between the cells, and what was a matrix becomes a soft
   blue smudge with no texture and no hue travel left in it.

   So on white the same bar is drawn rather than lit: no bloom at all, a
   quarter of the glow, a wash thin enough that the gaps read as page, and a
   cell that never fades below half. */
html[data-bs-theme="light"] .pg-health-bloom {
  /* Not merely faint -- absent. A wide blur on white is fog whatever its
     opacity, and it is fog sitting exactly where the four hues have to be
     told apart. */
  display: none;
}
html[data-bs-theme="light"] .pg-health-glow {
  --pg-glow-lo: 0.11;
  --pg-glow-hi: 0.21;
  opacity: 0.16;
}
/* A cell at a tenth over a white page is gone. The floor comes up so the cell
   is always there and it is the BRIGHTNESS that varies, which is what the
   twinkling is; below that it stops being a twinkle and becomes cells
   disappearing and coming back. Still well under the half it was: at a half,
   the darkest a square ever got was halfway to its brightest, and half a step
   is not a step anyone sees. */
html[data-bs-theme="light"] .pg-health-cells {
  --pg-cell-lo: 0.42;
}
/* Two units of solid colour beside a textured ten. On black it is the one
   place the hue is read undiluted; on white it is a hard line crossing the
   gauge, and the eye takes it for the real bar. */
html[data-bs-theme="light"] .pg-health-cell-glow {
  opacity: 0.15;
}
html[data-bs-theme="light"] .pg-health-inner {
  opacity: 0.5;
}
/* The readout sits over the middle of the arc rather than after it, so the
   number and the fill are read as one object. */
/* The readout sits over the middle of the arc rather than after it, so the
   number and the fill are read as one object. A little above the arc's own
   centre -- which is 86% of the box, (70-8)/72 of the viewBox -- so that the
   caption under the number finishes inside the dial instead of reaching down
   over the heading below it. */
.pg-health-readout {
  position: absolute;
  top: 80%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  pointer-events: none;
}
/* The number takes the band's colour, like the arc. Two inks rather than one:
   the vivid stop reads on a dark card and washes out on a white one, so light
   mode takes the darkest stop of the same family. The old fixed thresholds
   (danger under 70, warning under 90) are gone -- the number and the arc
   disagreeing about where the site stands is worse than either being wrong. */
.pg-health-score {
  color: rgb(var(--pg-health-ink-text));
  font-size: 32px;
  font-weight: 700;
  line-height: 1;
}
html[data-bs-theme="dark"] .pg-health-score {
  color: rgb(var(--pg-health-ink));
}
.pg-health-score span {
  font-size: 15px;
  font-weight: 600;
}
.pg-health-label {
  margin-top: 3px;
  color: var(--bs-secondary-color);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* Status checks.
   ---------------------------------------------------------------------------
   A chip per check, wrapping. This was a grid of tiles, each carrying a label
   over a value; across half a card that is twelve boxes of sixty-six pixels,
   which put both lines at nine pixels and turned "Veritabanı" and "Güncelleme"
   into ellipses. A grid of labels that have to be hovered to be read is not a
   status panel.

   The chip buys that width back by dropping the value from every check that is
   fine -- the colour has already said "Tamam", and saying it again in the space
   the label needed is the worst trade on the card. What is left is a row of
   readable names, and the two or three that still carry a value are the two or
   three worth looking at.

   Anything that expands is a full-width child of the same wrapping row, so it
   opens on the line under its own chip and pushes the rest down. */
/* The top padding was a caption -- "DURUM KONTROLLERİ" -- and the caption is
   gone: the gauge over it already says what this half is, and a heading whose
   only job is to name the obvious costs a line of a column that scrolls. The
   space it held is kept, so the chips land where they always did. */
.pg-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  padding: 0.5rem 0.5rem 0.5rem;
}
.pg-check {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  max-width: 100%;
  padding: 2px 7px;
  border: 0;
  border-radius: 50rem;
  background: var(--pg-tint);
  color: inherit;
  font: inherit;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.5;
  text-decoration: none;
}
button.pg-check,
a.pg-check {
  cursor: pointer;
}
a.pg-check:hover,
a.pg-check:focus-visible,
button.pg-check:hover,
button.pg-check:focus-visible {
  background: var(--pg-tint-hover);
  color: inherit;
}
.pg-check > i {
  flex: 0 0 auto;
  font-size: 10px;
}
.pg-check-name {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* The value only appears on a check that has something to report, so it is the
   part that should read as the exception: lighter weight than the name it
   follows, and set off by a rule rather than by a second colour, because the
   chip is already coloured by state. */
.pg-check-value {
  flex: 0 1 auto;
  overflow: hidden;
  padding-left: 5px;
  border-left: var(--bs-border-width) solid currentColor;
  font-weight: 500;
  white-space: nowrap;
  text-overflow: ellipsis;
  opacity: 0.85;
}
.pg-check-caret {
  font-size: 9px;
  opacity: 0.6;
}
.pg-check[aria-expanded="true"] .pg-check-caret {
  transform: rotate(180deg);
}
/* Bootstrap's own four, so a chip is the same red as every other red on the
   screen. The emphasis colours are the pair Bootstrap picks per theme, which is
   what keeps the text readable on the tint in both. */
.pg-check-success {
  background: rgba(var(--bs-success-rgb), 0.13);
  color: var(--bs-success-text-emphasis);
}
.pg-check-warning {
  background: rgba(var(--bs-warning-rgb), 0.15);
  color: var(--bs-warning-text-emphasis);
}
.pg-check-danger {
  background: rgba(var(--bs-danger-rgb), 0.15);
  color: var(--bs-danger-text-emphasis);
}
.pg-check-secondary {
  background: var(--pg-tint);
  color: var(--bs-secondary-color);
}
a.pg-check-success:hover,
button.pg-check-success:hover,
a.pg-check-success:focus-visible,
button.pg-check-success:focus-visible {
  background: rgba(var(--bs-success-rgb), 0.22);
  color: var(--bs-success-text-emphasis);
}
a.pg-check-warning:hover,
button.pg-check-warning:hover,
a.pg-check-warning:focus-visible,
button.pg-check-warning:focus-visible {
  background: rgba(var(--bs-warning-rgb), 0.24);
  color: var(--bs-warning-text-emphasis);
}
a.pg-check-danger:hover,
button.pg-check-danger:hover,
a.pg-check-danger:focus-visible,
button.pg-check-danger:focus-visible {
  background: rgba(var(--bs-danger-rgb), 0.24);
  color: var(--bs-danger-text-emphasis);
}
@keyframes pg-health-spin {
  to {
    transform: rotate(360deg);
  }
}

.pg-health-dot {
  margin-right: 5px;
  font-size: 6px;
  vertical-align: middle;
}

/* Which four colours the arc is drawn from.
   ---------------------------------------------------------------------------
   api.php hands over both sets -- --pg-health-d1..d4 for a dark card,
   --pg-health-l1..l4 for a light one -- and the choice is made here, because
   PHP has no idea which theme it is rendering into. Bright stops that read on
   black go pastel on white and the four hues wash into one; the light set is
   the same four families two steps down, so the transitions stay legible
   against the page rather than against the ink. */
.pg-health {
  --pg-health-s1: var(--pg-health-d1);
  --pg-health-s2: var(--pg-health-d2);
  --pg-health-s3: var(--pg-health-d3);
  --pg-health-s4: var(--pg-health-d4);
}
html[data-bs-theme="light"] .pg-health {
  --pg-health-s1: var(--pg-health-l1);
  --pg-health-s2: var(--pg-health-l2);
  --pg-health-s3: var(--pg-health-l3);
  --pg-health-s4: var(--pg-health-l4);
}

/* The animation, inside the bar.
   ---------------------------------------------------------------------------
   The cells are real squares laid over the ring and cut to the drawn arc by a
   mask, so the bar IS the matrix rather than sitting in front of one. Every
   square breathes on its own clock, slowly -- four to nine seconds -- and no
   two share a length, so the set has no period and nothing ever comes back
   round. It runs for as long as the card is open, at the same strength in the
   first second as in the hundredth.

   api.php gives each one a NEGATIVE delay. A positive delay is a wait: until it
   elapses the square has no animated value and sits at full opacity, so the
   first seconds after a draw were a cascade of squares dropping in one after
   another -- a burst that has nothing to do with the animation, and that made
   everything after it look like the animation had died down. A negative delay
   starts the cycle already part-way through: every square is mid-breath from
   the first frame, and their phases are spread from the first frame too.

   They were the contents of an SVG <pattern>, which is a third of the elements
   and looked right for ten or twenty seconds. Then it thinned out to almost
   nothing. The animations had not stopped -- their computed opacity keeps
   changing, measured at thirty seconds -- but the browser rasterises pattern
   and mask CONTENT into a cached texture and stops refreshing it, so the
   picture updated only now and then. Nothing about that looks broken, which is
   what makes it the worse fault. The rule it leaves behind: do not put an
   animation inside <defs>. Animate what is drawn, and let the static thing be
   the mask.

   Four stops rather than two, and unevenly spaced. A square that only breathes
   between dim and bright is a metronome, and sixty-four metronomes are still a
   rhythm; the second, lower rise gives every cell a flicker with two beats in
   it, which is what stops the eye finding the cycle.

   A run of light travelling the bar was tried first and is a different thing
   altogether: it is one event crossing the bar, and what this wants is a
   surface that is quietly alive everywhere at once. */
@media (prefers-reduced-motion: no-preference) {
  .pg-health-cells > rect {
    animation-name: pg-health-cell;
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
  }
  /* And the light around the bar breathes with them, by a little. Two
     different lengths, and not a whole multiple of each other, so the pair
     never comes back into step -- a swell the squares twinkle inside of,
     rather than a second rhythm to notice.

     These two are filtered, and a filter is re-run when what it filters
     changes. What changes here is the opacity of the RESULT, which is applied
     after the blur, so the blur itself is computed once and reused. */
  .pg-health-glow,
  .pg-health-bloom {
    animation-name: pg-health-breathe;
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
  }
  .pg-health-glow {
    animation-duration: 5.1s;
    animation-delay: -2.4s;
  }
  .pg-health-bloom {
    animation-duration: 7.1s;
    animation-delay: -6.1s;
  }
}
@keyframes pg-health-breathe {
  0%,
  100% {
    opacity: var(--pg-glow-lo);
  }
  50% {
    opacity: var(--pg-glow-hi);
  }
}
/* How dark a square gets at the bottom of its breath. This is the whole
   amplitude of the animation, and at 0.28 -- with a lit arc under it -- the
   difference between a square at rest and a square at its brightest was a few
   per cent of screen. The animation was running the whole time; it had nothing
   to show. */
.pg-health-cells {
  --pg-cell-lo: 0.08;
}
/* The blurred copy of the squares that sits under them.
   ---------------------------------------------------------------------------
   Without it the bar is squares on a card and the gaps are card, so a partly
   filled gauge reads as a progress bar advancing a pixel at a time rather than
   as a lit panel. The two arc glows cannot fix that: they are the light around
   the bar, they are drawn at a radius that fills nothing, and the panel clips
   them at the top of the card anyway.

   Held under half so the halo stays under the square that casts it. Above that
   the squares merge into each other and the matrix goes back to being a bar. */
.pg-health-cell-glow {
  opacity: 0.45;
}
/* One breath: down, up, down. Nothing more.
   ---------------------------------------------------------------------------
   It had four stops and an unevenly spaced flicker, on the theory that a
   square merely breathing is a metronome. It is worse than that. The timing
   function applies BETWEEN EACH PAIR of keyframes, not across the cycle -- so
   ease-in-out with four stops means the square eases to a stop and starts
   again five times a cycle, and spends most of its time barely moving. Early
   on that is hidden by the squares still dropping into their delays; once
   that settles, what is left is a grid of squares standing still with an
   occasional twitch. Which is exactly what "it dies down after five seconds"
   describes.

   Two stops and one ease is one continuous rise and fall with two still
   moments per cycle instead of ten. The flow comes from two hundred squares
   at two hundred different phases, not from any one of them being busy. */
@keyframes pg-health-cell {
  0%,
  100% {
    opacity: var(--pg-cell-lo);
  }
  50% {
    opacity: 1;
  }
}
/* .pg-health-base is gone.
   ---------------------------------------------------------------------------
   It was a solid arc under the squares, there to carry the gradient as one
   continuous sweep. Two faults, and the second is why nothing was left of it.

   It was what hid the animation: a lit bar under every square means a square
   dimming to a tenth over a bar already at a quarter changes the pixel by almost
   nothing. And it was drawn to its own rim rather than to the squares, so along
   both edges of the bar there was a line of colour that did not line up with
   anything -- a smooth arc showing from under a grid.

   The squares are the bar now. They run to both rims because the bar CLIPS them
   rather than fitting between them, the continuity comes from their own halo,
   and the sweep comes from the two blurred arcs, which have no edges to be out
   of line. */
/* The hairline inside the thick arc is the same gradient at two units wide, so
   it is the one place the hue is read undiluted by the cells. Worth its full
   strength. */
.pg-health-inner {
  opacity: 0.9;
}

/* A check that can expand keeps its rows on the row under its own tile rather
   than in a box at the foot of the card. With one panel on the card a single
   box was the same thing; with several, an unlabelled list under a grid of
   tiles said nothing about which tile had opened it. */
.pg-health-detail-slot {
  grid-column: 1 / -1;
}

/* Section captions, one over each half. The card header names the card; these
   name the two halves, which are answering different questions -- what is true
   of this installation, and what can be done about it. */
.pg-jobs-heading {
  padding: 0.5rem 0.75rem 0.125rem;
  color: var(--bs-secondary-color);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* The jobs column.
   ---------------------------------------------------------------------------
   One line per job: what it is, where it stands, and the control that acts on
   it. A line, not a tile, because a tile has room for a label and a word and
   these need a verb -- "Web Sunucusu Kuralları · 3 eksik · Onar" does not fit
   in sixty-six pixels, and shortened to fit it stopped saying anything.

   Anything that opens is a full-width child of the same flex row, so it wraps
   onto its own line directly beneath and pushes the rest of the column down.
   Nothing is laid beside the line and nothing is laid over it. */
.pg-jobs {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 0 0.5rem 0.5rem;
}
.pg-job {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 5px 6px 5px 9px;
  border-radius: var(--bs-border-radius);
  background: var(--pg-tint);
}
/* The name side. A button when there is something to expand, a plain box when
   there is not, which is why the control styling a button arrives with is
   undone here rather than in a variant. The action beside it is a sibling and
   never a child: a control inside the toggle would fire the toggle on its way
   out, and the fix button would open a panel every time it was pressed. */
.pg-job-open {
  display: flex;
  flex: 1 1 0;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
}
button.pg-job-open {
  cursor: pointer;
}
.pg-job-icon {
  flex: 0 0 auto;
  font-size: 13px;
}
/* The note gives up its width first. The name is what tells the operator which
   row this is; the note repeats a state the icon has already coloured. */
.pg-job-name {
  flex: 0 1 auto;
  overflow: hidden;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.35;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pg-job-note {
  flex: 1 1 2.5rem;
  min-width: 0;
  overflow: hidden;
  font-size: 10.5px;
  line-height: 1.35;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pg-job-caret {
  flex: 0 0 auto;
  font-size: 9px;
  opacity: 0.55;
}
.pg-job-open[aria-expanded="true"] .pg-job-caret {
  transform: rotate(180deg);
}
.pg-job-btn {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border: 0;
  border-radius: 50rem;
  background: var(--pg-tint-strong);
  color: inherit;
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.5;
  text-decoration: none;
  cursor: pointer;
}
.pg-job-btn:hover,
.pg-job-btn:focus-visible {
  background: var(--pg-tint-hover);
  color: inherit;
}
.pg-job-btn i {
  font-size: 10px;
}
/* The one control on the card that repairs rather than reports. It is coloured
   because the row it sits on is red: a neutral button beside a failing row
   reads as another place to look rather than as the answer. */
.pg-job-btn-fix {
  background: rgba(var(--bs-danger-rgb), 0.15);
  color: var(--bs-danger-text-emphasis);
}
.pg-job-btn-fix:hover,
.pg-job-btn-fix:focus-visible {
  background: rgba(var(--bs-danger-rgb), 0.25);
  color: var(--bs-danger-text-emphasis);
}
/* Disabled here means running, not unavailable, so the icon turns instead of
   the button going flat. */
.pg-job-btn:disabled {
  cursor: default;
}
@media (prefers-reduced-motion: no-preference) {
  .pg-job-btn:disabled i {
    animation: pg-health-spin 0.9s linear infinite;
  }
}
/* Whatever opens takes the whole width and lands on the next line of the same
   row. flex-basis 100% rather than a block outside the row, so the panel
   belongs to its line and cannot drift away from it. */
.pg-job-slot,
.pg-job-result {
  flex: 1 0 100%;
}
.pg-job-panel {
  padding-top: 2px;
}
.pg-job-panel-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 3px 6px;
  border-radius: var(--bs-border-radius-sm);
  font-size: 10px;
}
.pg-job-panel-row:nth-child(odd) {
  background: var(--pg-tint-soft);
}

/* Twelve-slot meter under the Operations Desk shipping lane (widget-id 9): one
   dot per parcel waiting, lit amber, so a glance tells a quiet day from a
   backlog before the rows are read. */
.pg-ship-meter {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0.5rem 0 0.25rem;
}
.pg-ship-meter span {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.18;
}
.pg-ship-meter span.is-on {
  background: #f59e0b;
  opacity: 1;
}

/* ── Widget empty state ───────────────────────────────────────────────────
   What a card shows when it has nothing to list.

   It centres by FILLING its box and centring its own content, not by
   positioning itself against an ancestor. That distinction is the whole point
   of the class: the markup it replaced used
   `position-absolute top-50 start-50 translate-middle`, which resolves against
   the nearest positioned ancestor -- the card -- rather than against the box
   the message belongs in. On the Orders card that put both halves' messages at
   the same coordinates and printed one sentence over the other; everywhere
   else it centred the message over the header as well as the body, which is
   why the empty cards all looked a little high.

   height:100% is what does the centring, and it resolves because every host is
   a flex item with a settled height: the card is aspect-ratio 1/1, its body
   takes the space the header leaves, and a .pg-split-half takes half of that.
   min-height is the floor for any host without a definite height, so the
   message can never collapse to a line of text in the corner.

   max-width on the sentence keeps it to about two lines at the card's width.
   Left to itself it ran the full width of the card and read as a paragraph of
   body copy rather than as the absence of content. */
/* Whatever box the message lands in becomes a column, and the message takes
   what is left in it. Several cards print a summary line above their list --
   "0 packages · worth 0,00", "0 contacts today" -- and that line is a sibling
   of the empty state, not something above the box.

   The first version of this said height:100% instead. On a card with no
   summary that is the same thing; on Pending Shipments and Contacts it is the
   full height of the box PLUS the summary line, so the total overflowed and
   the card grew a scrollbar with nothing in it to scroll. Growing to fill
   whatever is left is the version that holds in both. */
.card-body:not(.pg-split):has(.pg-widget-empty),
.pg-split-half:has(> .pg-widget-empty),
.pg-list:has(> .pg-widget-empty) {
  display: flex;
  flex-direction: column;
}

.pg-widget-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Takes the space the summary line leaves, and never less than its own
     content: flex-shrink is 0 on purpose.

     With 1 1 auto the box was allowed to shrink past what was inside it. On
     the Firewall card, where the same half also carries a mode badge and a
     three-figure row, the box came out at 81px against 123px of content -- and
     the 42px difference did not disappear, it drew over the row above. A
     scrollbar is a fair outcome for a box that genuinely cannot fit; text
     printed on top of other text is not, so the shrink is what had to go. */
  flex: 1 0 auto;
  min-height: 4rem;
  gap: 0.45rem;
  padding: 0.75rem 1.25rem;
  text-align: center;
  color: var(--bs-secondary-color);
}
.pg-widget-empty > .bi {
  font-size: 22px;
  line-height: 1;
  /* Present enough to read as a symbol, quiet enough that a dashboard of empty
     cards does not look like a dashboard of warnings. */
  opacity: 0.4;
}
.pg-widget-empty > p {
  margin: 0;
  /* About two lines at a card's width. Left to itself the sentence ran the
     full width of the card and read as body copy rather than as the absence of
     content. balance evens the lines out: without it the longer messages broke
     with a single word stranded on the last line. */
  max-width: 26ch;
  font-size: 12px;
  line-height: 1.4;
  text-wrap: balance;
}

/* Empty is the answer the operator wanted: no product out of stock, no comment
   held back, no refund still owed. The tick carries that, so it is allowed to
   be more present than the grey icons above -- but it stays an icon at rest,
   not a success banner, because the card is reporting quiet, not celebrating. */
.pg-widget-empty.is-good > .bi {
  color: var(--bs-success);
  opacity: 0.8;
}

/* The button, where a card has one. Small and outlined on purpose: it is an
   offer, not the point of the card, and several of these sit on one screen. */
.pg-widget-empty > .btn {
  margin-top: 0.15rem;
  font-size: 11px;
}

/* Some cards hand their rows to .pg-list, so the empty state lands one box
   deeper than the card body. .pg-list is an ordinary block and sizes to its
   content, which leaves the message nothing to fill and drops it at the top of
   an otherwise empty card -- centred across, stranded up.

   :has() rather than a class on the markup, because the call sites only know
   they have nothing to show; which wrapper they are inside is this
   stylesheet's business. */
.pg-list:has(> .pg-widget-empty) {
  flex: 1 1 auto;
  padding: 0;
}

/* Scrollbars.

   One rule for everything that scrolls inside a widget, whichever element ends
   up being the scroller: the card body itself, a .pg-split half, or a list
   nested deeper than either. The descendant combinator is what makes that true
   -- `.card.widget ::-webkit-scrollbar`, with the space, reaches the card body
   as well as everything below it -- which is why the second selector of each
   pair, `.card.widget .card-body ::-webkit-scrollbar`, was only ever repeating
   a subset of the first and has gone.

   height as well as width: a card body carrying plain .overflow-auto can scroll
   sideways, and a bar styled in one axis but not the other is the same
   inconsistency in miniature.

   Firefox is served separately, below, because it has no ::-webkit-scrollbar
   and needs the standard properties instead. */
.card.widget ::-webkit-scrollbar-track {
  -webkit-box-shadow: inset 1px 0 2px #0000004d;
  background-color: transparent;
}
.card.widget ::-webkit-scrollbar {
  width: 6px;
  height: 6px;
  background-color: transparent;
}
.card.widget ::-webkit-scrollbar-thumb {
  background-color: var(--pg-scrollbar-thumb);
}

/* Firefox: the standard properties, and nothing but Firefox.

   They cannot simply be added alongside the rules above. Declaring
   scrollbar-color on an element switches Chrome off ::-webkit-scrollbar for
   that element altogether, and scrollbar-width is not inherited -- so putting
   the pair on .card.widget turned every 6px bar on the dashboard back into the
   browser's 15px default with a tinted thumb. Measured, not guessed.

   The feature query is the narrowest fence available: Firefox does not
   implement the ::-webkit-scrollbar selector, so the block applies there and is
   skipped everywhere the rules above already work. The universal selector is
   needed because scrollbar-width is per element and the scroller is sometimes
   the card body, sometimes a .pg-split half, sometimes a list below either. */
@supports not selector(::-webkit-scrollbar) {
  .card.widget,
  .card.widget * {
    scrollbar-width: thin;
    scrollbar-color: var(--pg-scrollbar-thumb) transparent;
  }
}
.card.widget ::-webkit-scrollbar-track {
  margin-bottom: 1.1rem;
}

/* Dark theme */
html[data-bs-theme="dark"] body:not(.advanced-visuals) .card.widget {
  --bs-card-border-color: #3b3b3b;
}
html[data-bs-theme="dark"] .default #footer {
  color: var(--bs-body-color) !important;
}

html[data-bs-theme="dark"] .card.widget p.bg-light {
  background-color: #222;
  color: #f2f2f2;
}
html[data-bs-theme="dark"] .card.widget .card-header {
    color: #a6a6a6a1;

}


html[data-bs-theme="dark"] .card.widget .btn-group .btn.btn-outline-light {
  color: #fff;
}
html[data-bs-theme="dark"]
  .card.widget
  .btn-group
  .btn.btn-outline-light.active {
  color: #000;
}

html[data-bs-theme="dark"] .card.widget ::-webkit-scrollbar-track {
  -webkit-box-shadow: inset 0 0 4px #0000004d;
  background-color: transparent;
}

/* Widget backgrounds */
/* Widget accent colors — border + icon + solid background (no opacity) */

/* ── Light theme ─────────────────────────────────────────────────── */

/* Ecommerce summary head. The stock figure lost its tile: a bordered box
   around a number that never changes shape is padding, and the two tiles it
   sat in cost roughly a third of the card body. */
.pg-ec-head {
  padding: 5px 10px 4px;
}
.pg-ec-line {
  display: flex;
  align-items: baseline;
  gap: 6px;
  flex-wrap: wrap;
}
.pg-ec-val {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.3px;
  line-height: 1.15;
}
.pg-ec-lbl,
.pg-ec-sub {
  font-size: 10.5px;
}
.pg-ec-sub {
  margin-top: 1px;
}
.pg-ec-dot {
  opacity: 0.45;
  margin: 0 1px;
}
.pg-ec-facts {
  display: flex;
  gap: 10px;
  padding: 0 10px 4px;
}
.pg-ec-fact {
  flex: 1 1 0;
  min-width: 0;
}
.pg-ec-fact span {
  display: block;
  font-size: 10.5px;
}
.pg-ec-fact b {
  display: block;
  font-size: 13px;
  font-weight: 700;
  margin-top: 1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* An inset shadow rather than a border, so the emphasised row keeps the same
   30px height as the three above it. */
.pg-row.pg-row-total {
  box-shadow: inset 0 0 0 1px rgba(16, 185, 129, 0.22);
}

.widget[widget-id="1"]  { --bs-card-border-color: rgba(13,148,136,.30);  background: #f0fdfa; --pg-accent: #0d9488; --pg-accent-ink: #fff; }
.widget[widget-id="1"]  .card-header i { color: #0d9488; }
.widget[widget-id="3"]  { --bs-card-border-color: rgba(16,185,129,.30);  background: #ecfdf5; --pg-accent: #10b981; --pg-accent-ink: #18181b; }
.widget[widget-id="3"]  .card-header i { color: #10b981; }
.widget[widget-id="3"]  .pg-ec-val { color: #047857; }
.widget[widget-id="3"]  .pg-ec-warn { color: #b45309; }
.widget[widget-id="3"]  .pg-row-total { background: rgba(16,185,129,.10); }
.widget[widget-id="4"]  { --bs-card-border-color: rgba(6,182,212,.30);   background: #ecfeff; --pg-accent: #06b6d4; --pg-accent-ink: #18181b; }
.widget[widget-id="4"]  .card-header i { color: #06b6d4; }
/* Visitor summaries was left transparent and borderless while it was three
   panels sharing the page background. It is one card with one chart now, and a
   card that stops at the page colour has no edge to sit inside -- it reads as
   loose furniture above the grid. Same blue family as the chart line. */
.widget[widget-id="5"]  { --bs-card-border-color: rgba(59, 130, 246, 0.30);  background: #f2f7ff; }
.widget[widget-id="5"]  .card-header i { color: #3b82f6; }
.widget[widget-id="6"]  { --bs-card-border-color: rgba(239,68,68,.30);   background: #fef2f2; --pg-accent: #ef4444; --pg-accent-ink: #18181b; }
.widget[widget-id="6"]  .card-header i { color: #ef4444; }
.widget[widget-id="7"]  { --bs-card-border-color: rgba(139,92,246,.30);  background: #f5f3ff; --pg-accent: #8b5cf6; --pg-accent-ink: #fff; }
.widget[widget-id="7"]  .card-header i { color: #8b5cf6; }
.widget[widget-id="8"]  { --bs-card-border-color: rgba(5,150,105,.30);   background: #f0fdf4; --pg-accent: #059669; --pg-accent-ink: #18181b; }
.widget[widget-id="8"]  .card-header i { color: #059669; }
.widget[widget-id="9"]  { --bs-card-border-color: rgba(217,119,6,.30);   background: #fffbeb; --pg-accent: #d97706; --pg-accent-ink: #18181b; }
.widget[widget-id="9"]  .card-header i { color: #d97706; }
.widget[widget-id="10"] { --bs-card-border-color: rgba(99,102,241,.30);  background: #eef2ff; --pg-accent: #6366f1; --pg-accent-ink: #fff; }
.widget[widget-id="10"] .card-header i { color: #6366f1; }
.widget[widget-id="11"] { --bs-card-border-color: rgba(139,92,246,.30);  background: #faf5ff; --pg-accent: #a855f7; --pg-accent-ink: #18181b; }
.widget[widget-id="11"] .card-header i { color: #a855f7; }
.widget[widget-id="12"] { --bs-card-border-color: rgba(239,68,68,.30);   background: #fef2f2; --pg-accent: #ef4444; --pg-accent-ink: #18181b; }
.widget[widget-id="12"] .card-header i { color: #ef4444; }
.widget[widget-id="13"] { --bs-card-border-color: rgba(14,165,233,.30);  background: #f0f9ff; --pg-accent: #0ea5e9; --pg-accent-ink: #18181b; }
.widget[widget-id="13"] .card-header i { color: #0ea5e9; }
.widget[widget-id="14"] { --bs-card-border-color: rgba(124,58,237,.30);  background: #f5f3ff; --pg-accent: #7c3aed; --pg-accent-ink: #fff; }
.widget[widget-id="14"] .card-header i { color: #7c3aed; }
.widget[widget-id="15"] { --bs-card-border-color: rgba(217,119,6,.30);   background: #fefce8; --pg-accent: #d97706; --pg-accent-ink: #18181b; }
.widget[widget-id="15"] .card-header i { color: #d97706; }
.widget[widget-id="16"] { --bs-card-border-color: rgba(13,148,136,.30);  background: #f0fdfa; --pg-accent: #0d9488; --pg-accent-ink: #18181b; }
.widget[widget-id="16"] .card-header i { color: #0d9488; }
.widget[widget-id="17"] { --bs-card-border-color: rgba(100,116,139,.30); background: #f8fafc; --pg-accent: #64748b; --pg-accent-ink: #fff; }
.widget[widget-id="17"] .card-header i { color: #64748b; }
.widget[widget-id="18"] { --bs-card-border-color: rgba(202,138,4,.30);   background: #fefce8; --pg-accent: #ca8a04; --pg-accent-ink: #18181b; }
.widget[widget-id="18"] .card-header i { color: #ca8a04; }
.widget[widget-id="19"] { --bs-card-border-color: rgba(225,29,72,.30);   background: #fff1f2; --pg-accent: #e11d48; --pg-accent-ink: #fff; }
.widget[widget-id="19"] .card-header i { color: #e11d48; }
.widget[widget-id="20"] { --bs-card-border-color: rgba(37,99,235,.30);   background: #eff6ff; --pg-accent: #2563eb; --pg-accent-ink: #fff; }
.widget[widget-id="20"] .card-header i { color: #2563eb; }

/* 21, 23, 25 and 26 were left out of this table when they were built: they
   declared --pg-accent so their row badges had a colour, and stopped there. On
   a dashboard where every other card carries a tinted ground and a matching
   border, four plain white ones do not read as restraint, they read as
   unfinished -- which is what they were. Same three declarations as the rest,
   same shape, no special case. */
.widget[widget-id="21"] { --bs-card-border-color: rgba(16,185,129,.30);  background: #ecfdf6; --pg-accent: #10b981; --pg-accent-ink: #18181b; }
.widget[widget-id="21"] .card-header i { color: #10b981; }
.widget[widget-id="23"] { --bs-card-border-color: rgba(139,92,246,.30);  background: #f6f4ff; --pg-accent: #8b5cf6; --pg-accent-ink: #fff; }
.widget[widget-id="23"] .card-header i { color: #8b5cf6; }
.widget[widget-id="25"] { --bs-card-border-color: rgba(14,165,233,.30);  background: #eff9ff; --pg-accent: #0ea5e9; --pg-accent-ink: #18181b; }
.widget[widget-id="25"] .card-header i { color: #0ea5e9; }
.widget[widget-id="26"] { --bs-card-border-color: rgba(236,72,153,.30);  background: #fdf2f8; --pg-accent: #ec4899; --pg-accent-ink: #18181b; }
.widget[widget-id="26"] .card-header i { color: #ec4899; }

/* The workspace cards: My Tasks, Waiting on Me, My Week, Team Pulse. */
.widget[widget-id="27"] { --bs-card-border-color: rgba(99,102,241,.30);  background: #eef2ff; --pg-accent: #6366f1; --pg-accent-ink: #fff; }
.widget[widget-id="27"] .card-header i { color: #6366f1; }
.widget[widget-id="28"] { --bs-card-border-color: rgba(225,29,72,.30);   background: #fff1f2; --pg-accent: #e11d48; --pg-accent-ink: #fff; }
.widget[widget-id="28"] .card-header i { color: #e11d48; }
.widget[widget-id="29"] { --bs-card-border-color: rgba(2,132,199,.30);   background: #f0f9ff; --pg-accent: #0284c7; --pg-accent-ink: #fff; }
.widget[widget-id="29"] .card-header i { color: #0284c7; }
.widget[widget-id="30"] { --bs-card-border-color: rgba(101,163,13,.30);  background: #f7fee7; --pg-accent: #65a30d; --pg-accent-ink: #fff; }
.widget[widget-id="30"] .card-header i { color: #65a30d; }

/* Waiting on Me: a mention or reply not read yet carries a dot after the
   sender's name; read ones stay in the list without it. */
.pg-row.is-unread .pg-row-name::after {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: 0.35rem;
  border-radius: 50%;
  background: var(--pg-accent);
  vertical-align: 0.15em;
}

/* ── Dark theme ──────────────────────────────────────────────────── */

html[data-bs-theme="dark"] .widget[widget-id="1"]  { --bs-card-border-color: rgba(13,148,136,.28); background: #0a1413; --pg-accent: #14b8a6; }
html[data-bs-theme="dark"] .widget[widget-id="1"]  .card-header i { color: #2dd4bf; }
html[data-bs-theme="dark"] .widget[widget-id="3"]  { --bs-card-border-color: rgba(16,185,129,.28); background: #0c1410; }
html[data-bs-theme="dark"] .widget[widget-id="3"]  .card-header i { color: #34d399; }
html[data-bs-theme="dark"] .widget[widget-id="3"]  .pg-ec-val { color: #34d399; }
html[data-bs-theme="dark"] .widget[widget-id="3"]  .pg-ec-warn { color: #fbbf24; }
html[data-bs-theme="dark"] .widget[widget-id="3"]  .pg-row-total { background: rgba(16,185,129,.13); }
html[data-bs-theme="dark"] .widget[widget-id="4"]  { --bs-card-border-color: rgba(6,182,212,.28);  background: #0c1416; }
html[data-bs-theme="dark"] .widget[widget-id="4"]  .card-header i { color: #22d3ee; }
html[data-bs-theme="dark"] .widget[widget-id="5"]  { --bs-card-border-color: rgba(59, 130, 246, 0.26); background: #0d1119; }
html[data-bs-theme="dark"] .widget[widget-id="5"]  .card-header i { color: #60a5fa; }
html[data-bs-theme="dark"] .widget[widget-id="6"]  { --bs-card-border-color: rgba(239,68,68,.28);  background: #161010; }
html[data-bs-theme="dark"] .widget[widget-id="6"]  .card-header i { color: #f87171; }
html[data-bs-theme="dark"] .widget[widget-id="7"]  { --bs-card-border-color: rgba(139,92,246,.28); background: #121018; }
html[data-bs-theme="dark"] .widget[widget-id="7"]  .card-header i { color: #a78bfa; }
html[data-bs-theme="dark"] .widget[widget-id="8"]  { --bs-card-border-color: rgba(5,150,105,.28);  background: #0c140e; }
html[data-bs-theme="dark"] .widget[widget-id="8"]  .card-header i { color: #34d399; }
html[data-bs-theme="dark"] .widget[widget-id="9"]  { --bs-card-border-color: rgba(217,119,6,.28);  background: #161410; }
html[data-bs-theme="dark"] .widget[widget-id="9"]  .card-header i { color: #fbbf24; }
html[data-bs-theme="dark"] .widget[widget-id="10"] { --bs-card-border-color: rgba(99,102,241,.28); background: #0e1020; }
html[data-bs-theme="dark"] .widget[widget-id="10"] .card-header i { color: #818cf8; }
html[data-bs-theme="dark"] .widget[widget-id="11"] { --bs-card-border-color: rgba(139,92,246,.28); background: #141018; }
html[data-bs-theme="dark"] .widget[widget-id="11"] .card-header i { color: #c084fc; }
html[data-bs-theme="dark"] .widget[widget-id="12"] { --bs-card-border-color: rgba(239,68,68,.28);  background: #161010; }
html[data-bs-theme="dark"] .widget[widget-id="12"] .card-header i { color: #f87171; }
html[data-bs-theme="dark"] .widget[widget-id="13"] { --bs-card-border-color: rgba(14,165,233,.28); background: #0e141e; }
html[data-bs-theme="dark"] .widget[widget-id="13"] .card-header i { color: #38bdf8; }
html[data-bs-theme="dark"] .widget[widget-id="14"] { --bs-card-border-color: rgba(124,58,237,.28); background: #10101a; }
html[data-bs-theme="dark"] .widget[widget-id="14"] .card-header i { color: #a78bfa; }
html[data-bs-theme="dark"] .widget[widget-id="15"] { --bs-card-border-color: rgba(217,119,6,.28);  background: #161410; }
html[data-bs-theme="dark"] .widget[widget-id="15"] .card-header i { color: #fcd34d; }
html[data-bs-theme="dark"] .widget[widget-id="16"] { --bs-card-border-color: rgba(13,148,136,.28); background: #0c1412; }
html[data-bs-theme="dark"] .widget[widget-id="16"] .card-header i { color: #2dd4bf; }
html[data-bs-theme="dark"] .widget[widget-id="17"] { --bs-card-border-color: rgba(100,116,139,.28); background: #121416; }
html[data-bs-theme="dark"] .widget[widget-id="17"] .card-header i { color: #94a3b8; }
html[data-bs-theme="dark"] .widget[widget-id="18"] { --bs-card-border-color: rgba(202,138,4,.28);  background: #161410; }
html[data-bs-theme="dark"] .widget[widget-id="18"] .card-header i { color: #fbbf24; }
html[data-bs-theme="dark"] .widget[widget-id="19"] { --bs-card-border-color: rgba(225,29,72,.28);  background: #160c10; }
html[data-bs-theme="dark"] .widget[widget-id="19"] .card-header i { color: #fb7185; }
html[data-bs-theme="dark"] .widget[widget-id="20"] { --bs-card-border-color: rgba(37,99,235,.28);  background: #0e101e; }
html[data-bs-theme="dark"] .widget[widget-id="20"] .card-header i { color: #60a5fa; }
html[data-bs-theme="dark"] .widget[widget-id="21"] { --bs-card-border-color: rgba(16,185,129,.28); background: #0b1411; }
html[data-bs-theme="dark"] .widget[widget-id="21"] .card-header i { color: #34d399; }
html[data-bs-theme="dark"] .widget[widget-id="23"] { --bs-card-border-color: rgba(139,92,246,.28); background: #111018; }
html[data-bs-theme="dark"] .widget[widget-id="23"] .card-header i { color: #a78bfa; }
html[data-bs-theme="dark"] .widget[widget-id="25"] { --bs-card-border-color: rgba(14,165,233,.28); background: #0d141c; }
html[data-bs-theme="dark"] .widget[widget-id="25"] .card-header i { color: #38bdf8; }
html[data-bs-theme="dark"] .widget[widget-id="26"] { --bs-card-border-color: rgba(236,72,153,.28); background: #170d13; }
html[data-bs-theme="dark"] .widget[widget-id="26"] .card-header i { color: #f472b6; }
html[data-bs-theme="dark"] .widget[widget-id="27"] { --bs-card-border-color: rgba(99,102,241,.28); background: #0e1020; --pg-accent: #818cf8; --pg-accent-ink: #18181b; }
html[data-bs-theme="dark"] .widget[widget-id="27"] .card-header i { color: #818cf8; }
html[data-bs-theme="dark"] .widget[widget-id="28"] { --bs-card-border-color: rgba(225,29,72,.28);  background: #160c10; --pg-accent: #fb7185; --pg-accent-ink: #18181b; }
html[data-bs-theme="dark"] .widget[widget-id="28"] .card-header i { color: #fb7185; }
html[data-bs-theme="dark"] .widget[widget-id="29"] { --bs-card-border-color: rgba(2,132,199,.28);  background: #0d141c; --pg-accent: #38bdf8; --pg-accent-ink: #18181b; }
html[data-bs-theme="dark"] .widget[widget-id="29"] .card-header i { color: #38bdf8; }
html[data-bs-theme="dark"] .widget[widget-id="30"] { --bs-card-border-color: rgba(101,163,13,.28); background: #11150b; --pg-accent: #a3e635; --pg-accent-ink: #18181b; }
html[data-bs-theme="dark"] .widget[widget-id="30"] .card-header i { color: #a3e635; }



/* System status card. Darker and cooler than the rest of the dashboard,
   because the gauge inside it is a glow and a glow needs something to glow
   against -- on the default card background the blurred arc washes out. The
   settings screen renders the same widget without a widget-id, so it is named
   here rather than given a rule of its own.
   The light theme cannot go dark, so it takes the palest tint of the same
   violet and lets the grey track carry the contrast instead. */
.widget[widget-id="2"],
#system_status_card {
  --bs-card-border-color: rgba(168, 85, 247, 0.28);
  --pg-accent: #8b5cf6;
  --pg-accent-ink: #fff;
  background: #faf9ff;
}
.widget[widget-id="2"] .card-header i,
#system_status_card .card-header i {
  color: #8b5cf6;
}
html[data-bs-theme="dark"] .widget[widget-id="2"],
html[data-bs-theme="dark"] #system_status_card {
  --bs-card-border-color: rgba(168, 85, 247, 0.26);
  background: #101017;
}
html[data-bs-theme="dark"] .widget[widget-id="2"] .card-header i,
html[data-bs-theme="dark"] #system_status_card .card-header i {
  color: #c084fc;
}


/* ══ Card appearance themes ════════════════════════════════════════════════

   Four treatments of the same card. An administrator picks one from the Panel
   entry's context menu and it is stored in dashboard.widget_theme;
   welcome.php writes the choice onto #content, so everything below is scoped to
   that attribute and the settings screen's own copy of the system status card
   is left out of it.

   "flat" has no rules here at all, and that is the point. Flat IS the block of
   per-widget backgrounds above, so the default costs nothing and an
   installation that never opens the menu keeps exactly the dashboard it had.
   The other three replace background, border and shadow, and they read
   --pg-accent -- which every widget already declares -- so one rule set dresses
   all twenty-two cards instead of three more colour tables.

   Specificity, because it decides whether this works at all: the per-widget
   dark rules above are html[data-bs-theme="dark"] .widget[widget-id="N"], which
   is (0,3,1). A theme rule written as [data-widget-theme] .card.widget is
   (0,3,0) and would lose to them -- the cards would take the theme in light and
   drop it in dark. Each block therefore repeats the html[...] prefix for its
   dark half rather than relying on source order. */

/* ── Strength ─────────────────────────────────────────────────────────────
   One dial per theme. Every colour in the three blocks below is mixed at one
   of these, so a theme that reads too loud is one number here rather than a
   dozen values spread across six rules.

   Neon runs at full strength and glass and aurora do not, and the reason is
   where each of them puts its colour rather than how much it uses. Neon keeps
   the hue on the border: twenty-three rims read as an outline. Glass and
   aurora lay it across the face of the card, under the list, and on a near
   black ground a saturated veil has nothing to be diluted by -- twenty-three
   of those at full strength is a colour riot, and dark is where it showed.

   Dimming alone was not the fix. What these two needed was for the colour to
   stop before the reading area starts, which is the geometry in each block,
   and for the dark tints to be mixed INTO the card background instead of
   floated over it as a translucent accent. A colour mixed with the ground is a
   tint of the card; the same colour at the same alpha over the ground is a
   film on top of it, and the second is what made the text look submerged.

   ── The selector, and why it is shaped like that ──
   Every rule below reads `[data-widget-theme] .pg-widget > .card.widget`. The
   `.pg-widget >` is not decoration and not there for readability: it is what
   lifts the selector from (0,3,0) to (0,4,0), which is the only thing putting
   it above the per-widget dark rules further up this file --
   `html[data-bs-theme="dark"] .widget[widget-id="N"]`, at (0,3,1).

   Written the short way a theme applies in light and is beaten by the flat
   dark tint the moment the panel goes dark. That is not hypothetical: aurora
   was rewritten so its light and dark halves held the same values, the
   now-identical dark half was deleted as dead code, and the theme silently
   stopped applying in dark. Every card fell back to its flat tint and the
   computed background-image read `none`. Raising the base selector means a
   dark twin exists only where the VALUES differ -- never to win an argument. */
[data-widget-theme="neon"] {
  --pg-fx-edge: 42%;   /* border */
  --pg-fx-lift: 100%;  /* the halo under the card */
}
[data-widget-theme="glass"] {
  --pg-fx-top: 14%;    /* accent at the head of the sheet, gone by mid-card */
  --pg-fx-edge: 24%;
}
[data-widget-theme="aurora"] {
  --pg-fx-one: 16%;    /* bloom at the top left */
  --pg-fx-two: 12%;    /* and at the bottom right */
  --pg-fx-edge: 20%;
}
html[data-bs-theme="dark"] [data-widget-theme="neon"] {
  --pg-fx-edge: 55%;
  --pg-fx-lift: 100%;
}
html[data-bs-theme="dark"] [data-widget-theme="glass"] {
  --pg-fx-top: 16%;
  --pg-fx-edge: 26%;
}
html[data-bs-theme="dark"] [data-widget-theme="aurora"] {
  --pg-fx-one: 22%;
  --pg-fx-two: 16%;
  --pg-fx-edge: 22%;
}

/* ── Neon ─────────────────────────────────────────────────────────────────
   The ground stays neutral and the light comes off the edge: a bleed just
   inside the border, and a low halo under the card.

   The bleed is a wide blur with a large negative spread, so however wide the
   blur, it only reaches a few pixels in from the border. That is what keeps it
   a rim of light rather than a lit card, and it is why this theme survives at
   full strength where the other two could not: nothing it does happens where
   the numbers are printed.

   Deliberately asymmetric between the two panel themes. A glow is light
   emitted against darkness, so in dark it is the loudest of the four; on a
   white ground there is nothing for it to emit against and it settles into a
   coloured shadow. Faking parity would mean flooding the light theme with
   colour, which is what the flat theme already does better. */
[data-widget-theme="neon"] .pg-widget > .card.widget {
  background: var(--bs-card-bg);
  border-color: color-mix(in srgb, var(--pg-accent, #64748b) var(--pg-fx-edge), transparent);
  box-shadow:
    inset 0 0 22px -14px var(--pg-accent, #64748b),
    0 8px 26px -20px color-mix(in srgb, var(--pg-accent, #64748b) var(--pg-fx-lift), transparent);
}

/* ── Glass ────────────────────────────────────────────────────────────────
   Translucent card over the panel backdrop. It is the one theme that needs
   something behind it to be worth anything, which is why choosing it also
   turns the panel backdrop on -- see the auto mapping in welcome.php.

   No backdrop-filter. What is behind a card is the wash: a few radial
   gradients so soft that blurring them changes nothing you can see. The blur
   still had to be redone for every card on every frame of a scroll, and again
   under any dialog or menu laid over the dashboard -- twenty-odd filtered
   surfaces paying for a difference nobody could point at.

   The tint runs down the card rather than across it, and it is finished by 58%
   -- roughly where the header ends and the first row begins. The card is still
   identifiable by its colour at a glance, from the part of it that carries the
   title and the icon, and the list below reads off plain glass. The first pass
   ran the tint corner to corner and every row sat in it.

   The white inset line along the top edge is what reads as glass rather than
   as a tint: it is the sheet catching light at its own thickness. Kept at .40
   in light and .07 in dark, because on a dark ground the same value reads as a
   seam rather than a highlight. */
[data-widget-theme="glass"] .pg-widget > .card.widget {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--pg-accent, #64748b) var(--pg-fx-top), transparent) 0%,
      transparent 58%),
    rgba(255, 255, 255, 0.58);
  border-color: color-mix(in srgb, var(--pg-accent, #64748b) var(--pg-fx-edge), transparent);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.40),
    0 6px 20px -20px rgba(15, 15, 35, 0.40);
}
html[data-bs-theme="dark"] [data-widget-theme="glass"] .pg-widget > .card.widget {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--pg-accent, #64748b) var(--pg-fx-top), transparent) 0%,
      transparent 58%),
    rgba(255, 255, 255, 0.045);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.07),
    0 8px 24px -22px #000;
}

/* ── Aurora ───────────────────────────────────────────────────────────────
   Two light sources across the diagonal: the widget's own accent at the top
   left, and a second bloom at the bottom right mixed from it.

   The second hue is derived rather than declared. Twenty-three widgets would
   otherwise need a second colour table that nothing else reads, and every one
   of them would be a chance for a pairing nobody checked. Mixing the accent
   with indigo keeps every pair inside a quarter turn of the wheel, so emerald
   lands on teal, pink lands on violet, and none of it has to be maintained.

   Both stops are the accent mixed INTO the card background, not the accent at
   an alpha over it. Over near black those two are not the same thing: the
   translucent version keeps the hue at full saturation and lays it on the text
   like a filter, while the mixed version is a darker relative of the card --
   the same way the flat theme's #0b1411 is a relative of emerald. That single
   change is most of the difference between the first pass and this one.

   The blooms are also pulled into the corners: 74% by 48%, dying at 58%. At
   the first pass's 88% by 66% they met in the middle of a square card, which
   turned a diagonal of light into a wash over the whole thing. */
[data-widget-theme="aurora"] .pg-widget > .card.widget {
  --pg-accent-shift: color-mix(in srgb, var(--pg-accent, #64748b) 55%, #6366f1);
  background:
    radial-gradient(74% 48% at 100% 100%,
      color-mix(in srgb, var(--pg-accent-shift) var(--pg-fx-two), var(--bs-card-bg)) 0%,
      transparent 58%),
    radial-gradient(74% 48% at 0% 0%,
      color-mix(in srgb, var(--pg-accent, #64748b) var(--pg-fx-one), var(--bs-card-bg)) 0%,
      transparent 58%),
    var(--bs-card-bg);
  border-color: color-mix(in srgb, var(--pg-accent, #64748b) var(--pg-fx-edge), transparent);
}

/* ══ Panel backdrop ════════════════════════════════════════════════════════

   The panel's content area, not the document body: the wash sits behind the
   widget grid and the icon rail keeps its own colour. Stored in
   dashboard.panel_backdrop, and "auto" is resolved in welcome.php from the card
   theme rather than here, because CSS cannot map one attribute onto another.

   background-attachment: fixed, so the wash reads as light in the room instead
   of as a pattern printed on a page that scrolls past it.

   Every alpha here is under .11 in light and under .17 in dark. These sit
   behind body text and behind the cards' own translucent surfaces; anything
   stronger stops being a backdrop and starts being a competing surface. There
   is no rule for "none" -- absence is the whole behaviour, and it is what the
   flat theme maps to. */
#content[data-panel-backdrop="mesh"],
#content[data-panel-backdrop="dusk"],
#content[data-panel-backdrop="ember"] {
  background-attachment: fixed;
  background-repeat: no-repeat;
}
#content[data-panel-backdrop="mesh"] {
  background-image:
    radial-gradient(58% 42% at 8% 0%,    rgba(124, 58, 237, 0.10), transparent 62%),
    radial-gradient(52% 44% at 94% 34%,  rgba(14, 165, 233, 0.09), transparent 62%),
    radial-gradient(48% 38% at 42% 100%, rgba(236, 72, 153, 0.07), transparent 62%);
}
#content[data-panel-backdrop="dusk"] {
  background-image:
    radial-gradient(62% 46% at 4% 0%,    rgba(79, 70, 229, 0.10), transparent 64%),
    radial-gradient(58% 48% at 100% 78%, rgba(13, 148, 136, 0.08), transparent 64%);
}
#content[data-panel-backdrop="ember"] {
  background-image:
    radial-gradient(60% 44% at 96% 0%, rgba(217, 119, 6, 0.10), transparent 64%),
    radial-gradient(54% 46% at 0% 82%, rgba(225, 29, 72, 0.08), transparent 64%);
}

/* Dark needs more of the same colour, not a different one: the identical alpha
   over near-black reads as nothing at all. */
html[data-bs-theme="dark"] #content[data-panel-backdrop="mesh"] {
  background-image:
    radial-gradient(58% 42% at 8% 0%,    rgba(139, 92, 246, 0.16), transparent 62%),
    radial-gradient(52% 44% at 94% 34%,  rgba(56, 189, 248, 0.13), transparent 62%),
    radial-gradient(48% 38% at 42% 100%, rgba(244, 114, 182, 0.10), transparent 62%);
}
html[data-bs-theme="dark"] #content[data-panel-backdrop="dusk"] {
  background-image:
    radial-gradient(62% 46% at 4% 0%,    rgba(129, 140, 248, 0.15), transparent 64%),
    radial-gradient(58% 48% at 100% 78%, rgba(45, 212, 191, 0.12), transparent 64%);
}
html[data-bs-theme="dark"] #content[data-panel-backdrop="ember"] {
  background-image:
    radial-gradient(60% 44% at 96% 0%, rgba(251, 191, 36, 0.14), transparent 64%),
    radial-gradient(54% 46% at 0% 82%, rgba(251, 113, 133, 0.11), transparent 64%);
}

/* ══ Card filters ══════════════════════════════════════════════════════════
   A card body marked data-pg-filterable carries a column of .pg-filter
   buttons and rows tagged data-kind; the page script shows the rows of the
   pressed button's kind (welcome.php). Each button names its kind, says how
   many came in today and, where there is a daily series, draws the week as
   seven bars. Its colour arrives inline as --pg-k (tile) and --pg-k-ink
   (glyph), so one card can carry eight kinds without eight rules here. */
.pg-filter {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  min-width: 0;
  padding: 0.35rem 0.45rem;
  border: 0;
  border-radius: var(--bs-border-radius-lg);
  background: transparent;
  color: var(--bs-body-color);
  font-size: 0.8125rem;
  line-height: 1.2;
  text-align: left;
  transition: background-color 0.15s ease;
}
.pg-filter:hover,
.pg-filter:focus-visible {
  background: var(--pg-tint);
}
/* The pressed kind: a tint of its own colour and a short bar on the edge
   facing the list, so it reads as the thing the list is showing. */
.pg-filter[aria-pressed="true"] {
  background: color-mix(in srgb, var(--pg-k, var(--pg-accent)) 14%, transparent);
  box-shadow: inset -3px 0 0 var(--pg-k, var(--pg-accent));
}
.pg-filter-ico {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--bs-border-radius);
  background: var(--pg-k, var(--pg-accent, #64748b));
  color: var(--pg-k-ink, var(--pg-accent-ink, #fff));
  font-size: 14px;
}
.pg-filter-text {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.pg-filter-label {
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pg-filter-sub {
  overflow: hidden;
  color: var(--bs-secondary-color);
  font-size: 0.6875rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pg-filter-count {
  flex: 0 0 auto;
  min-width: 1.5rem;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--pg-k, var(--pg-accent)) 18%, transparent);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  text-align: center;
}
.pg-filter-count.is-urgent {
  background: var(--bs-danger);
  color: #fff;
}
.pg-filter-count.is-clear {
  background: color-mix(in srgb, #10b981 18%, transparent);
}

/* A week of daily counts in seven bars (pg_widget_bars). Plain boxes rather
   than a canvas: a filter column holds up to eight of them and they are
   redrawn with the card. */
.pg-bars {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 14px;
}
.pg-bars > i {
  width: 3px;
  border-radius: 1px;
  background: var(--pg-k, var(--pg-accent));
  opacity: 0.8;
}
.pg-bars > i.is-zero {
  opacity: 0.25;
}

/* The way to the full list of the kind that is showing, under its rows. */
.pg-filter-more {
  display: block;
  margin: 0.25rem 0 0.5rem;
  padding: 0.35rem;
  border-radius: var(--bs-border-radius-lg);
  color: var(--pg-accent);
  font-size: 0.75rem;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
}
.pg-filter-more:hover,
.pg-filter-more:focus-visible {
  background: var(--pg-tint);
  color: var(--pg-accent);
}
.pg-filter-none .pg-widget-empty {
  padding: 2rem 0.5rem;
}

/* Activity (widget 7): the kinds down the left, the feed on the right. Stacked
   in a narrow card the kinds wrap into a row of chips above the feed, with the
   bars and the second line left out -- a column of eight full-width buttons
   would push the feed below the fold of a phone-width card. */
.pg-act > .pg-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  padding: 0.5rem;
}
.pg-act > .pg-filters .pg-filter {
  width: auto;
  padding: 0.2rem 0.45rem 0.2rem 0.2rem;
  border: var(--bs-border-width) solid var(--pg-rule);
  border-radius: 999px;
}
.pg-act > .pg-filters .pg-filter[aria-pressed="true"] {
  box-shadow: none;
  border-color: var(--pg-k, var(--pg-accent));
}
.pg-act > .pg-filters .pg-filter-ico {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  font-size: 11px;
}
.pg-act > .pg-filters .pg-bars,
.pg-act > .pg-filters .pg-filter-sub {
  display: none;
}
@container pgwidget (min-width: 30rem) {
  .pg-split.pg-act > .pg-filters {
    flex: 0 0 36%;
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 2px;
    max-width: 15rem;
    padding: 0.25rem 0.35rem;
  }
  /* 24px tiles and tight padding: eight kinds fit the height of a card
     without the column scrolling. */
  .pg-act > .pg-filters .pg-filter {
    width: 100%;
    padding: 0.25rem 0.45rem;
    border: 0;
    border-radius: var(--bs-border-radius-lg);
  }
  .pg-act > .pg-filters .pg-filter[aria-pressed="true"] {
    box-shadow: inset -3px 0 0 var(--pg-k, var(--pg-accent));
  }
  .pg-act > .pg-filters .pg-filter-ico {
    width: 24px;
    height: 24px;
    border-radius: var(--bs-border-radius);
    font-size: 12px;
  }
  .pg-act > .pg-filters .pg-bars {
    display: inline-flex;
  }
  .pg-act > .pg-filters .pg-filter-sub {
    display: block;
  }
}

/* Operations Desk (widget 9): what the shop owes, as three lanes -- to ship,
   to refund, out of stock. The lanes are .pg-filter buttons laid out as
   tiles across the top, each with its count large enough to read from the
   other side of the desk; the pressed lane's list fills the rest of the
   card. In a one-track card the tiles keep their row and drop the second
   line, so the list still starts within the first third of the card. */
.pg-ops {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.pg-lanes {
  flex: 0 0 auto;
  display: grid;
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  gap: 0.4rem;
  padding: 0.5rem 0.5rem 0.35rem;
}
.pg-lanes .pg-filter {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "ico text count";
  align-items: center;
  column-gap: 0.5rem;
  padding: 0.45rem 0.55rem;
  border: var(--bs-border-width) solid var(--pg-rule);
  background: var(--pg-tint);
}
.pg-lanes .pg-filter[aria-pressed="true"] {
  border-color: color-mix(in srgb, var(--pg-k, var(--pg-accent)) 50%, transparent);
  background: color-mix(in srgb, var(--pg-k, var(--pg-accent)) 12%, transparent);
  box-shadow: inset 0 -3px 0 var(--pg-k, var(--pg-accent));
}
.pg-lanes .pg-filter-ico {
  grid-area: ico;
  width: 32px;
  height: 32px;
  font-size: 16px;
}
.pg-lanes .pg-filter-text {
  grid-area: text;
}
.pg-lanes .pg-filter-count {
  grid-area: count;
  min-width: 0;
  padding: 0;
  background: none;
  font-size: 1.5rem;
  line-height: 1;
}
.pg-lanes .pg-filter-count.is-urgent {
  background: none;
  color: var(--bs-danger);
}
.pg-lanes .pg-filter-count.is-clear {
  background: none;
  color: #10b981;
}
@container pgwidget (max-width: 30rem) {
  .pg-lanes .pg-filter {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "ico count" "text text";
    row-gap: 0.25rem;
    padding: 0.4rem;
  }
  .pg-lanes .pg-filter-ico {
    width: 24px;
    height: 24px;
    font-size: 12px;
  }
  .pg-lanes .pg-filter-count {
    justify-self: end;
    font-size: 1.15rem;
  }
  .pg-lanes .pg-filter-sub {
    display: none;
  }
}
.pg-ops-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
}
.pg-ops-meter {
  padding: 0.15rem 1rem 0.1rem;
}

/* Best sellers in the Ecommerce Summary (widget 3): units sold as a quiet
   pill, then what they took. A product without a photo carries its rank on
   the tile instead. */
.pg-ec-qty {
  background: color-mix(in srgb, var(--pg-accent) 16%, transparent);
  color: var(--bs-body-color);
  font-size: 10px;
  font-weight: 600;
}
.pg-rank {
  font-size: 12px;
  font-weight: 700;
}

/* Top pages in Visitor Summaries (widget 5): the list takes the chart's
   place from the strip's last cell, at the same size, so the card does not
   move when it opens. */
.pg-vs-pages {
  flex: 1 1 auto;
  min-height: 4rem;
  margin: 2px -8px 0;
  overflow-x: hidden;
  overflow-y: auto;
}
.pg-vs-pages .pg-list {
  padding-top: 0;
}
.pg-vs-top .pg-vs-cell-label .bi {
  margin-left: 0.15rem;
  opacity: 0.8;
}

/* ══ Dashboard clock ═══════════════════════════════════════════════════════
   Was a centred display-3 stack that ate a fifth of the fold before the first
   widget appeared. Left aligned with the grid and set on one baseline, the same
   three facts cost roughly a third of the height.

   On a wide screen the brief and the clock share one row -- the sentence on
   the left where reading starts, the clock right-aligned at the far end with
   the next meeting under it -- so the whole bar is about as tall as the brief
   alone. Narrower, they stack, brief first. */
.pg-clockbar {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.25rem 0 1.25rem;
}
@media (min-width: 992px) {
  .pg-clockbar {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
  }
  .pg-clockbar > .pg-brief {
    align-self: center;
  }
  /* Parent-qualified: the base rules for these two come further down. */
  .pg-clockbar > .pg-clockbar-now {
    flex: 0 0 auto;
    align-items: flex-end;
    max-width: 40%;
    text-align: right;
  }
  .pg-clockbar .pg-clockbar-when {
    flex-direction: column;
    align-items: flex-end;
  }
}
/* The briefing.
   A mark and a short paragraph, laid out the way a remark is laid out rather
   than the way a stat line is: the chip reads as whoever is speaking, and the
   greeting and the line after it run on as one piece of prose instead of being
   separated by a bullet. Weight does the separating -- the greeting is the
   darker half -- which costs no punctuation and no second row.

   The sentence is capped at 62ch. Prose set across two thousand pixels is not
   read, it is scanned, and the whole point of writing this as a sentence is
   that it gets read. The card around it is allowed a little more, for its
   padding and the mark.

   A quiet card rather than bare text: a translucent surface over the panel
   backdrop, so the line reads as one object sitting on the page instead of a
   caption floating above the widgets. Translucent without a blur, for the
   reason the glass cards give: the wash behind it is too soft for a blur to
   show, and the filter was redone on every frame of a scroll. */
.pg-brief {
  position: relative;
  align-self: flex-start;
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  max-width: min(100%, 68ch);
  padding: 0.6rem 0.9rem 0.6rem 0.7rem;
  border: 1px solid color-mix(in srgb, var(--bs-border-color) 70%, transparent);
  border-radius: 0.85rem;
  background: color-mix(in srgb, var(--bs-body-bg) 72%, transparent);
}
html[data-bs-theme="dark"] .pg-brief {
  background: color-mix(in srgb, var(--bs-body-bg) 55%, transparent);
}
/* A thin stroke of the clock's gradient down the left edge, inset from the
   corners so it does not have to follow the radius. Positioned rather than
   clipped: overflow on the card would also clip the mark's ring. */
.pg-brief::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5rem;
  bottom: 0.5rem;
  width: 3px;
  border-radius: 3px;
  background: var(--clock-gradient);
  pointer-events: none;
}
.pg-brief-body {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  min-width: 0;
}

/* "Say something else": a quiet round button at the end of the card, the
   same size as the mark so the two ends of the card balance. It turns while
   the new line is on its way. */
.pg-brief-again {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  margin: 0 -0.35rem 0 0.1rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--bs-tertiary-color);
  font-size: 0.875rem;
  line-height: 1;
  transition: color 0.15s ease, background-color 0.15s ease;
}
.pg-brief-again:hover,
.pg-brief-again:focus-visible {
  color: var(--bs-body-color);
  background: var(--bs-secondary-bg);
}
.pg-brief-again .bi {
  display: inline-block;
}
.pg-brief-again .bi::before {
  vertical-align: 0;
}
.pg-brief-again.is-busy .bi {
  animation: pg-brief-spin 0.8s linear infinite;
}
@keyframes pg-brief-spin {
  to { transform: rotate(360deg); }
}

/* The speaker. It carries the hour's own glyph -- sunrise, sun, dusk, moon --
   so the mark is telling you something too, and takes the clock's gradient so
   the two halves of this bar are visibly one object. */
.pg-brief-mark {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  margin-top: 0;
  border-radius: 50%;
  background: var(--clock-gradient);
  box-shadow: 0 1px 6px var(--clock-glow);
  color: #fff;
  font-size: 0.8125rem;
  line-height: 1;
}
.pg-brief-mark .bi::before {
  vertical-align: 0;
  line-height: 1;
}

.pg-brief-text {
  max-width: 62ch;
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--bs-secondary-color);
}
/* The half with a name in it. */
.pg-brief-greet {
  color: var(--bs-body-color);
  font-weight: 600;
}
.pg-brief-line {
  color: var(--bs-secondary-color);
}

/* The way to whatever the sentence is about.
   Inside the sentence, on the words themselves -- "the 2 empty products" -- and
   not on a chip parked at the end. A chip is a second thing to read that says
   the same as the phrase before it, and it makes you look away from the words
   to find the target. Here the thing named and the thing clicked are one.

   Underlined rather than coloured: the line around it is already secondary ink
   and a third colour in one sentence is a decoration, not a signal. */
.pg-brief-link {
  color: var(--bs-body-color);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: var(--bs-border-color);
  text-underline-offset: 0.15em;
}
.pg-brief-link:hover,
.pg-brief-link:focus-visible {
  color: var(--bs-body-color);
  text-decoration-color: currentColor;
}
/* It arrives rather than being there, which is most of what makes it read as
   something said. A transform and an opacity, so nothing reflows while it
   plays -- and nothing at all for anyone who has asked for less movement. */
@media (prefers-reduced-motion: no-preference) {
  .pg-brief-text {
    animation: pg-brief-in 0.45s ease-out both;
  }
  /* The strip follows the sentence rather than arriving with it, so the eye
     lands on the words first. */
  .pg-brief-ws {
    animation: pg-brief-in 0.45s ease-out 0.18s both;
  }
  /* One soft ring from the mark as the line arrives, once, and then still. */
  .pg-brief-mark {
    animation: pg-brief-ring 0.9s ease-out 1;
  }
  /* A new line asked for with the button arrives the same way. The second
     set of keyframes is what restarts it: an element keeps a running
     animation for as long as its name stays the same. */
  .pg-brief.is-new .pg-brief-text {
    animation: pg-brief-again 0.45s ease-out both;
  }
  .pg-brief.is-new .pg-brief-mark {
    animation: pg-brief-ring-again 0.9s ease-out 1;
  }
}
@keyframes pg-brief-in {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: none; }
}
@keyframes pg-brief-ring {
  from { box-shadow: 0 1px 6px var(--clock-glow), 0 0 0 0 var(--clock-glow); }
  to   { box-shadow: 0 1px 6px var(--clock-glow), 0 0 0 0.55rem transparent; }
}
@keyframes pg-brief-again {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: none; }
}
@keyframes pg-brief-ring-again {
  from { box-shadow: 0 1px 6px var(--clock-glow), 0 0 0 0 var(--clock-glow); }
  to   { box-shadow: 0 1px 6px var(--clock-glow), 0 0 0 0.55rem transparent; }
}

/* The workspace strip: what was handed over, what is under way, what is late.
   Small pills under the sentence, the same few facts in the same place on
   every load -- the glance, where the sentence is the read. */
.pg-brief-ws {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.5rem;
}
.pg-brief-ws-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  max-width: 100%;
  padding: 0.15rem 0.55rem 0.15rem 0.45rem;
  border: 1px solid color-mix(in srgb, var(--bs-border-color) 60%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--bs-secondary-bg) 80%, transparent);
  color: var(--bs-body-color);
  font-size: 0.75rem;
  line-height: 1.4;
  text-decoration: none;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.pg-brief-ws-item:hover,
.pg-brief-ws-item:focus-visible {
  color: var(--bs-body-color);
  border-color: color-mix(in srgb, currentColor 45%, transparent);
  transform: translateY(-1px);
}
/* Something new: a small dot in the clock's colours ahead of the icon. */
.pg-brief-ws-item.is-new::before {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--clock-gradient);
}
.pg-brief-ws-item.is-late .bi,
.pg-brief-ws-item.is-late .pg-brief-ws-label {
  color: var(--bs-danger);
}
/* A timer that is running: a green dot that breathes, so the one item that
   goes on counting by itself looks alive. */
.pg-brief-ws-item.is-live::before {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--bs-success);
}
.pg-brief-ws-item.is-live .bi {
  color: var(--bs-success);
}
@media (prefers-reduced-motion: no-preference) {
  .pg-brief-ws-item.is-live::before {
    animation: pg-live 1.6s ease-in-out infinite;
  }
}
@keyframes pg-live {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.3; }
}
.pg-brief-ws-label {
  font-weight: 600;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}
.pg-brief-ws-text {
  min-width: 0;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pg-brief-ws-bar {
  flex: 0 0 auto;
  width: 3.5rem;
  height: 3px;
  border-radius: 3px;
  background: var(--bs-border-color);
  overflow: hidden;
}
.pg-brief-ws-bar > span {
  display: block;
  height: 100%;
  background: var(--clock-gradient);
}
.pg-brief-ws-pct {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
@media (max-width: 575.98px) {
  .pg-brief {
    padding: 0.5rem 0.65rem;
  }
  .pg-brief-ws-text {
    max-width: 14ch;
  }
}
.pg-clockbar-now {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.35rem;
  min-width: 0;
}
.pg-clockbar-when {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 0.7rem;
}
.pg-clockbar-time {
  font-size: 2.25rem;
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: -0.01em;
  /* The digits are replaced every minute by the clock poll; proportional
     figures would shift the date beside them whenever a 1 swaps for a 0. */
  font-variant-numeric: tabular-nums;
  color: var(--clock-fallback-color);
}
/* Progressive enhancement: gradient text where clipping is supported. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .pg-clockbar-time {
    background: var(--clock-gradient);
    color: transparent;
    -webkit-background-clip: text; /* Chrome/Safari/Edge */
    background-clip: text;          /* Standard (Firefox & modern browsers) */
    -webkit-text-fill-color: transparent;
    /* drop-shadow, not text-shadow: the fill is transparent, so a text shadow
       would sit behind the glyphs and read through them as a smudge instead of
       haloing them. A filter blooms what was actually painted. */
    filter: drop-shadow(0 0 var(--clock-bloom) var(--clock-glow));
  }
}
.pg-clockbar-date {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--bs-tertiary-color);
}

/* The next meeting or visit, under the clock: when, what, and how long until
   it starts. Every event left today is in the markup and only the first is
   shown; the page script removes one when it ends. */
.pg-clockbar-next {
  display: flex;
  max-width: 100%;
}
.pg-next + .pg-next {
  display: none;
}
.pg-next {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 100%;
  padding: 0.15rem 0.6rem 0.15rem 0.5rem;
  border: 1px solid color-mix(in srgb, var(--bs-border-color) 60%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--bs-body-bg) 72%, transparent);
  color: var(--bs-body-color);
  font-size: 0.75rem;
  line-height: 1.4;
  text-decoration: none;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
html[data-bs-theme="dark"] .pg-next {
  background: color-mix(in srgb, var(--bs-body-bg) 55%, transparent);
}
.pg-next:hover,
.pg-next:focus-visible {
  color: var(--bs-body-color);
  border-color: color-mix(in srgb, currentColor 45%, transparent);
  transform: translateY(-1px);
}
.pg-next .bi {
  color: var(--bs-secondary-color);
}
.pg-next-time {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.pg-next-title {
  min-width: 0;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--bs-secondary-color);
}
.pg-next-until {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--clock-fallback-color);
}
.pg-next-until:empty {
  display: none;
}
/* Under way: the countdown becomes "now" with the timer's breathing dot. */
.pg-next.is-now .pg-next-until {
  color: var(--bs-success);
}
.pg-next.is-now .pg-next-until::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 0.3rem;
  border-radius: 50%;
  background: currentColor;
  vertical-align: 0.1em;
}
@media (prefers-reduced-motion: no-preference) {
  .pg-next.is-now .pg-next-until::before {
    animation: pg-live 1.6s ease-in-out infinite;
  }
}
@media (max-width: 575.98px) {
  .pg-clockbar-time {
    font-size: 1.875rem;
  }
}

/* ──────────────────────────────────────────────────────────────────────
   Navbar brand: inline SVG logo lockup (icon + Pinegrap wordmark)
   ────────────────────────────────────────────────────────────────────
   Layout-shift notes — every rule here earns its place:
   - The lockup is inline-flex with line-height:1 so the SVG never sits on a
     text baseline. An inline SVG defaults to display:inline, which reserves
     descender space below it and pushes the 50px navbar row around.
   - .pinegrap-logo carries an explicit width AND height derived from one
     variable, so the box is known before the SVG paints (no CLS) and the
     version pill next to it never jumps.
   - Mobile shows the icon only. Rather than shipping a second copy of the
     artwork, the box is narrowed to the icon's slice of the viewBox and the
     wordmark is clipped away. The icon starts at x=0 in the viewBox, so a
     simple overflow:hidden crop is exact.
   ──────────────────────────────────────────────────────────────────── */
.pinegrap-logo-lockup {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  line-height: 1;
}

.pinegrap-logo {
  /* Only knob worth touching: everything below scales off this. */
  --pg-logo-h: 30px;
  /* viewBox 530.44 x 142.47 -> full lockup aspect ratio */
  --pg-logo-w: calc(var(--pg-logo-h) * 3.7232);
  /* icon occupies x 0..64.6 of the viewBox; 68 leaves a hair of clearance */
  --pg-logo-icon-w: calc(var(--pg-logo-h) * 0.4773);

  display: block;
  flex: 0 0 auto;
  height: var(--pg-logo-h);
  width: var(--pg-logo-w);
  overflow: hidden;
  line-height: 0;
  color: var(--pg-logo-fallback-color);
}

.pinegrap-logo > svg {
  display: block;
  height: 100%;
  width: var(--pg-logo-w);
  fill: url(#pgLogoGradient);
}

/* Below md: crop to the icon and re-scope the gradient to it, otherwise the
   icon would only ever show the first 12% of the full-lockup ramp. */
@media (max-width: 767.98px) {
  .pinegrap-logo {
    width: var(--pg-logo-icon-w);
  }
  .pinegrap-logo > svg {
    fill: url(#pgLogoGradientIcon);
  }
}

.pinegrap-version {
  font-size: 0.65em;
    position: absolute;
    bottom: -5px;
    left: 18px;
}

/* ──────────────────────────────────────────────────────────────────────
   WCAG 2.1 AA baseline
   ──────────────────────────────────────────────────────────────────── */

/* Skip-to-content link: visually hidden until focused. */
.pg-skip-link {
  position: fixed;
  top: -100px;
  left: 8px;
  z-index: 2000;
  padding: 0.5rem 1rem;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  border: 2px solid var(--pg-primary-ink);
  border-radius: var(--bs-border-radius);
  text-decoration: none;
  font-weight: 600;
  transition: top 0.15s ease-in-out;
}
.pg-skip-link:focus,
.pg-skip-link:focus-visible {
  top: 8px;
  outline: 3px solid var(--pg-primary-ink);
  outline-offset: 2px;
}

/* Ensure a visible focus ring on every interactive element.
   Bootstrap covers most cases; this hardens custom widgets and any
   element where focus-visible was suppressed by inline styles. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible,
.btn:focus-visible,
.nav-link:focus-visible,
.dropdown-item:focus-visible,
.breadcrumb-item a:focus-visible {
  outline: 2px solid var(--pg-primary-ink);
  outline-offset: 2px;
  box-shadow: 0 0 0 0.2rem rgba(var(--pg-primary-rgb), 0.35);
}

/* Some legacy admin links carry inline `outline:0` style; force visible focus. */
a.no-popover:focus-visible,
button.no-popover:focus-visible {
  outline: 2px solid var(--pg-primary-ink) !important;
  outline-offset: 2px;
}


/* ── Admin screen toolbar ─────────────────────────────────────────────────
   The house pattern for a management screen: everything the screen can do
   sits in ONE bar under the page header, and the content below it stays
   plain. First built inside view_folders.php (the file manager) and lifted
   here so every screen can wear it instead of re-inventing a bar of its own -
   which is how settings.php ended up with six different button recipes.

   Markup:

     <div class="pg-toolbar">
       <div class="dropdown">…primary: btn btn-sm btn-primary rounded-pill px-3…</div>
       <button class="btn btn-sm btn-ghost">…secondary tools…</button>
       <div class="pg-toolbar-grow">…breadcrumb / title / location…</div>
       <div class="input-group input-group-sm rounded-pill pg-toolbar-search">…</div>
       <div class="btn-group btn-group-sm">…view switches: btn-ghost + .active…</div>
       <div class="dropdown ms-auto">…overflow: btn-ghost with bi-three-dots-vertical…</div>
     </div>

   Rules that go with it: one recipe per intent, more than four buttons go
   in a menu, icons always inside <i class="bi …">. */

.pg-toolbar,
#button_bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .35rem;
  padding: .5rem .75rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
  min-width: 0;
  max-width: 100%;
}

/* What the page bar takes from the toolbar and what it leaves.
   The explorer's toolbar is the top edge of a panel that runs to the window, so
   the side padding and the rule under it are edges of that panel. The page bar
   has no panel: it sits in the page gutter under the heading, where the padding
   would start the first action three quarters of a rem right of the cards below
   it, and the rule would just be a line drawn across the page.

   It sits here rather than with the rest of #button_bar because it is a
   correction to the block above and has to come after it -- the two selectors
   weigh the same, so the later one wins. */
#button_bar {
  padding-left: 0;
  padding-right: 0;
  border-bottom: 0;
  margin-bottom: 0.75rem;
}

/* Menus inside the bar anchor to their own button, not to the page, so a
   dropdown near the right edge does not push the layout sideways. */
.pg-toolbar .dropdown,
#button_bar .dropdown { position: relative; }
.pg-toolbar .dropdown-menu,
#button_bar .dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: .25rem;
  z-index: 1075;

  /* A long menu is not allowed to run off the bottom of the window, and it
     cannot be trusted to stay inside a panel that clips its overflow either -
     the file manager's app shell does exactly that. It scrolls instead. */
  max-height: calc(100vh - 10rem);
  overflow-y: auto;
}
.pg-toolbar .dropdown-menu.dropdown-menu-end,
#button_bar .dropdown-menu.dropdown-menu-end { left: auto; right: 0; }

/* The one element that takes the slack - a breadcrumb, a screen title, a
   filter summary. Everything else keeps its natural width. */
.pg-toolbar-grow { flex: 1 1 220px; min-width: 160px; overflow: hidden; }

.pg-toolbar-search { width: 200px; }

/* Ghost button: a toolbar full of outlined buttons reads as a row of boxes,
   so secondary tools carry no border until they are hovered. Defined through
   Bootstrap's own button variables, which is what makes it follow the light
   and dark themes without a second rule.

   The house rule for a button with no variant on it (the long :not() chain
   further up) has to exclude this one by name. That selector carries a class
   for every :not() in it - eighteen of them - so it outweighs a single class
   many times over, and this block would never have taken effect. It went
   unnoticed because the file manager scoped its own copy under an id, and an
   id beats any number of classes; the moment that screen moved onto the
   shared class the buttons came back with borders. */
.btn-ghost {
  --bs-btn-color: var(--bs-secondary-color);
  --bs-btn-hover-color: var(--bs-body-color);
  --bs-btn-hover-bg: var(--bs-secondary-bg);
  --bs-btn-active-color: var(--bs-body-color);
  --bs-btn-active-bg: var(--bs-secondary-bg);
  --bs-btn-border-color: transparent;
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-border-color: transparent;
}

/* A view switch that is currently on. Not :active - that is the pressed
   state and lasts a moment; this one has to hold. */
.btn-ghost.active {
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
}

/* Narrow screens: the controls keep the first line, and the two elements that
   actually need width - the location and the search box - each get a line of
   their own. Without this the location gets about two hundred pixels to say
   where you are, which is the one thing it cannot do in two hundred pixels. */
@media (max-width: 767.98px) {

  .pg-toolbar,
  #button_bar { row-gap: .45rem; }

  .pg-toolbar-grow,
  .pg-toolbar-search {
    order: 9;
    flex: 1 1 100%;
    width: auto !important;
    min-width: 0;
  }

  .pg-toolbar-search { order: 10; }
}

/* The orders list's statistics strip (view_orders.php): a sparkline under a
   figure, and the two small charts beside it. Chart.js draws with
   maintainAspectRatio off, so the box sets the height. */
.pg-orders-spark { position: relative; height: 56px; }
.pg-orders-chart { position: relative; height: 160px; }

/* The design list's shared components panel carries a six column table;
   Bootstrap's 400px offcanvas would wrap every cell. */
#sd-comp-offcanvas { --bs-offcanvas-width: min(100vw, 860px); }

/* ── Settings screen section anchors ──────────────────────────────────────
   The settings cards carry an id (#pgset-security, #pgset-waf, …) so the
   global search can link straight to a section of that very long screen.
   The screen has a sticky header, so without this the browser parks the
   card title underneath it and the operator lands on a heading they cannot
   read. The value is the header height plus a little air.

   The same applies to #pgsub- ids, the jump points inside the longer sections,
   and to the product screen's #pg_pb_sec_ ids -- all three are reached from a
   rail the same way and land under the header for the same reason. */
[id^="pgset-"],
[id^="pgsub-"],
[id^="pg_pb_sec_"] { scroll-margin-top: 5rem; }


/* ── Admin screen step rail ───────────────────────────────────────────────
   A numbered rail down the left edge of a screen whose sections are read in
   order, top to bottom (edit_offer.php). Bootstrap has no stepper, so the
   rail, its line and its node live here; everything inside a step is an
   ordinary card and takes its widths from the grid and the utilities. */

.pg-step { display: grid; grid-template-columns: 2.75rem minmax(0, 1fr); }
.pg-step + .pg-step { margin-top: .75rem; }

.pg-step-rail { position: relative; display: flex; justify-content: center; }
.pg-step-rail::before {
  content: "";
  position: absolute;
  top: 2.3rem;
  bottom: -.75rem;
  width: 2px;
  background: var(--bs-border-color);
}
.pg-step:last-child .pg-step-rail::before { display: none; }

.pg-step-node {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  margin-top: .5rem;
  border: 2px solid var(--bs-border-color);
  border-radius: 50%;
  background: var(--bs-body-bg);
  color: var(--bs-secondary-color);
  font-size: .8rem;
  font-weight: 700;
}
.pg-step-done .pg-step-node { border-color: var(--bs-success); color: var(--bs-success); }
.pg-step-todo .pg-step-node { border-color: var(--bs-danger); color: var(--bs-danger); }

@media (max-width: 575.98px) {
  .pg-step { grid-template-columns: 1.9rem minmax(0, 1fr); }
}

/* ══ Settings screen ══════════════════════════════════════════════════════
   The layout is Bootstrap's: a row, a column for the rail, a column for the
   sections. What is here is the rail's own appearance, the order the sections
   are read in, and a type scale -- nothing that the grid already does. */
.pg-settings-nav {
  position: sticky;
  /* Against the navbar's height token rather than a number copied from it, so
     the rail keeps its distance if the header ever changes. */
  top: calc(var(--pg-navbar-h) + 0.75rem);
  display: flex;
  flex-direction: column;
  gap: 1px;
  max-height: calc(100vh - var(--pg-navbar-h) - 2rem);
  overflow-y: auto;
}
.pg-settings-group {
  padding: 0.75rem 0.5rem 0.25rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}
.pg-settings-group:first-child {
  padding-top: 0;
}
.pg-settings-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem 0.5rem;
  border-radius: var(--bs-border-radius);
  color: var(--bs-body-color);
  font-size: 0.8125rem;
  line-height: 1.3;
  text-decoration: none;
}
.pg-settings-link i {
  flex: 0 0 1rem;
  font-size: 0.875rem;
  text-align: center;
  opacity: 0.7;
}
.pg-settings-link span {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pg-settings-link:hover,
.pg-settings-link:focus-visible {
  background: var(--pg-tint);
  color: var(--bs-body-color);
}
/* The section under the reading line. Marked by weight, a tint and an edge
   rather than by a fill: the rail sits beside twelve cards that all carry
   tints of their own, and a filled row would read as a thirteenth. */
.pg-settings-link.is-active {
  background: var(--pg-tint-strong);
  box-shadow: inset 2px 0 0 var(--bs-primary);
  font-weight: 600;
}
.pg-settings-link.is-active i {
  opacity: 1;
  color: var(--bs-primary);
}

/* Jump points inside the commerce section. Indented to the icon column of the
   rows above them, so the indent alone says these belong to the section they
   sit under rather than standing beside it. */
.pg-settings-subs {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.pg-settings-sublink {
  padding-left: 2rem;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
.pg-settings-sublink:hover,
.pg-settings-sublink:focus-visible {
  color: var(--bs-body-color);
}

/* A notch smaller through the whole screen. Twelve sections of settings is a
   dense page whatever the layout, and the card titles were h5 -- a heading
   size, for what is really a label on a group of fields. */
.pg-settings-body .card-header {
  padding-bottom: 0.25rem;
  font-size: 0.75rem !important;
  letter-spacing: 0.05em;
}
.pg-settings-body .card-header .h5,
.pg-settings-body .card-header span.h5 {
  font-size: 0.75rem !important;
  letter-spacing: 0.05em;
}
.pg-settings-body .form-label {
  margin-bottom: 0.25rem;
  font-size: 0.8125rem;
}
.pg-settings-body .form-check-label,
.pg-settings-body .form-select,
.pg-settings-body .form-control {
  font-size: 0.8125rem;
}
.pg-settings-body .form-text {
  font-size: 0.75rem;
}
.pg-settings-body h6 {
  font-size: 0.6875rem;
}

/* Section rail placement, shared by every screen that has one.
   <main> becomes two columns from xl up: the rail, then everything the page
   drew. Below xl it is an ordinary block again and the page is untouched --
   which is why the wrapper exists at all, so there is one thing to switch on
   rather than a layout per screen. */
@media (min-width: 1200px) {
  main.pg-has-rail {
    display: flex;
    /* No align-items here on purpose. The rail has to be as tall as the page
       beside it, because the nav inside it is sticky and a sticky box inside a
       box its own size has nowhere to travel -- it scrolls away with the form.
       flex-start sizes the item to its contents and is exactly what breaks it;
       stretch is the default and is the one that is wanted. */
    gap: 1.5rem;
  }
  main.pg-has-rail > .pg-settings-menu {
    flex: 0 0 13rem;
    width: 13rem;
  }
  main.pg-has-rail > .pg-page-body {
    flex: 1 1 auto;
    min-width: 0;
  }
}
/* Below the split the rail is a panel under the header, the same one the
   settings screen has always used. */
@media (max-width: 1199.98px) {
  main.pg-has-rail > .pg-settings-menu {
    margin-bottom: 1rem;
  }
}

/* The rail arrives after the first paint: the shell renders the slot hidden and
   the script lifts it out beside the page once the DOM is there. Without this
   the screen drew full width and then lost 232px of it in one frame, and every
   line of the form slid sideways under the eye after it was already readable.

   So the space is booked here from markup the server already knows --
   .pg-awaiting-rail goes on .software-content for any screen that asked for a
   rail -- and initSectionRail drops the class in the same breath as it does the
   move. The content box lands on the same x either way, so there is nothing to
   see. Measured: drift is 0px both axes with this, 232px and 16px without.

   Written through #content rather than a class pair because #content carries a
   padding of its own from an id selector, and a class pair loses to it -- the
   reservation was silently doing nothing until this was keyed the same way. */
@media (min-width: 1200px) {
  .software-content.pg-awaiting-rail > #content {
    padding-left: calc(1rem + 13rem + 1.5rem);
  }
}
@media (max-width: 1199.98px) {
  /* Below the split the rail is a toggle above the form, so what it needs is
     height: #content's own 1rem plus the button. The 1rem margin under the
     button is deliberately not counted -- it collapses and does not move the
     form, so booking it would trade one jump for another. */
  .software-content.pg-awaiting-rail > #content {
    padding-top: calc(1rem + var(--pg-tap));
  }
}

/* The button and the rail travel together: the panel hangs off this box, so
   it lands under the button rather than under whatever happens to be the
   nearest positioned ancestor. */
.pg-settings-menu {
  position: relative;
  /* No height here on purpose either, and this one is the trap. The box does
     have to be as tall as the page beside it -- but the flex row above already
     does that by stretching it. height: 100% looks like it says the same thing
     and does the opposite: a declared cross size means the item is no longer
     auto-sized, stretch is skipped, and the box comes out the height of the
     nav. That is what leaves the rail scrolling away with the form. */
}

/* The toggle exists only where the rail becomes a panel; Bootstrap's own
   d-lg-none keeps it off the desktop layout. */
.pg-settings-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  min-height: var(--pg-tap);
  padding: 0 0.75rem;
  border: var(--bs-border-width) solid var(--bs-border-color-translucent);
  border-radius: var(--bs-border-radius);
  background: var(--pg-tint);
  color: var(--bs-body-color);
  font-size: 0.8125rem;
  font-weight: 600;
  text-align: left;
}
.pg-settings-toggle-text {
  flex: 1 1 auto;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pg-settings-toggle-icon {
  flex: 0 0 auto;
  color: var(--bs-primary);
}
.pg-settings-toggle-chev {
  flex: 0 0 auto;
  font-size: 0.7rem;
  opacity: 0.6;
  transition: transform 0.15s ease;
}
.pg-settings-toggle[aria-expanded="true"] .pg-settings-toggle-chev {
  transform: rotate(180deg);
}

/* Where the grid stacks the two columns the rail stops being a column and
   becomes a panel that drops from under the header. It overlays the form
   rather than pushing it down, so choosing a section does not move the page
   under the finger that is choosing. */
@media (max-width: 1199.98px) {
  .pg-settings-toggle {
    position: sticky;
    top: calc(var(--pg-navbar-h) + 0.25rem);
    z-index: 3;
  }
  .pg-settings-nav {
    position: absolute;
    top: calc(100% + 0.25rem);
    right: 0;
    left: 0;
    z-index: 3;
    display: none;
    max-height: 60vh;
    padding: 0.5rem;
    border: var(--bs-border-width) solid var(--bs-border-color-translucent);
    border-radius: var(--bs-border-radius-lg);
    background: var(--bs-body-bg);
    box-shadow: var(--bs-box-shadow);
  }
  .pg-settings-nav.is-open {
    display: flex;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pg-settings-toggle-chev {
    transition: none;
  }
}

/* ── Loading curtain ──────────────────────────────────────────────────────
 * Printed straight after <body> by pg_preloader_markup(), and raised again
 * when a link is clicked, so the panel stops flashing between screens.
 *
 * It is the page's own background with a bar across the top and nothing
 * else. This is on every screen dozens of times an hour; anything with a
 * personality of its own is tiring by the third visit.
 *
 * The failsafe animation is the point of the block: if the inline script
 * never runs — an error above it, a blocked inline script — the curtain
 * would otherwise sit over an unusable panel forever. Ten seconds is long
 * enough that a slow page never sees it and short enough that a broken one
 * is not lost.
 * ---------------------------------------------------------------------- */
.pg-preloader {
    position: fixed;
    inset: 0;
    z-index: 20000;
    background: var(--bs-body-bg, #fff);
    opacity: 1;
    visibility: visible;
    transition: opacity .26s ease, visibility 0s linear 0s;
    animation: pg-preloader-failsafe 0s linear 10s forwards;
}
/* visibility waits for the fade, so the hidden curtain is out of the layer
   tree entirely rather than sitting invisibly on top of every screen. */
.pg-preloader.is-done {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .26s ease, visibility 0s linear .26s;
}

.pg-preloader-bar {
    position: absolute; top: 0; left: 0; right: 0; height: 3px;
    overflow: hidden; background: rgba(128, 128, 128, .16);
}
.pg-preloader-bar i {
    position: absolute; top: 0; bottom: 0; left: -40%; width: 40%;
    background: linear-gradient(90deg, transparent, var(--bs-primary, #6366f1), transparent);
    animation: pg-preloader-slide 1.05s cubic-bezier(.4, 0, .2, 1) infinite;
}
@keyframes pg-preloader-slide { to { left: 100%; } }

/* The mark in the middle: the brand lockup with the dots under it, or on a
   private-label install the dots alone. Held back a beat — a page that
   arrives promptly shows the bar and nothing else, which is the difference
   between "loading" and a logo flashing at somebody twice a minute. */
.pg-preloader-mark {
    position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    display: flex; flex-direction: column; align-items: center; gap: 18px;
    opacity: 0;
    animation: pg-preloader-appear .45s ease .3s forwards;
}
.pg-preloader-logo { display: block; width: min(210px, 46vw); }
.pg-preloader-logo svg { display: block; width: 100%; height: auto; }
.pg-preloader-dots {
    display: flex; gap: 7px;
}
.pg-preloader-dots i {
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--bs-secondary-color, #6c757d);
    animation: pg-preloader-pulse 1.1s ease-in-out infinite;
}
.pg-preloader-dots i:nth-child(2) { animation-delay: .15s; }
.pg-preloader-dots i:nth-child(3) { animation-delay: .3s; }
@keyframes pg-preloader-appear { to { opacity: 1; } }
@keyframes pg-preloader-pulse { 0%, 100% { opacity: .25; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-3px); } }
@keyframes pg-preloader-failsafe { to { opacity: 0; visibility: hidden; pointer-events: none; } }

@media (prefers-reduced-motion: reduce) {
    .pg-preloader { transition: none; }
    .pg-preloader-bar i { animation: none; left: 0; width: 100%; opacity: .5; }
    .pg-preloader-mark { animation: none; opacity: 1; }
    .pg-preloader-dots i { animation: none; opacity: .6; transform: none; }
}
@media print { .pg-preloader { display: none !important; } }

/* ══ Admin screen — user permission rows ══════════════════════════════════
   Shared by add_user.php and edit_user.php through includes/user_permissions.php.
   One row per rights group: gate switch, glyph, title, a live summary of what
   is selected, and a button that opens the group's offcanvas panel. Everything
   here is layout and state; the panels themselves are Bootstrap offcanvas. */
.pg-perms {
  display: flex;
  flex-direction: column;
}
.pg-perm {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 1rem;
  border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
}
.pg-perm:first-child {
  border-top: 0;
}
/* The gate is a bare switch: its label is visually-hidden because the title
   beside it already names the row, and a second copy would be read twice. */
.pg-perm-gate-box {
  min-height: 0;
  margin-bottom: 0;
  padding-left: 2.25rem;
  flex: 0 0 auto;
}
.pg-perm-icon {
  flex: 0 0 auto;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: var(--bs-border-radius);
  display: grid;
  place-items: center;
  font-size: 0.9375rem;
  /* Set per row from PHP; the tint is derived so one value carries both. */
  color: var(--pg-perm-color, var(--bs-secondary-color));
  background: color-mix(in srgb, var(--pg-perm-color, currentColor) 16%, transparent);
}
.pg-perm-text {
  min-width: 0;
  flex: 1 1 auto;
}
.pg-perm-text b {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.25;
}
.pg-perm-text span {
  display: block;
  margin-top: 0.125rem;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
.pg-perm-summary {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  flex-wrap: wrap;
  justify-content: flex-end;
  font-size: 0.75rem;
  white-space: nowrap;
}
.pg-perm-pill {
  padding: 0.1875rem 0.5rem;
  border-radius: var(--bs-border-radius-pill);
  border: var(--bs-border-width) solid var(--bs-primary-border-subtle);
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary-text-emphasis);
  font-weight: 600;
}
/* A counter with nothing in it is not drawn at all: an empty group already says
   so through the dimmed row, and a row of zeroes reads as broken. */
.pg-perm-pill:empty {
  display: none;
}
.pg-perm-none {
  color: var(--bs-secondary-color);
  opacity: 0.75;
}
.pg-perm-open {
  flex: 0 0 auto;
}
/* Off: the row stays readable but stops competing with the ones that are on.
   The gate itself keeps full contrast so it can still be found and clicked. */
.pg-perm.is-off .pg-perm-icon,
.pg-perm.is-off .pg-perm-text,
.pg-perm.is-off .pg-perm-summary {
  opacity: 0.45;
}
/* Dimmed, not hidden: the panel is where the first selection is made, so a
   group that is off still has to be reachable. */
.pg-perm.is-off .pg-perm-open {
  opacity: 0.55;
}

/* The whole block is hidden for the roles that carry every right; the notice
   below takes its place. */
.pg-perm-everything {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
}
.pg-perm-everything > i {
  font-size: 1.125rem;
  line-height: 1.4;
}
.pg-perm-everything-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin-top: 0.5rem;
}
.pg-perm-everything-list span {
  padding: 0.125rem 0.5rem;
  border-radius: var(--bs-border-radius-sm);
  background: var(--pg-tint);
  font-size: 0.6875rem;
}

/* ── The panels ───────────────────────────────────────────────────────────
   Wider than Bootstrap's default because a folder tree indents, and a tree that
   wraps is unreadable. Capped at the viewport so a narrow screen still shows
   the page behind it. */
.pg-perm-panel {
  --bs-offcanvas-width: min(32rem, 100vw - 2rem);
}
.pg-perm-panel .offcanvas-header {
  align-items: center;
  border-bottom: var(--bs-border-width) solid var(--bs-border-color-translucent);
}
.pg-perm-panel .offcanvas-title {
  font-size: 1rem;
  font-weight: 600;
}
.pg-perm-panel .offcanvas-body {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.pg-perm-panel-lead {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
}
.pg-perm-panel-lead:empty {
  display: none;
}
.pg-perm-panel .offcanvas-footer {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
.pg-perm-panel-count {
  flex: 1 1 auto;
}
.pg-perm-block-title {
  margin-bottom: 0.375rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}
/* The list scrolls inside the panel rather than inside the page - the reason
   the panel exists. max-height is on the card body as before, but now it is the
   only scrolling box on screen, so the wheel cannot land in the wrong one. */
.pg-perm-list {
  margin-bottom: 0;
}
.pg-perm-list .card-body {
  max-height: 22rem;
  overflow: auto;
  padding-top: 0.5rem;
}
.pg-perm-switches {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}
.pg-perm-switch .form-check-label {
  font-size: 0.8125rem;
}
.pg-perm-switch .form-text {
  margin-top: 0.125rem;
  padding-left: 2.5rem;
  font-size: 0.75rem;
}

@media (max-width: 767.98px) {
  /* The summary and the button drop under the title rather than squeezing it;
     the gate and the glyph keep the first line. */
  .pg-perm {
    flex-wrap: wrap;
  }
  .pg-perm-text {
    flex: 1 1 60%;
  }
  .pg-perm-summary {
    order: 3;
    flex: 1 1 100%;
    justify-content: flex-start;
    padding-left: 4.75rem;
  }
  .pg-perm-open {
    order: 2;
  }
}

/* ══ Admin screen — user identity header ══════════════════════════════════
   edit_user.php: who this account is, plus the actions that act on the account
   itself rather than on the form (sign in as, reset password, unlock). */
.pg-identity {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  flex-wrap: wrap;
}
.pg-identity-pic {
  flex: 0 0 auto;
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 50%;
  object-fit: cover;
  display: grid;
  place-items: center;
  font-size: 1.0625rem;
  font-weight: 600;
  color: #fff;
  background: var(--users-color, #7b1fa2);
}
.pg-identity-who {
  min-width: 0;
}
.pg-identity-name {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  font-size: 1rem;
  font-weight: 600;
}
.pg-identity-meta {
  margin-top: 0.1875rem;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
.pg-identity-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* ══ Admin screen — account side cards ════════════════════════════════════
   edit_user.php's right column: contact, sign-in and sessions, recent activity.
   Rows of read-mostly facts, each with at most one action. */
.pg-fact {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  padding: 0.5625rem 0;
  border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
}
.pg-fact:first-child {
  border-top: 0;
  padding-top: 0;
}
.pg-fact-icon {
  flex: 0 0 auto;
  width: 1.625rem;
  height: 1.625rem;
  border-radius: var(--bs-border-radius);
  display: grid;
  place-items: center;
  font-size: 0.8125rem;
  background: var(--pg-tint);
  color: var(--bs-secondary-color);
}
.pg-fact-text {
  min-width: 0;
  flex: 1 1 auto;
}
.pg-fact-text b {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
}
.pg-fact-text span {
  display: block;
  margin-top: 0.125rem;
  font-size: 0.6875rem;
  color: var(--bs-secondary-color);
}
.pg-fact-action {
  margin-left: auto;
  flex: 0 0 auto;
}

.pg-contact-card {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.pg-contact-pic {
  flex: 0 0 auto;
  width: 3.25rem;
  height: 3.25rem;
  border-radius: var(--bs-border-radius-lg);
  object-fit: cover;
}
.pg-contact-who {
  min-width: 0;
}
.pg-contact-who b {
  display: block;
  font-size: 0.84375rem;
  font-weight: 600;
}
.pg-contact-who span {
  display: block;
  margin-top: 0.125rem;
  font-size: 0.71875rem;
  color: var(--bs-secondary-color);
  overflow: hidden;
  text-overflow: ellipsis;
}

.pg-log-line {
  display: flex;
  gap: 0.625rem;
  padding: 0.4375rem 0;
  border-top: var(--bs-border-width) dashed var(--bs-border-color-translucent);
  font-size: 0.71875rem;
}
.pg-log-line:first-child {
  border-top: 0;
}
.pg-log-when {
  flex: 0 0 5rem;
  color: var(--bs-secondary-color);
}
.pg-log-what {
  min-width: 0;
  flex: 1 1 auto;
}
.pg-log-ip {
  flex: 0 0 auto;
  color: var(--bs-secondary-color);
}

/* Sessions inside their panel: device and badges on the first line, the dates
   on the second. A six column table wraps the device name onto three lines at
   this width, which is why this is not a table. */
.pg-session {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6875rem 0.875rem;
  border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
}
.pg-session:first-child {
  border-top: 0;
}
.pg-session > i {
  flex: 0 0 auto;
  font-size: 1.0625rem;
  color: var(--bs-secondary-color);
}
.pg-session-text {
  min-width: 0;
  flex: 1 1 auto;
}
.pg-session-name {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  flex-wrap: wrap;
  font-size: 0.78125rem;
  font-weight: 600;
}
.pg-session-meta {
  margin-top: 0.1875rem;
  font-size: 0.6875rem;
  color: var(--bs-secondary-color);
}
.pg-session-actions {
  margin-left: auto;
  flex: 0 0 auto;
  display: flex;
  gap: 0.375rem;
}

/* Role picker: radio cards instead of a picklist, so the four roles and what
   each one means are readable without opening anything. btn-check hides the
   input; the label is the card. */
.pg-roles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.625rem;
}
.pg-role-card {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0.5625rem 0.75rem;
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-body-bg);
  cursor: pointer;
}
.pg-role-card b {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  font-size: 0.8125rem;
  font-weight: 600;
}
.pg-role-card b i {
  font-size: 0.875rem;
  color: var(--bs-secondary-color);
}
.pg-role-card span {
  display: block;
  margin-top: 0.1875rem;
  padding-left: 1.3125rem;
  font-size: 0.71875rem;
  color: var(--bs-secondary-color);
}
.btn-check:checked + .pg-role-card {
  border-color: var(--bs-primary);
  background: var(--bs-primary-bg-subtle);
}
.btn-check:checked + .pg-role-card b,
.btn-check:checked + .pg-role-card b i {
  color: var(--bs-primary-text-emphasis);
}
.btn-check:focus-visible + .pg-role-card {
  outline: 0;
  box-shadow: 0 0 0 0.25rem var(--bs-focus-ring-color);
}
.btn-check:disabled + .pg-role-card {
  opacity: 0.6;
  cursor: default;
}

/* While a side panel is open the chat launcher would cover its footer: it is
   fixed at a higher z-index than Bootstrap's offcanvas. */
body.pg-side-panel-open .pg-chat-launcher {
  display: none;
}

/* Rows inside a permission list. Flex rather than Bootstrap's float, so a row
   can carry something after its label - the expiry box below - and so that box
   can take a line of its own by asking for the full basis. */
.pg-perm-list .form-check {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding-left: 0;
  margin-bottom: 0.125rem;
}
.pg-perm-list .form-check > .form-check-input {
  flex: 0 0 auto;
  float: none;
  margin: 0;
}
.pg-perm-list .form-check > .form-check-label {
  flex: 1 1 auto;
  min-width: 0;
}

/* Expiration date on a private-folder grant (get_acl_folder_tree('view')).
   Closed it is a button at the end of the folder's row and costs no height;
   open it takes the next line, because the label before it is as wide as the
   folder name plus its indentation and an inline field would wrap into the
   checkbox column. */
.pg-acl-expiry {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
}
.pg-acl-expiry.is-open {
  flex: 1 1 100%;
  gap: 0.5rem;
  margin: 0.125rem 0 0.375rem 1.75rem;
}
.pg-acl-expiry-add {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  padding: 0.125rem 0.5rem;
  border: var(--bs-border-width) dashed var(--bs-border-color);
  border-radius: var(--bs-border-radius-pill);
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: 0.6875rem;
  line-height: 1.4;
  white-space: nowrap;
}
.pg-acl-expiry-add:hover,
.pg-acl-expiry-add:focus-visible {
  border-color: var(--bs-primary);
  color: var(--bs-primary-text-emphasis);
}
.pg-acl-expiry.is-open .pg-acl-expiry-add {
  display: none;
}
.pg-acl-expiry-field {
  display: none;
  align-items: center;
  gap: 0.5rem;
}
.pg-acl-expiry.is-open .pg-acl-expiry-field {
  display: flex;
}
.pg-acl-expiry-label {
  white-space: nowrap;
}
.pg-acl-expiry-input.form-control {
  flex: 0 0 auto;
  width: 8rem;
  font-size: 0.75rem;
}
.pg-acl-expiry-clear {
  border: 0;
  background: transparent;
  padding: 0.125rem 0.25rem;
  color: var(--bs-secondary-color);
  font-size: 0.6875rem;
  line-height: 1;
}
.pg-acl-expiry-clear:hover,
.pg-acl-expiry-clear:focus-visible {
  color: var(--bs-danger-text-emphasis);
}

/* How much of a list is selected, beside its "select all" switch. The switch
   itself carries the third state (indeterminate) that Bootstrap already draws. */
.pg-perm-list .card-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.pg-perm-list-count {
  margin-left: auto;
  font-size: 0.6875rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}
/* jQuery UI hangs the calendar off <body> with an inline z-index it works out
   from the field's ancestors - 999 here, which is under Bootstrap's offcanvas
   (1045) and modal (1055). The panel would cover the calendar it just opened,
   so the value has to be forced past both. */
#ui-datepicker-div {
  z-index: 1065 !important;
}


/* ══ Site Settings: the hub and the category screens ══════════════════════
   The settings were one screen with sixteen cards until 2026-09-12; they are
   now a hub of seven tiles and a screen per category. What is here is the tile,
   the chip and the save bar -- the grid, the cards and every control inside
   them are Bootstrap's. */

.pg-hub-search { max-width: 34rem; }
.pg-hub-search .form-control,
.pg-hub-search .input-group-text { padding-top: .55rem; padding-bottom: .55rem; }

/* A tile is a card you press. The whole head is the link, so the icon, the
   name and the count are one target rather than three. */
.pg-tile-head {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-bottom: .5rem;
  color: inherit;
  text-decoration: none;
}
.pg-tile-icon {
  display: flex;
  flex: 0 0 2.5rem;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--bs-border-radius);
  background: var(--pg-tint);
  color: var(--bs-primary);
  font-size: 1.1rem;
}
.pg-tile-title { display: flex; flex-direction: column; min-width: 0; }
.pg-tile-name { font-size: 1.05rem; font-weight: 600; line-height: 1.2; }
.pg-tile-count { color: var(--bs-secondary-color); font-size: .75rem; }
.pg-tile-go { margin-left: auto; color: var(--bs-secondary-color); font-size: .8rem; }
.pg-tile-head:hover .pg-tile-name { color: var(--bs-primary); }
.pg-tile-head:hover .pg-tile-go { color: var(--bs-primary); }

.pg-tile-desc {
  margin-bottom: .75rem;
  color: var(--bs-secondary-color);
  font-size: .8125rem;
}

/* The status line closes the card. Tiles are sized by their own content, so
   there is nothing to push it against. */
.pg-tile-status {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding-top: .75rem;
  border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
  font-size: .75rem;
}
.pg-tile-status .bi-circle-fill { font-size: .5rem; }

/* Chips: a section of a category, on the tile and again in the category
   toolbar. Small enough that a list of six reads as one control. */
.pg-chips { display: flex; flex-wrap: wrap; gap: .35rem; }
.pg-chip {
  padding: .2rem .55rem;
  border: var(--bs-border-width) solid var(--bs-border-color-translucent);
  border-radius: 2rem;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  font-size: .75rem;
  line-height: 1.4;
  text-decoration: none;
  white-space: nowrap;
}
.pg-chip:hover,
.pg-chip:focus-visible {
  border-color: var(--bs-primary);
  background: var(--pg-tint);
  color: var(--bs-primary);
}
.pg-chip.is-active {
  border-color: var(--bs-primary);
  background: var(--pg-tint-strong);
  color: var(--bs-primary);
  font-weight: 600;
}

/* The chip strip scrolls inside the toolbar rather than wrapping it onto a
   second line: the bar keeps one height and the search stays where it was. */
.pg-toolbar .pg-chips {
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: thin;
  padding-bottom: .15rem;
}

/* ── The cards of a category screen ──────────────────────────────────────
   One card per row, the full width of the screen.
 *
   Two other layouts were tried and both failed for the same reason: these
   cards are dense forms whose fields are laid out on Bootstrap's grid. Put
   two side by side and the row is as tall as the taller one, with the short
   card sitting in a pool of empty space. Put them in narrow masonry columns
   and every field inside stacks, so the cards run on down the page -- which
   is the one thing a settings screen must not do.
 *
   Full width lets each card use the grid it was written with: three fields
   across instead of three fields down. The screen is then as short as its
   content allows, which is what makes it readable. */
.pg-set-card + .pg-set-card { margin-top: 1.5rem; }

/* Bootstrap's columns answer to the window, not to the box they are in. That
   is right at full width and wrong on a phone, where a card is the whole of a
   narrow screen: a field marked col-xxl-4 would still take a third of it.
   The card is made a container and its fields stack while it is narrow. */
.pg-set-card { container-type: inline-size; }

@container (max-width: 34rem) {
  .pg-set-card .row > [class*="col-"] {
    flex: 0 0 100%;
    max-width: 100%;
  }
}

/* ── how wide a field is ──────────────────────────────────────────────────
   Two questions, and they are answered separately.

   How many to a ROW: one, or at most two. Three abreast reads as squeezed
   however wide the card is -- the labels start wrapping, the values line up
   with nothing, and there is no column for the eye to follow. Six abreast is
   what the Merchant Center block used to do. So a cell is either the whole
   row or half of it, and that is the entire grid.

   How wide the CONTROL is: the class says what the field HOLDS and the
   control takes only the room that needs. A three-character limit stretched
   across half a card reads as an empty box, and the operator starts wondering
   what else is supposed to go in it.

     pg-f-xs   a number or code of a few characters, a time, a percentage
     pg-f-sm   a short value: a postcode, a phone number, an id, a quantity
     pg-f-md   a name, a city, an amount, a picklist
     pg-f-lg   an e-mail address, a hostname, a key -- a row of its own
     (no class) a URL, a sentence, a textarea, a code block, a tag field

   Keyed to the width of the CARD and not of the window, which is the whole
   point of the container query: the same card is drawn about 300px wide on a
   phone, 900 inside the settings dialog's pane and 1400 on the settings page,
   so a viewport breakpoint cannot say "half of this card". A field always has
   the whole row while the card is narrow; nothing here needs a phone rule. */
.pg-set-card .row > .pg-f-xs,
.pg-set-card .row > .pg-f-sm,
.pg-set-card .row > .pg-f-md,
.pg-set-card .row > .pg-f-lg {
  flex: 0 0 100%;
  max-width: 100%;
}

@container (min-width: 34rem) {
  .pg-set-card .row > .pg-f-xs,
  .pg-set-card .row > .pg-f-sm,
  .pg-set-card .row > .pg-f-md {
    flex-basis: 50%;
    max-width: 50%;
  }
}

/* ── one setting to a line ────────────────────────────────────────────────
   A settings card reads as a list of decisions, so it is drawn as one: the
   name of the setting and its explanation on the left, the control that sets
   it on the right, and a rule between one decision and the next. Two fields
   abreast made the eye jump; a control floating under its own label made the
   card a wall.

   Applied from here rather than by rewriting the cards, and opted into by
   SHAPE: a cell that holds a label and one ordinary control, or a cell that
   holds one switch. Everything else -- a textarea, a code field, a tag field,
   a table, a group of buttons, a nested row -- is left exactly as it was,
   because those want the whole width and always did.

   :has() is the test, so a browser without it keeps the old stacked layout
   rather than half of this one. That is the right way to be missing a feature
   in a stylesheet: the screen still works, it is just plainer. */
@supports selector(:has(*)) {

  .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group),
  .pg-set-card .row > [class]:has(> .form-check > input[type="checkbox"]) {
    /* The rule and the text start at the same place, which is the card's own
       content edge. A .row carries a negative horizontal margin and gives each
       cell the matching padding back, so a border drawn on the cell runs half a
       gutter WIDER than the words inside it -- the line reaches past the text at
       both ends and the card looks out of true. The padding is handed to the
       margin instead: same position for the text, and the line now begins and
       ends exactly where the text does. */
    flex: 0 0 auto;
    width: calc(100% - var(--bs-gutter-x));
    max-width: calc(100% - var(--bs-gutter-x));
    margin-top: 0;
    margin-inline: calc(var(--bs-gutter-x) * .5);
    padding-inline: 0;
    padding-block: 1rem .85rem;
  }

  /* One rhythm, not two. Bootstrap spaces the lines of a .row with a gutter:
     a top margin on every cell, cancelled on the first line by an equal
     negative margin on the row itself. A converted cell spaces itself with its
     own padding and gives that margin up, which leaves the row's negative half
     uncompensated -- measured, the card's 1rem of body padding vanished and
     the first setting sat flush against the header in every one of the eight
     panes. Mixing the two also gave one card two different gaps depending on
     which kind of cell happened to come next.

     So the gutter is handed over whole: the row keeps none of it and every
     cell, converted or not, is spaced by the same padding underneath. The row
     then takes that last cell's padding back off the bottom, so what shows at
     both edges of the card is the card's own padding, which is what it was
     always for. (A hidden cell renders no padding, so the row ends at the last
     VISIBLE cell either way.)

     Written with the gutter variable rather than a :has() test for the rows
     that need it: :has() may not be nested inside :has(), and "a row holding a
     converted cell" needs exactly that -- the selector parses as invalid and
     the rule is dropped without a word. */
  .pg-set-card .row {
    --bs-gutter-y: 0;
  }
  .pg-set-card .row > * {
    padding-bottom: .85rem;
  }
  .pg-set-card .card-body > .row {
    margin-bottom: -.85rem;
  }

  /* The rule belongs BETWEEN two decisions, so it is drawn on the second one
     and there is no such thing as a trailing line. Drawn on the first would
     need "the last one visible" and a stylesheet cannot ask that: a switch that
     opens a block keeps that block in the page while it is shut, so the last
     row on screen is hardly ever the last child. A card with one setting in it
     -- Sign in with Google, when it is off -- then ended with a rule under the
     only thing in it and a gap under that. */
  .pg-set-card .row > * + [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group),
  .pg-set-card .row > * + [class]:has(> .form-check > input[type="checkbox"]) {
    padding-top: .85rem;
    border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
  }

  /* The two columns. The control column is given a width rather than left to
     its content: a .form-control is width:100%, and in a content-sized column
     that is a question with no answer -- the field would end up as wide as
     whatever happened to be typed in it. The pg-f-* caps narrow it from there,
     so a two-digit limit still gets a two-digit box.

     The name and the control share the FIRST row and the explanation goes
     under both. The control used to span both rows so that it would centre
     against the name and its explanation together, and that is what put every
     row out of true: a spanning item hands its height to the tracks it spans,
     so an empty second row still took a share of the control's height and
     lifted the name out of the middle. Measured across the eight panes: every
     plain field 7px out, every input group 19px, the icon picker 28px -- and
     the amount changed with the control, which is what made the card look
     crooked rather than merely offset. Sharing one row makes that row as tall
     as whichever of the two is taller and centres both in it, at any height. */
  .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group),
  .pg-set-card .row > [class]:has(> .form-check > input[type="checkbox"]) > .form-check {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(7rem, 19rem);
    grid-template-areas:
      "pgname pgctrl"
      "pgnote pgnote";
    align-items: center;
    column-gap: 1.5rem;
    padding-left: 0;
    min-height: 0;
  }

  /* Anything the two columns were not expecting takes a line of its own under
     them rather than being wedged into a cell it does not belong in. Stated
     before the placements below so that the placements win. */
  .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group) > *,
  .pg-set-card .row > [class]:has(> .form-check > input[type="checkbox"]) > .form-check > * {
    grid-column: 1 / -1;
  }

  .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group) > .form-label,
  .pg-set-card .row > [class]:has(> .form-check > input[type="checkbox"]) > .form-check > .form-check-label {
    grid-area: pgname;
    margin-bottom: 0;
    font-weight: 500;
  }

  .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group) > .form-text,
  .pg-set-card .row > [class]:has(> .form-check > input[type="checkbox"]) > .form-check > .form-text {
    grid-area: pgnote;
    margin-top: .1rem;
  }

  .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group) > input.form-control,
  .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group) > .form-select,
  .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group) > .input-group {
    grid-area: pgctrl;
    justify-self: end;
    width: 100%;
  }

  .pg-set-card .row > [class]:has(> .form-check > input[type="checkbox"]) > .form-check > .form-check-input {
    grid-area: pgctrl;
    justify-self: end;
    float: none;
    margin-left: 0;
    margin-top: 0;
  }

  /* A phone has no room for two columns: the control goes back under its own
     name, and the rule between settings is what keeps them apart. */
  @container (max-width: 34rem) {

    .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group),
    .pg-set-card .row > [class]:has(> .form-check > input[type="checkbox"]) > .form-check {
      grid-template-columns: minmax(0, 1fr);
      grid-template-areas:
        "pgname"
        "pgnote"
        "pgctrl";
      align-items: start;
      row-gap: .4rem;
    }

    .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group) > input.form-control,
    .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group) > .form-select,
    .pg-set-card .row > [class]:has(> .form-label):has(> input.form-control:not(.tagin), > .form-select, > .input-group) > .input-group,
    .pg-set-card .row > [class]:has(> .form-check > input[type="checkbox"]) > .form-check > .form-check-input {
      justify-self: start;
    }
  }
}

/* ── the settings dialog on glass ─────────────────────────────────────────
   The panel already has one frosted surface and the operator is the one who
   asked for it: Appearance -> Transparent Acrylic Effect puts .advanced-visuals
   on the body, and every dropdown and popover marked .backdrop is then drawn
   through it. The settings dialog is the same kind of object -- laid over the
   screen you are working on, closed to get back to it -- so it is drawn the
   same way and with the same filter values rather than a second recipe of its
   own. One look, one place to change it.

   Only the section list is frosted, not the whole dialog. A backdrop filter is
   run again on every frame in which anything inside or under the frosted box
   changes, and the pane is a scroller: with the filter on the dialog, every
   frame of scrolling a category re-blurred all 1180x860 pixels of it, and so
   did every frame of any animation on the screen behind -- the dashboard has
   several that never stop. Measured in headless Chromium over the dashboard
   in dark, that held the dialog to about ten frames a second, scrolling or
   idle; with the filter on the list alone it stays near sixty. The radius was
   not the cost (24px and 100px measured the same), the area was, so the
   values are unchanged.

   The pane is drawn on the panel's own background, solid. Without a blur any
   translucency there is a second layer of text under the settings -- at 90%
   the dashboard's figures and headings could still be read through the form.

   The scrim behind it is lightened to match, and that is not decoration: a
   frosted sheet over an opaque black one is not frosted. What a dropdown has
   behind it is the screen, and having the screen back there is the whole point.

   The cards give up their fill. In this theme a card is already within one
   step of the pane it sits on -- measured rgb(16,17,19) on rgb(16,17,20) --
   so what draws a card is its border, not its colour, and leaving the fill in
   would have laid an opaque sheet over the glass for no gain. */
.advanced-visuals:not(.toolbar) .pg-settings-modal .modal-content.pg-sm {
  background-color: transparent;
}
.advanced-visuals:not(.toolbar) .pg-settings-modal .pg-sm-main {
  background-color: var(--bs-body-bg);
}

/* The dialogs a category opens over the settings are small and hold still,
   so they keep the whole sheet frosted. */
.advanced-visuals:not(.toolbar) .pg-settings-modal .pg-sm-side,
.advanced-visuals:not(.toolbar) #pg_settings_modal_extras .modal-content {
  -webkit-backdrop-filter: brightness(1) saturate(1.5) blur(20px); backdrop-filter: brightness(1) saturate(1.5) blur(20px);
}
.advanced-visuals:not(.toolbar) #pg_settings_modal_extras .modal-content {
  background-color: rgba(var(--bs-body-bg-rgb), 0.62);
}

[data-bs-theme="dark"] .advanced-visuals:not(.toolbar) .pg-settings-modal .pg-sm-side,
[data-bs-theme="dark"] .advanced-visuals:not(.toolbar) #pg_settings_modal_extras .modal-content {
  -webkit-backdrop-filter: brightness(0.7) saturate(1.7) blur(100px); backdrop-filter: brightness(0.7) saturate(1.7) blur(100px);
}

/* The surfaces inside keep their relationship to one another -- the list
   darker than the pane, the save bar darker again -- but they hold it in
   translucency now, or each would be an opaque patch on the glass. */
.advanced-visuals:not(.toolbar) .pg-settings-modal .pg-sm-side {
  background-color: color-mix(in srgb, var(--bs-secondary-bg) 55%, transparent);
}
.advanced-visuals:not(.toolbar) .pg-settings-modal .pg-sm-foot {
  background-color: color-mix(in srgb, var(--bs-tertiary-bg) 55%, transparent);
}
.advanced-visuals:not(.toolbar) .pg-settings-modal .pg-sm-link.is-active {
  background-color: rgba(var(--bs-body-bg-rgb), 0.55);
}
.advanced-visuals:not(.toolbar) .pg-settings-modal .pg-set-card > .card {
  background-color: transparent;
}

/* Only while the settings are open, and only then: the class is put on the
   body by the dialog itself. Every other modal in the panel still gets the
   scrim it was designed against. */
.advanced-visuals.pg-settings-open .modal-backdrop {
  --bs-backdrop-opacity: 0.25;
}

/* A field held shut against autofill is not a disabled field and must not
   look like one: Bootstrap greys [readonly], and every field in the settings
   would come up grey until it was clicked. The mark is this software's own, so
   only the fields it shut are handed their colour back. */
.pg-set-card .form-control[data-pgro],
.pg-set-card textarea[data-pgro] {
  background-color: var(--bs-body-bg);
  opacity: 1;
}

/* The cell owns the row, the control owns the cell. Direct children only:
   inside an .input-group the control belongs to the group, and capping it
   there would pull the group apart -- so the group is capped instead. A cell
   whose control is wrapped in something else keeps the whole cell, which is
   the safe way to be wrong. */
.pg-set-card .pg-f-xs > .form-control,
.pg-set-card .pg-f-xs > .form-select,
.pg-set-card .pg-f-xs > .input-group { max-width: 11rem; }

.pg-set-card .pg-f-sm > .form-control,
.pg-set-card .pg-f-sm > .form-select,
.pg-set-card .pg-f-sm > .input-group { max-width: 19rem; }

.pg-set-card .pg-f-md > .form-control,
.pg-set-card .pg-f-md > .form-select,
.pg-set-card .pg-f-md > .input-group { max-width: 28rem; }

.pg-set-card .pg-f-lg > .form-control,
.pg-set-card .pg-f-lg > .form-select,
.pg-set-card .pg-f-lg > .input-group { max-width: 34rem; }

/* ── The hub ──────────────────────────────────────────────────────────────
   Tiles of a fixed width, as many per row as fit, full width on a phone. The
   grid is the same idea as the dashboard's widget field: nothing is sized as a
   fraction of the window, so a wide screen gets more tiles rather than wider
   ones. */
.pg-hub-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
  align-items: start;
}

/* What each tile knows about its category, under the description: two or three
   label/value pairs read straight off the config row. */
.pg-tile-facts {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .15rem .5rem;
  margin: 0 0 .75rem;
  font-size: .75rem;
}
.pg-tile-facts dt {
  color: var(--bs-secondary-color);
  font-weight: 400;
  white-space: nowrap;
}
.pg-tile-facts dd {
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}

/* The "/" hint in the hub search. A key cap, not a button: it says what the
   keyboard can do, it is not a thing to press with the pointer. */
.pg-hub-key {
  padding: .05rem .3rem;
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: .25rem;
  background: var(--pg-tint);
  color: var(--bs-secondary-color);
  font-size: .7rem;
  line-height: 1.2;
}
.pg-hub-count { white-space: nowrap; }

/* ══ The settings modal ═══════════════════════════════════════════════════
   Site Settings is not a place you go to any more: the button is in the
   header and the settings open over whatever screen the operator is on.

   The shape is a desktop settings window and it is the same shape at every
   width: the option list on the left, one pane beside it, and only the pane
   scrolls, so the list and the save bar never leave the screen. Three sizes:

     >= 992px   the dialog floats, the screen behind stays visible at the
                edges -- that is what says "this is on top of your work, and
                closing it puts you back" rather than "you have navigated".
     576-992px  the dialog takes the whole screen, the list narrows.
     < 576px    a phone has no room for both at once, so the list becomes a
                drawer over the pane. Still a list on the left, opened from
                the button in the dialog's header.

   Flexbox note that the whole thing rests on: a flex item does not shrink
   below its content on the MAIN axis unless it is told min-height/min-width 0.
   Every item in a column chain here therefore carries min-height: 0. Without
   it .pg-sm-main grows to the height of all its cards, the pane never becomes
   a scroller, and the save bar ends up a thousand pixels below the screen. */
.pg-settings-modal .modal-dialog {
  max-width: min(1180px, calc(100vw - 3rem));
}
.pg-settings-modal .modal-content.pg-sm {
  position: relative;
  flex-direction: row;
  overflow: hidden;
  height: min(860px, calc(100vh - 3.5rem));
  border: var(--bs-border-width) solid var(--bs-border-color-translucent);
  border-radius: var(--bs-border-radius-xl);
}

/* Viewport height on a phone is not a constant: the browser toolbar hides on
   scroll and 100vh keeps the taller figure, so a dialog sized in vh hangs
   below the screen with its save bar out of reach. dvh is the live figure.
   Declared after the vh rule, so a browser that does not know dvh keeps it. */
@supports (height: 100dvh) {
  .pg-settings-modal .modal-content.pg-sm {
    height: min(860px, calc(100dvh - 3.5rem));
  }
}

/* ── the sidebar ───────────────────────────────────────────────────────── */
.pg-sm-side {
  display: flex;
  flex: 0 0 15rem;
  flex-direction: column;
  width: 15rem;
  min-height: 0;
  border-right: var(--bs-border-width) solid var(--bs-border-color-translucent);
  background: var(--bs-secondary-bg);
}
.pg-sm-brand {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: 1rem 1rem .5rem;
  color: var(--bs-secondary-color);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.pg-sm-nav {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: .25rem .5rem 1rem;
  scroll-padding-block: .5rem;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.pg-sm-group {
  padding: .85rem .5rem .25rem;
  color: var(--bs-secondary-color);
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
}
/* Every row looks the same whether it opens a pane or leaves for a page: the
   arrow on the right is what tells them apart. Colour is not used for that --
   in this panel colour already means something else.

   min-height is the touch target, not the text: 2.25rem here and 2.75rem in
   the drawer, where a finger is the only pointer. */
.pg-sm-link {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  min-height: 2.25rem;
  padding: .4rem .5rem;
  border: 0;
  border-radius: var(--bs-border-radius);
  background: none;
  color: var(--bs-body-color);
  font-size: .8125rem;
  line-height: 1.3;
  text-align: left;
  text-decoration: none;
}
.pg-sm-link i:first-child {
  flex: 0 0 1rem;
  font-size: .875rem;
  text-align: center;
  opacity: .7;
}
.pg-sm-link span {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pg-sm-link:hover,
.pg-sm-link:focus-visible {
  background: var(--pg-tint-strong);
  color: var(--bs-body-color);
}
.pg-sm-link.is-active {
  background: var(--bs-body-bg);
  box-shadow: inset 2px 0 0 var(--bs-primary);
  font-weight: 600;
}
.pg-sm-link.is-active i:first-child {
  color: var(--bs-primary);
  opacity: 1;
}
.pg-sm-out {
  margin-left: auto;
  color: var(--bs-secondary-color);
  font-size: .65rem;
  opacity: .7;
}

/* ── the pane ──────────────────────────────────────────────────────────── */
.pg-sm-main {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}
.pg-sm-head {
  display: flex;
  flex: 0 0 auto;
  align-items: flex-start;
  gap: .75rem;
  padding: 1rem 1.25rem .75rem;
}
.pg-sm-heading {
  flex: 1 1 auto;
  min-width: 0;
}
.pg-sm-title {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1.2;
}
.pg-sm-desc {
  margin: .15rem 0 0;
  color: var(--bs-secondary-color);
  font-size: .8125rem;
}

/* The button that opens the drawer. Only a phone has it; on anything wider
   the list is already on screen and a button that opens what is open says
   something untrue. */
.pg-sm-burger {
  display: none;
  flex: 0 0 auto;
  padding: .25rem .45rem;
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: none;
  color: var(--bs-body-color);
  line-height: 1;
}
.pg-sm-burger .bi { font-size: 1.1rem; }

/* Help for the category on view. An icon beside the close button, the same
   height as the title line it sits on, and quiet until the pointer reaches it,
   like the icon-only controls of the header. */
.pg-sm-help {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  margin-top: -.3rem;
  border-radius: 50%;
  color: var(--bs-secondary-color);
  text-decoration: none;
}
.pg-sm-help:hover,
.pg-sm-help:focus-visible {
  background: var(--pg-tint-hover);
  color: var(--bs-body-color);
}
.pg-sm-help .bi { font-size: 1.1rem; line-height: 1; }

.pg-sm-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 1rem 1.25rem;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.pg-sm-notice:empty { display: none; }

/* The pane takes focus after it is replaced, so that a keyboard or a
   screen reader starts at the new content instead of at the body. That is
   programmatic focus and wants no ring. */
#pg_settings_modal_pane:focus { outline: 0; }

.pg-sm-foot {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: .75rem;
  padding: .65rem 1.25rem;
  padding-bottom: max(.65rem, env(safe-area-inset-bottom));
  border-top: var(--bs-border-width) solid var(--bs-border-color-translucent);
  background: var(--bs-tertiary-bg);
}
.pg-sm-meta {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* While a pane is on its way the pane says so rather than going blank, and
   the sidebar stays usable. The bar under the header is the only motion: a
   dimmed block with no sign of life reads as a screen that has stopped. */
.pg-sm-body.is-loading {
  opacity: .55;
  pointer-events: none;
}
.pg-sm-progress {
  position: relative;
  flex: 0 0 auto;
  height: 2px;
  overflow: hidden;
  background: var(--pg-tint-strong);
  opacity: 0;
  transition: opacity .15s linear;
}
.pg-sm.is-busy .pg-sm-progress { opacity: 1; }
.pg-sm-progress::after {
  position: absolute;
  inset-block: 0;
  left: 0;
  width: 40%;
  background: var(--bs-primary);
  content: "";
  animation: pg-sm-slide 1.1s ease-in-out infinite;
}
@keyframes pg-sm-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}

/* ── the whole screen, below the sidebar breakpoint ────────────────────── */
@media (max-width: 991.98px) {
  /* modal-fullscreen-lg-down asks for 100vw and the rule above caps it at
     calc(100vw - 3rem); the cap is what wins, and the screen behind shows
     along the edge of a dialog that is meant to be the whole screen. */
  .pg-settings-modal .modal-dialog {
    max-width: none;
  }
  .pg-settings-modal .modal-content.pg-sm {
    height: 100%;
    border: 0;
    border-radius: 0;
  }
  .pg-sm-side {
    flex-basis: 12rem;
    width: 12rem;
  }
  .pg-sm-brand { padding-top: .75rem; }
  .pg-sm-head { padding-top: .75rem; }
}

/* ── the drawer, on a phone ────────────────────────────────────────────── */
@media (max-width: 575.98px) {
  .pg-sm-side {
    position: absolute;
    z-index: 3;
    inset-block: 0;
    left: 0;
    flex-basis: auto;
    width: min(17rem, 85%);
    border-right: var(--bs-border-width) solid var(--bs-border-color);
    box-shadow: var(--bs-box-shadow-lg);
    transform: translateX(-100%);
    transition: transform .18s ease-out, visibility 0s linear .18s;
    visibility: hidden;
  }
  .pg-sm.is-nav-open .pg-sm-side {
    transform: none;
    transition: transform .18s ease-out, visibility 0s linear 0s;
    visibility: visible;
  }
  /* Tapping beside the drawer closes it. Not display:none when shut: a
     pointer-events:none scrim can stay in the flow and needs no second rule. */
  .pg-sm-scrim {
    position: absolute;
    z-index: 2;
    inset: 0;
    background: rgba(0, 0, 0, .4);
    opacity: 0;
    transition: opacity .18s linear;
    pointer-events: none;
  }
  .pg-sm.is-nav-open .pg-sm-scrim {
    opacity: 1;
    pointer-events: auto;
  }
  .pg-sm-burger { display: block; }
  .pg-sm-link { min-height: 2.75rem; }
  .pg-sm-head,
  .pg-sm-body { padding-inline: .875rem; }
  .pg-sm-foot { padding-inline: .875rem; }
  .pg-sm-title { font-size: 1.05rem; }
  /* Two lines of description on a phone is a third of the pane. */
  .pg-sm-desc { display: none; }
  /* The footer keeps only what has to be pressed. */
  .pg-sm-meta { display: none; }
  .pg-sm-head .btn-close {
    padding: .5rem;
    margin: -.25rem -.25rem 0 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .pg-sm-side,
  .pg-sm-scrim,
  .pg-sm-progress { transition: none; }
  .pg-sm-progress::after { animation: none; }
}

/* ── Dashboard sales map (widget 1) ─────────────────────────────────────────
   The left panel of a two-track card: a period head and the outlines, with the
   period and map switches in a menu in the corner of them. The right panel is a
   plain .pg-list and needs nothing of its own. */
/* No overflow of its own, deliberately. .pg-split already gives this half
   `overflow-y: auto` once the card is wide enough to lay its halves out as a
   row, which is the only place a scrollbar belongs. Setting one here as well
   changed nothing there and broke the stacked card: any overflow other than
   visible cancels a flex item's automatic minimum size, so the panel was free
   to be squeezed to nothing -- measured at 0px, with the region list drawn
   straight over the map. */
.pg-smap-main {
  display: flex;
  flex-direction: column;
}
.pg-smap-main > .pg-head { flex: 0 0 auto; }

/* Three facts on one line in a panel half a card wide: let the line break
   between them rather than inside them, or "Tüm zamanlar" ends up split over
   two lines with "Tüm" alone on the first. */
.pg-smap-main .pg-head-line { flex-wrap: wrap; }
.pg-smap-main .pg-head-unit,
.pg-smap-main .pg-head-total { white-space: nowrap; }

.pg-smap-canvas {
  position: relative;
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  /* A floor, not a height. On a wide card the panel is a row and this box takes
     the space the head and the switch leave; stacked on a narrow one the panel
     is a column whose height comes from its contents, and an SVG that sizes
     itself from its box collapses the box to nothing. Measured at 8px before
     this line existed. */
  min-height: 7.5rem;
  padding: 4px 10px;
}
/* Both dimensions given, so the outlines fill whatever box they are handed and
   the viewBox does the fitting. Sizing from the width instead lets the drawing
   overflow a short row. */
.pg-smap-canvas svg {
  width: 100%;
  height: 100%;
}

/* Every province starts as the card's own quiet surface, so the ones that sold
   something are the only thing with colour in the panel. */
.pg-smap-canvas path {
  fill: var(--bs-secondary-bg);
  stroke: var(--bs-body-bg);
  stroke-width: 1;
  /* The outlines are drawn at whatever size the card gives them, and a hairline
     that scales with the viewBox turns into a smear on a wide card. */
  vector-effect: non-scaling-stroke;
}

/* Four steps of the card's own accent, by quantile rather than by value - see
   pg_sales_map_steps(). Mixed towards transparent rather than written out as
   four hex values, so the ramp follows the card whatever colour it is given and
   holds in both themes. */
.pg-smap-canvas path.pg-smap-s1 { fill: color-mix(in srgb, var(--pg-accent, var(--bs-primary)) 28%, transparent); }
.pg-smap-canvas path.pg-smap-s2 { fill: color-mix(in srgb, var(--pg-accent, var(--bs-primary)) 48%, transparent); }
.pg-smap-canvas path.pg-smap-s3 { fill: color-mix(in srgb, var(--pg-accent, var(--bs-primary)) 72%, transparent); }
.pg-smap-canvas path.pg-smap-s4 { fill: var(--pg-accent, var(--bs-primary)); }

/* Only a place that sold something is a link, so only that one takes the
   pointer. The rest still answer the tooltip, which is how "nothing here"
   gets said.

   .is-static is the card drawn for somebody who may read the report but not
   open the orders screen: the shapes still carry their figures, they just do
   not pretend to lead anywhere. */
.pg-smap-canvas:not(.is-static) path:not(.pg-smap-s0) { cursor: pointer; }
.pg-smap-canvas path:not(.pg-smap-s0):hover {
  stroke: var(--bs-body-color);
  stroke-width: 1.5;
}

.pg-smap-tip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
  display: none;
  max-width: 14rem;
  padding: .4rem .55rem;
  border: var(--bs-border-width) solid var(--pg-rule-strong);
  border-radius: var(--bs-border-radius);
  background: var(--bs-body-bg);
  box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
  font-size: 11px;
  line-height: 1.35;
  /* It follows the pointer, so it must never be under it. */
  pointer-events: none;
}
.pg-smap-tip.is-on { display: block; }
.pg-smap-tip b {
  display: block;
  font-size: 12px;
}
.pg-smap-tip span { display: block; }

/* Tighter than .btn-sm, and never wrapping. Three period labels across a panel
   half the width of the card broke each one over two lines, which pushed the
   panel past the card body and left the half with a scrollbar of its own. */
/* The switches, as one icon in the corner of the map.
   
   They were a strip of buttons under it, and that strip cost the map a band of
   its own height in a panel that has none to spare. Worse, a .btn-group
   stretched across the panel gives every button an equal share of it whatever
   its label says, so three period labels wrapped to two lines apiece and pushed
   the panel past the card body. The head beside the revenue names the period on
   show, so nothing is lost by folding them away. */
.pg-smap-menu {
  position: absolute;
  top: 2px;
  right: 4px;
  z-index: 4;
}
.pg-smap-menu-btn {
  padding: .1rem .3rem;
  line-height: 1;
  /* Over the outlines, so it carries enough of the card under it to stay
     legible wherever the map happens to be dark. */
  background: rgba(var(--bs-body-bg-rgb), .72);
}
.pg-smap-menu .dropdown-menu {
  --bs-dropdown-font-size: 12px;
  --bs-dropdown-item-padding-y: .25rem;
  --bs-dropdown-min-width: 9rem;
}
.pg-smap-menu .dropdown-item {
  display: flex;
  align-items: center;
  gap: .4rem;
}
/* The tick is the only thing that marks the current choice: a menu row painted
   in the accent reads as a state, and these rows are a choice. */
.pg-smap-menu .dropdown-item.active {
  background: transparent;
  color: inherit;
  font-weight: 600;
}
.pg-smap-menu .dropdown-item:not(.active) .pg-smap-tick { visibility: hidden; }

/* One layer per view, both in the same box: the one not being shown carries
   .d-none rather than being thrown away, so switching back does not go to the
   network again. */
.pg-smap-layer {
  display: flex;
  width: 100%;
  height: 100%;
  min-height: 0;
}

/* ── Counter sale (add_order.php) ─────────────────────────────────────────
   One screen, two views. #pg_sale[data-mode] is "kasa" (till: prices with
   VAT, the change, large controls) or "belge" (document: VAT breakdown, the
   account and the due date). The server prints both figures in every row
   and the view hides the other set, so switching needs no request. */
.pg-sale[data-mode="kasa"] .pg-belge,
.pg-sale[data-mode="kasa"] .pg-belge-inline,
.pg-sale[data-mode="kasa"] .pg-belge-block,
.pg-sale[data-mode="belge"] .pg-kasa,
.pg-sale[data-mode="belge"] .pg-kasa-inline {
  display: none !important;
}
.pg-sale .card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: .75rem;
}
.pg-sale-ch {
  margin: 0;
  font-size: .95rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.pg-sale kbd,
.pg-sale-keys kbd {
  font-size: .7em;
  padding: .1em .4em;
  border-radius: 4px;
  border: 1px solid var(--bs-border-color-translucent);
  background: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
  white-space: nowrap;
  box-shadow: none;
}
/* A switched-off ghost button stays borderless; Bootstrap's disabled state
   would draw the outline the ghost recipe exists to leave out. */
.pg-sale .btn-ghost:disabled { border-color: transparent; }
/* The view switch (till / document): one frame round both, the chosen
   view filled - a segmented control, as in the approved preview, rather
   than the joined .btn-group of the toolbar recipe. */
.pg-sale-switch {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--bs-border-radius-lg);
}
.pg-sale-switch .btn {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .2rem .6rem;
  border: 0;
  border-radius: var(--bs-border-radius);
}
.pg-sale-switch .btn.active,
.pg-sale-switch .btn[aria-pressed="true"] {
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
}
@media (max-width: 575.98px) {
  .pg-sale .pg-sale-lbl { display: none; }
}

/* Entry card: the product box and the customer box, side by side once the
   card is wide enough - measured on the card, since the column is. */
.pg-sale-entry { container-type: inline-size; }
.pg-sale-entry .card-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
}
@container (min-width: 620px) {
  .pg-sale-entry .card-body { grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr); }
  .pg-sale[data-mode="belge"] .pg-sale-entry .card-body { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); }
}
.pg-sale-field { position: relative; min-width: 0; }

/* The product box: icon, text, quantity for the next product, Add. A
   .form-control wrapper so it wears the panel's own border and focus. */
.pg-sale-ig {
  display: flex;
  align-items: stretch;
  padding: 0;
  min-width: 0;
  overflow: hidden;
}
.pg-sale-ig:focus-within {
  border-color: var(--pg-primary);
  box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--bs-focus-ring-color);
}
.pg-sale-ig input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  color: inherit;
  padding: .45rem .25rem;
  outline: 0;
  box-shadow: none;
}
.pg-sale-ig-sm input { padding: .6rem .25rem; }
.pg-sale-ig-icon {
  display: grid;
  place-items: center;
  padding: 0 .7rem;
  color: var(--bs-secondary-color);
}
.pg-sale-ig > .btn { border-radius: 0; }
.pg-sale-mult {
  display: flex;
  align-items: center;
  gap: .15rem;
  margin: 0;
  padding: 0 .4rem;
  border-left: 1px solid var(--bs-border-color-translucent);
  color: var(--bs-secondary-color);
  font-size: .85em;
}
.pg-sale-mult input {
  width: 2.8em;
  text-align: center;
  padding: .3rem 0;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}
.pg-sale-mult input::-webkit-inner-spin-button,
.pg-sale-mult input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.pg-sale[data-mode="kasa"] #pg_sale_product { font-size: 1.15rem; padding: .7rem .25rem; }
.pg-sale[data-mode="kasa"] #pg_sale_product_group .pg-sale-ig-icon { font-size: 1.3rem; padding: 0 .85rem; }
.pg-sale[data-mode="kasa"] #pg_sale_product_group > .btn { padding-inline: 1.1rem; font-size: 1rem; }
.pg-sale-foot {
  display: flex;
  align-items: center;
  gap: .5rem;
  min-height: 1.2rem;
  margin-top: .4rem;
  font-size: .78rem;
  color: var(--bs-secondary-color);
}
.pg-sale-foot kbd { margin-left: auto; }
.pg-sale-foot.is-error {
  align-items: flex-start;
  color: var(--bs-danger-text-emphasis);
  font-weight: 500;
}
.pg-sale-foot.is-error .bi { margin-top: .15rem; }
.pg-sale-dot {
  flex: none;
  width: .55rem;
  height: .55rem;
  border-radius: 50%;
  background: var(--bs-success);
  box-shadow: 0 0 0 3px rgba(var(--bs-success-rgb), .2);
}
.pg-sale.is-busy .pg-sale-dot {
  background: var(--pg-primary);
  box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb), .2);
}
.pg-sale-dot.is-pulse { animation: pg-sale-pulse .6s ease-out; }
@keyframes pg-sale-pulse {
  0% { box-shadow: 0 0 0 0 var(--bs-success); }
  100% { box-shadow: 0 0 0 8px transparent; }
}

/* Search results under the product box (over its foot line) and under the
   customer box. */
.pg-sale-results {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% - 1.3rem);
  z-index: 1030;
  max-height: 330px;
  overflow: auto;
  padding: .25rem;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--bs-border-radius-lg);
  box-shadow: var(--bs-box-shadow);
}
#pg_sale_customer { position: relative; }
#pg_sale_cust_results { top: calc(100% + .25rem); }
.pg-sale-res {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: .1rem 1rem;
  align-items: center;
  padding: .5rem .6rem;
  border-radius: var(--bs-border-radius);
  cursor: pointer;
}
.pg-sale-res:hover,
.pg-sale-res.is-active { background: var(--bs-tertiary-bg); }
.pg-sale-res b {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pg-sale-res small { grid-column: 1; color: var(--bs-secondary-color); font-size: .78rem; }
.pg-sale-res-price {
  grid-row: 1 / 3;
  grid-column: 2;
  text-align: end;
  font-variant-numeric: tabular-nums;
}
.pg-sale-res-price small { display: block; }
.pg-sale-res[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }
.pg-sale-res-new b { color: var(--bs-link-color); }
.pg-sale-res-empty { padding: .7rem; color: var(--bs-secondary-color); font-size: .875rem; }

/* The customer box, closed. */
.pg-sale-cust {
  display: flex;
  align-items: center;
  gap: .6rem;
  min-height: 3.05rem;
  padding: .35rem .4rem .35rem .6rem;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--bs-border-radius-lg);
  background: var(--bs-body-bg);
}
.pg-sale[data-mode="kasa"] .pg-sale-cust { min-height: 3.55rem; }
.pg-sale-av {
  flex: none;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}
.pg-sale-av.is-picked { background: rgba(var(--bs-primary-rgb), .16); color: var(--bs-link-color); }
.pg-sale-who { flex: 1; min-width: 0; line-height: 1.25; }
.pg-sale-who b,
.pg-sale-who small {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pg-sale-who b { font-weight: 600; }
.pg-sale-who small { color: var(--bs-secondary-color); font-size: .76rem; }

/* The cart. */
.pg-sale-cart { container-type: inline-size; }
.pg-sale-table { --bs-table-bg: transparent; }
.card .pg-sale-table { margin: 0 !important; }
.pg-sale-table th {
  font-size: .76rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
  white-space: nowrap;
  vertical-align: bottom;
}
.pg-sale-table th small { display: block; font-weight: 400; opacity: .8; }
.pg-sale-table td { vertical-align: middle; }
.pg-sale-table td.text-end,
.pg-sale-table th.text-end { white-space: nowrap; font-variant-numeric: tabular-nums; }
.pg-sale[data-mode="kasa"] .pg-sale-table td { padding-top: .8rem; padding-bottom: .8rem; }
.pg-sale-c-no { width: 1%; color: var(--bs-secondary-color); font-variant-numeric: tabular-nums; }
.pg-sale-c-thumb,
.pg-sale-c-rm { width: 1%; }
.pg-sale-thumb {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--bs-border-radius);
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
  font-size: 1.1rem;
  object-fit: cover;
}
.pg-sale-pname { display: block; font-weight: 600; line-height: 1.3; }
.pg-sale[data-mode="kasa"] .pg-sale-pname { font-size: 1.05rem; }
.pg-sale-psub { display: block; margin-top: .1rem; font-size: .76rem; color: var(--bs-secondary-color); }
.pg-sale-unit-inline { display: none; font-size: .76rem; color: var(--bs-secondary-color); }
.pg-sale-step {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--bs-border-radius-lg);
  background: var(--bs-body-bg);
}
.pg-sale-step button {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: var(--bs-border-radius);
  background: transparent;
  color: var(--bs-secondary-color);
}
.pg-sale-step button:hover:not(:disabled) { background: var(--bs-tertiary-bg); color: var(--bs-body-color); }
.pg-sale-step button:disabled { opacity: .35; }
.pg-sale-step input {
  width: 2.8em;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  text-align: center;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  outline: 0;
}
.pg-sale-step input:focus-visible { box-shadow: inset 0 -2px 0 var(--pg-primary); }
.pg-sale[data-mode="kasa"] .pg-sale-step button { width: 38px; height: 38px; }
.pg-sale-linemsg {
  display: block;
  max-width: 15rem;
  margin-top: .25rem;
  font-size: .74rem;
  color: var(--bs-danger-text-emphasis);
  white-space: normal;
}
.pg-sale-rm {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: var(--bs-border-radius);
  background: transparent;
  color: var(--bs-secondary-color);
}
.pg-sale-rm:hover { background: rgba(var(--bs-danger-rgb), .14); color: var(--bs-danger-text-emphasis); }
.pg-sale-table tr.pg-sale-flash > td { animation: pg-sale-flash 1.4s ease-out; }
@keyframes pg-sale-flash {
  0% { background: rgba(var(--bs-primary-rgb), .18); }
  100% { background: transparent; }
}
.pg-sale-table tr.pg-sale-line-error > td { background: rgba(var(--bs-danger-rgb), .1); }
.pg-sale-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .2rem;
  padding: 2.2rem 1rem;
  text-align: center;
  color: var(--bs-secondary-color);
}
.pg-sale-empty .bi { font-size: 2.2rem; opacity: .6; }
.pg-sale-empty strong { color: var(--bs-body-color); font-size: 1rem; }

/* A narrow cart (phone, or a narrow column) turns each row into two lines:
   the name, then the quantity, the amount and the bin. */
@container (max-width: 575px) {
  .pg-sale-table thead { display: none; }
  .pg-sale-table tbody tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    align-items: center;
    column-gap: .6rem;
    padding: .7rem .75rem;
    border-bottom: 1px solid var(--bs-border-color-translucent);
  }
  .pg-sale-table tbody tr:last-child { border-bottom: 0; }
  .pg-sale-table tbody tr.pg-sale-empty-row { display: block; padding: 0; }
  .pg-sale-table tbody tr.pg-sale-line-error { background: rgba(var(--bs-danger-rgb), .1); }
  .pg-sale-table tbody td { padding: 0; border: 0; background: transparent; }
  .pg-sale-table td.pg-sale-c-thumb,
  .pg-sale-table td.pg-sale-c-no,
  .pg-sale-table td.pg-sale-c-unit,
  .pg-sale-table td.pg-sale-c-rate,
  .pg-sale-table td.pg-sale-c-tax { display: none !important; }
  .pg-sale-table td.pg-sale-c-name { grid-column: 1 / -1; margin-bottom: .5rem; }
  .pg-sale-unit-inline { display: block; }
}

/* The status column: summary, payment, account. The column is as tall as
   the row, so the stickiness is on this box. */
.pg-sale-status { display: flex; flex-direction: column; gap: 1rem; }
@media (min-width: 992px) {
  .pg-sale-status { position: sticky; top: 3.3rem; }
}
.pg-sale-total { padding: 1rem 1rem .85rem; }
.pg-sale-total-label {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}
.pg-sale-total-amount {
  margin: .15rem 0 .2rem;
  font-size: 2.35rem;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.pg-sale-total-sm { font-size: 1.8rem; }
.pg-sale-total-sub { font-size: .8rem; color: var(--bs-secondary-color); }
.pg-sale-rows {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .3rem 1rem;
  margin: 0;
  padding: .75rem 1rem;
  border-top: 1px solid var(--bs-border-color-translucent);
  font-size: .875rem;
}
.pg-sale-rows-flush { border-top: 0; }
.pg-sale-rows dt { font-weight: 400; color: var(--bs-secondary-color); }
.pg-sale-rows dd { margin: 0; text-align: end; font-variant-numeric: tabular-nums; }
.pg-sale-rows .pg-sale-grand {
  margin-top: .2rem;
  padding-top: .45rem;
  border-top: 1px solid var(--bs-border-color-translucent);
  color: var(--bs-body-color);
  font-weight: 700;
}
.pg-sale-rows dt.pg-sale-grand { align-self: end; }
.pg-sale-rows dd.pg-sale-grand { font-size: 1.35rem; }

.pg-sale-pay { display: flex; flex-direction: column; gap: .85rem; }
.pg-sale-methods {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: .4rem;
}
.pg-sale-method {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .2rem;
  padding: .5rem .2rem;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--bs-border-radius-lg);
  background: var(--bs-body-bg);
  color: var(--bs-secondary-color);
  font-size: .78rem;
  line-height: 1.2;
}
.pg-sale-method .bi { font-size: 1.15rem; }
.pg-sale-method:hover { background: var(--bs-tertiary-bg); color: var(--bs-body-color); }
.pg-sale-method[aria-pressed="true"] {
  border-color: var(--pg-primary);
  background: rgba(var(--bs-primary-rgb), .14);
  color: var(--bs-body-color);
  box-shadow: inset 0 0 0 1px var(--pg-primary);
}
.pg-sale[data-mode="kasa"] .pg-sale-method { padding: .8rem .2rem; font-size: .84rem; }
.pg-sale[data-mode="kasa"] .pg-sale-method .bi { font-size: 1.45rem; }
.pg-sale-link {
  align-self: flex-start;
  padding: 0;
  border: 0;
  background: none;
  color: var(--bs-link-color);
  font-size: .78rem;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.pg-sale-quick { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .4rem; }
.pg-sale-quick button {
  padding: .12rem .6rem;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--bs-border-radius-pill);
  background: transparent;
  color: inherit;
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
}
.pg-sale-quick button:hover { background: var(--bs-tertiary-bg); }
.pg-sale-change {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: .5rem;
  margin-top: .6rem;
  padding: .55rem .75rem;
  border-radius: var(--bs-border-radius-lg);
  background: rgba(var(--bs-success-rgb), .16);
}
.pg-sale-change span { font-size: .8rem; color: var(--bs-secondary-color); }
.pg-sale-change b { font-size: 1.5rem; color: var(--bs-success-text-emphasis); font-variant-numeric: tabular-nums; }
.pg-sale-change.is-short { background: rgba(var(--bs-warning-rgb), .18); }
.pg-sale-change.is-short b { font-size: 1.1rem; color: var(--bs-warning-text-emphasis); }
.pg-sale-inv,
.pg-sale-note { display: flex; gap: .55rem; align-items: flex-start; }
.pg-sale-inv { font-size: .875rem; }
.pg-sale-inv .bi { margin-top: .15rem; color: var(--bs-secondary-color); }
.pg-sale-inv small { display: block; font-size: .76rem; color: var(--bs-secondary-color); }
.pg-sale-note { margin-top: .5rem; font-size: .76rem; color: var(--bs-secondary-color); }
.pg-sale-pay > .pg-sale-note:first-child { margin-top: 0; }
.pg-sale-note .bi { margin-top: .1rem; }
#pg_sale_invoice:empty { display: none; }
.pg-sale-go {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  padding: .8rem 1rem;
  font-size: 1.05rem;
  font-weight: 600;
}
.pg-sale-go kbd { margin-left: auto; background: rgba(0, 0, 0, .1); border-color: rgba(0, 0, 0, .15); color: inherit; }
.pg-sale-go.is-working { cursor: progress; }

.pg-sale-balance { display: flex; align-items: baseline; flex-wrap: wrap; gap: .5rem; margin-bottom: .75rem; }
.pg-sale-balance b { font-size: 1.5rem; font-variant-numeric: tabular-nums; }
.pg-sale-facts {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .3rem .9rem;
  margin: 0;
  font-size: .84rem;
}
.pg-sale-facts dt { font-weight: 400; color: var(--bs-secondary-color); }
.pg-sale-facts dd { margin: 0; overflow-wrap: anywhere; }

/* The card that replaces the summary once the sale is done. */
.pg-sale-done-head {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: 1rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
.pg-sale-done-head > .bi { font-size: 2.1rem; color: var(--bs-success); }
.pg-sale-done-head b { display: block; font-size: 1.05rem; }
.pg-sale-done-head small { font-size: .8rem; color: var(--bs-secondary-color); }
.pg-sale-done-list {
  list-style: none;
  margin: 0;
  padding: .25rem 0;
  border-top: 1px solid var(--bs-border-color-translucent);
}
.pg-sale-done-list li { display: flex; align-items: center; gap: .6rem; padding: .5rem 1rem; font-size: .86rem; }
.pg-sale-done-list .bi { color: var(--bs-secondary-color); }
.pg-sale-done-actions { display: flex; flex-wrap: wrap; gap: .4rem; padding: .5rem 1rem 1rem; }
.pg-sale-next {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .6rem;
  padding: .75rem 1rem;
  border-top: 1px solid var(--bs-border-color-translucent);
  font-size: .8rem;
  color: var(--bs-secondary-color);
}
.pg-sale-next .btn { margin-left: auto; }
.pg-sale-next .btn kbd { background: transparent; border-color: rgba(0, 0, 0, .2); color: inherit; }

/* Below lg the column drops under the cart; the total and the button come
   back in a bar at the foot of the screen, clear of the chat launcher. */
.pg-sale-savebar {
  bottom: 0;
  z-index: 3;
  padding: .5rem 0 .25rem;
  background: linear-gradient(to top, var(--bs-body-bg) 70%, transparent);
}
.pg-sale-savebar-in {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .5rem .55rem .5rem .9rem;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--bs-border-radius-pill);
  background: var(--bs-body-bg);
  box-shadow: var(--bs-box-shadow);
}
body:has(#pg-chat-root) .pg-sale-savebar-in { margin-right: 4.75rem; }
.pg-sale-savebar-text { min-width: 0; line-height: 1.2; }
.pg-sale-savebar-text b { display: block; font-size: 1.15rem; font-variant-numeric: tabular-nums; }
.pg-sale-savebar-text small {
  display: block;
  font-size: .74rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pg-sale-savebar-in .btn { flex: none; margin-left: auto; }

.pg-sale-recent { --bs-offcanvas-width: min(430px, 100vw); }
.pg-sale-rs {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: .25rem .8rem;
  padding: .75rem 1rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
.pg-sale-rs-acts { grid-column: 1 / -1; display: flex; align-items: center; flex-wrap: wrap; gap: .4rem; }
.pg-sale-keys { display: grid; grid-template-columns: auto 1fr; gap: .5rem 1rem; margin: 0; }
.pg-sale-keys dt { font-weight: 400; text-align: end; }
.pg-sale-keys dd { margin: 0; color: var(--bs-secondary-color); }

@media (prefers-reduced-motion: reduce) {
  .pg-sale-table tr.pg-sale-flash > td,
  .pg-sale-dot.is-pulse { animation: none; }
}



/* ── Workspace ───────────────────────────────────────────────────────────
   Channels, tasks and the planning board (workspace*.php), and the
   Workspace drawer the order, contact and product screens open. Colours
   come from the Bootstrap variables so the dark theme follows. */
body .workspace-color {
  color: var(--workspace-color) !important;
}
html[data-bs-theme="dark"] body .workspace-color {
  color: var(--workspace-color-highlight) !important;
}

/* The workspace's own accent, on its screens and in its drawers elsewhere,
   and the scale every workspace rule measures with: four corner radii from
   a chip to a drawer, four spaces, four sizes of face, two shadows. The
   RGB numbers are --workspace-color written out, for rgba(); the two are
   kept together here so a change to one is a change to both. */
body.workspace,
.ws-app,
[id^="ws-"] {
  --ws-accent: var(--workspace-color);
  --ws-accent-rgb: 94, 53, 177;
  --ws-soft: rgba(var(--ws-accent-rgb), .08);
  --ws-radius-xs: .45rem;
  --ws-radius-sm: .65rem;
  --ws-radius: 1rem;
  --ws-radius-lg: 1.25rem;
  --ws-space-1: .25rem;
  --ws-space-2: .5rem;
  --ws-space-3: .75rem;
  --ws-space-4: 1rem;
  --ws-avatar-xs: 1rem;
  --ws-avatar-sm: 1.6rem;
  --ws-avatar: 2.2rem;
  --ws-avatar-lg: 3rem;
  --ws-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 10px 30px -14px rgba(0, 0, 0, .16);
  --ws-shadow-accent: 0 .75rem 1.5rem -.75rem rgba(var(--ws-accent-rgb), .45);
}
html[data-bs-theme="dark"] body.workspace,
html[data-bs-theme="dark"] .ws-app,
html[data-bs-theme="dark"] [id^="ws-"] {
  --ws-accent: var(--workspace-color-highlight);
  --ws-accent-rgb: 179, 157, 219;
  --ws-soft: rgba(var(--ws-accent-rgb), .1);
  --ws-shadow: 0 1px 2px rgba(0, 0, 0, .2), 0 10px 30px -14px rgba(0, 0, 0, .5);
  --ws-shadow-accent: 0 .75rem 1.5rem -.75rem rgba(var(--ws-accent-rgb), .35);
}

/* Focus rings follow the keyboard, not the mouse: the workspace is full of
   small controls, and a ring left on whatever was last clicked drew a box
   around it, so a click leaves no ring (:focus without :focus-visible),
   while the Tab key draws the accent ring the keyboard needs to see where
   it stands. Borders still change colour on a focused field. */
body.workspace *:focus:not(:focus-visible),
[id^="ws-"] *:focus:not(:focus-visible),
.ws-ctx *:focus:not(:focus-visible),
.ws-ask-modal *:focus:not(:focus-visible) {
  outline: 0;
  box-shadow: none;
}
body.workspace *:focus-visible,
[id^="ws-"] *:focus-visible,
.ws-ctx *:focus-visible,
.ws-ask-modal *:focus-visible {
  outline: 2px solid var(--ws-accent);
  outline-offset: 2px;
  box-shadow: none;
}

/* Drawers and the overview's cards sit on the scale too: a drawer's inner
   corners rounded and the panel lifted on the soft shadow instead of a
   hairline, a home card the same. */
body.workspace .offcanvas.offcanvas-end,
[id^="ws-"].offcanvas.offcanvas-end {
  border-left: 0;
  border-radius: var(--ws-radius-lg) 0 0 var(--ws-radius-lg);
  box-shadow: var(--ws-shadow);
}
.ws-home-card.card {
  border-color: transparent;
  border-radius: var(--ws-radius-lg);
  box-shadow: var(--ws-shadow);
}

/* On a touch screen the composer text is 16px so iOS does not zoom in on it. */
@media (pointer: coarse) {
  .ws-composer textarea {
    font-size: 16px;
  }
}
/* The panel gives list tables room above for their toolbar; the tables on
   the workspace screens sit inside cards and have none. */
body.workspace .card .table {
  margin-top: 0;
}
/* The workspace screens run to the edges of the page (their main area has
   no padding). The channel screen is one panel and fills it; the others keep
   their toolbar across the whole width and give what is under it the
   toolbar's own side padding, so the rounded cards do not meet the edge and
   line up with the first button above them. The settings screen gives its
   main area that padding itself, so its two columns keep their gutter. */
body.workspace:not(.workspace_settings) #content.p-0 > .row,
body.workspace:not(.workspace_settings) .ws-shell-main > .row {
  --bs-gutter-x: 0;
}
body.workspace:not(.workspace_channels) #ws-root {
  padding: 0 .75rem .75rem;
}
body.workspace_settings .ws-shell-main {
  padding: 0 .75rem .75rem;
}
body.workspace:not(.workspace_channels) #ws-root > .pg-toolbar,
body.workspace_settings .ws-shell-main > #button_bar {
  max-width: none;
  margin-left: -.75rem;
  margin-right: -.75rem;
}
body.workspace_settings .ws-shell-main > #button_bar {
  padding: .5rem .75rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
.ws-app {
  display: grid;
  grid-template-columns: 17rem minmax(0, 1fr);
  gap: 0;
  height: calc(100vh - 8.5rem);
  min-height: 28rem;
  background: var(--bs-body-bg);
  overflow: hidden;
  border-bottom: 1px solid var(--bs-border-color-translucent);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 10px 30px -12px rgba(0, 0, 0, .18);
  
}
@supports (height: 100dvh) {
  .ws-app { height: calc(100dvh - 8.5rem); }
}
/* A discussion beside the channel (threads.php): a third column on a wide
   screen; on a phone it covers the channel until it is closed (below). */
.ws-thread-panel {
  display: none;
}
.ws-app.ws-thread-open {
  grid-template-columns: 17rem minmax(0, 1fr) var(--ws-thread-w, minmax(20rem, 26rem));
}
.ws-app.ws-thread-open .ws-thread-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  border-left: 1px solid var(--bs-border-color);
  background: var(--bs-body-bg);
}
/* The discussion panel is widened by dragging its left edge; the strip
   sits over the border so the panel keeps its own padding. */
.ws-thread-resizer {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -3px;
  width: 7px;
  cursor: col-resize;
  z-index: 5;
  touch-action: none;
}
.ws-thread-resizer::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 3px;
  width: 1px;
  background: transparent;
  transition: background .15s;
}
.ws-thread-resizer:hover::after,
.ws-thread-resizer:focus-visible::after,
.ws-app.ws-thread-resizing .ws-thread-resizer::after {
  background: var(--ws-accent);
}
.ws-app.ws-thread-resizing {
  cursor: col-resize;
  user-select: none;
}
.ws-app.ws-thread-resizing iframe {
  pointer-events: none;
}
.ws-app.ws-thread-open .ws-composer-hint {
  display: none !important;
}
.ws-send-group .btn {
  white-space: nowrap;
}
.ws-thread-panel > .ws-head,
.ws-thread-panel > .alert,
.ws-thread-panel > .ws-thread-source,
.ws-thread-panel > .ws-composer-wrap {
  flex-shrink: 0;
}
.ws-thread-head .ws-head-title h2 {
  font-size: 1rem;
}
.ws-thread-source {
  max-height: 30%;
  overflow-y: auto;
  padding: .5rem 1rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
  background: var(--ws-soft);
  font-size: .85rem;
}
.ws-thread-source-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: .5rem;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}
.ws-thread-source-head .btn-link {
  font-size: .75rem;
  text-transform: none;
  letter-spacing: 0;
}
.ws-thread-source-body b {
  margin-right: .4rem;
}
.ws-thread-source-time {
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
/* Under a message of the channel: the discussion about it. */
.ws-thread-wrap {
  margin-top: .35rem;
}
.ws-thread-bar {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .45rem;
  max-width: 100%;
  padding: .3rem .6rem;
  border: 1px solid rgba(var(--ws-accent-rgb), .35);
  border-radius: var(--ws-radius-sm);
  background: var(--ws-soft);
  color: var(--bs-body-color);
  font-size: .8rem;
  text-align: left;
}
button.ws-thread-bar:hover {
  border-color: var(--ws-accent);
}
.ws-thread-bar.ws-thread-bar-news {
  border-color: var(--ws-accent);
}
.ws-thread-bar.ws-thread-bar-closed {
  border-style: dashed;
  background: transparent;
}
.ws-thread-bar-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.ws-thread-bar-text b {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-thread-bar-facts {
  font-size: .72rem;
  color: var(--bs-secondary-color);
}
.ws-thread-bar-go {
  color: var(--ws-accent);
  font-weight: 600;
  white-space: nowrap;
}
.ws-thread-faces img {
  width: 1.35rem;
  height: 1.35rem;
}
.ws-thread-ai {
  margin-top: .2rem;
  font-size: .72rem;
  color: var(--bs-secondary-color);
}
.ws-msg-from-thread {
  display: inline-flex;
  align-items: center;
  padding: 0 .45rem;
  border: 0;
  border-radius: var(--ws-radius);
  background: var(--ws-soft);
  color: var(--ws-accent);
  font-size: .7rem;
  font-weight: 600;
}
/* The sidebar's discussions: the title, and under it the channel. */
.ws-thread-link .ws-channel-name {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}
.ws-thread-link-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-thread-link-channel {
  font-size: .68rem;
  color: var(--bs-secondary-color);
}
@media (max-width: 1199.98px) {
  .ws-app.ws-thread-open {
    grid-template-columns: 14rem minmax(0, 1fr) var(--ws-thread-w, minmax(18rem, 22rem));
  }
}
.ws-side {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-right: 1px solid var(--bs-border-color);
  background: rgba(var(--ws-accent-rgb), .05);
}
.ws-side-head {
  padding: .75rem .75rem .5rem;
  display: flex;
  gap: .5rem;
  align-items: center;
}
.ws-side-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 .5rem .75rem;
}
.ws-side-section {
  margin-top: .75rem;
}
.ws-side-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  padding: 0 .5rem .25rem;
}
.ws-side-title .btn {
  padding: 0 .35rem;
  line-height: 1.2;
}
/* A section heading that opens and closes the section, drawn like a
   group's (below); a closed one keeps its count on the right. */
.ws-side-toggle {
  display: flex;
  align-items: center;
  gap: .3rem;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
}
.ws-side-toggle:hover {
  color: var(--bs-body-color);
}
.ws-side-toggle-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-side-more {
  font-size: .78rem;
  padding: .1rem .5rem;
  text-decoration: none;
}
.ws-channel-link {
  display: flex;
  align-items: center;
  gap: .4rem;
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--bs-body-color);
  text-align: left;
  border-radius: var(--ws-radius-sm);
  padding: .3rem .5rem;
  font-size: .9rem;
  text-decoration: none;
}
.ws-channel-link:hover {
  background: var(--bs-secondary-bg);
}
.ws-channel-link.active {
  background: rgba(var(--ws-accent-rgb), .16);
  color: var(--bs-emphasis-color);
  font-weight: 600;
  box-shadow: inset 3px 0 0 var(--ws-accent);
}
.ws-channel-link {
  position: relative;
  transition: background .12s ease;
}
.ws-channel-link .ws-pin {
  display: none;
  color: var(--bs-secondary-color);
  padding: 0 .15rem;
  border-radius: var(--ws-radius-xs);
}
.ws-channel-link:hover .ws-pin,
.ws-channel-link .ws-pin.ws-pinned {
  display: inline-block;
}
.ws-channel-link .ws-pin:hover {
  color: var(--ws-accent);
}
.ws-channel-link .ws-pin.ws-pinned {
  color: var(--ws-accent);
}
.ws-channel-link.ws-dragging {
  opacity: .45;
}
.ws-sortable .ws-channel-link {
  cursor: pointer;
}
.ws-channel-link .ws-channel-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-channel-link.ws-unread .ws-channel-name {
  font-weight: 700;
}
.ws-channel-link.ws-notjoined {
  color: var(--bs-secondary-color);
}
/* A channel's own colour on its sign; the rest of the list has a colour per
   part, so a channel and a screen never look alike. */
.ws-channel-link .ws-ch-icon {
  color: var(--bs-secondary-color);
}
.ws-channel-link[data-ws-colored] .ws-ch-icon {
  color: var(--ws-ch);
}
.ws-channel-link[data-ws-nav="home"] > .bi,
.ws-rail-item[data-ws-nav="home"] > .bi { color: #4a7fa8; }
.ws-channel-link[data-ws-nav="inbox"] > .bi,
.ws-rail-item[data-ws-nav="inbox"] > .bi { color: #3e8a7a; }
.ws-channel-link[data-ws-nav="tasks"] > .bi,
.ws-rail-item[data-ws-nav="tasks"] > .bi { color: #4f8a5b; }
.ws-channel-link[data-ws-nav="notes"] > .bi,
.ws-rail-item[data-ws-nav="notes"] > .bi { color: #c2913a; }
.ws-channel-link[data-ws-nav="board"] > .bi,
.ws-rail-item[data-ws-nav="board"] > .bi { color: #6b5ca5; }
.ws-channel-link[data-ws-nav="calendar"] > .bi,
.ws-rail-item[data-ws-nav="calendar"] > .bi { color: #a5578e; }
.ws-channel-link[data-ws-nav="scheduled"] > .bi,
.ws-rail-item[data-ws-nav="scheduled"] > .bi { color: #c9774a; }
.ws-channel-link[data-ws-nav="timeline"] > .bi,
.ws-rail-item[data-ws-nav="timeline"] > .bi { color: #5468a8; }
.ws-channel-link[data-ws-nav="archived"] > .bi,
.ws-rail-item[data-ws-nav="archived"] > .bi { color: #7a7f87; }
.ws-channel-link[data-ws-nav="audit"] > .bi,
.ws-rail-item[data-ws-nav="audit"] > .bi { color: #b5534b; }
.ws-channel-link[data-ws-nav="settings"] > .bi,
.ws-rail-item[data-ws-nav="settings"] > .bi { color: #6f6a5e; }
/* The same tones lifted for the dark theme, where the ones above sink. */
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="home"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="home"] > .bi { color: #8fbde3; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="inbox"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="inbox"] > .bi { color: #7fc9b8; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="tasks"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="tasks"] > .bi { color: #8fcf9b; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="notes"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="notes"] > .bi { color: #e6bb6c; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="board"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="board"] > .bi { color: #b1a4e0; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="calendar"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="calendar"] > .bi { color: #dc95c7; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="scheduled"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="scheduled"] > .bi { color: #e9a67f; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="timeline"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="timeline"] > .bi { color: #99a8e0; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="archived"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="archived"] > .bi { color: #a9aeb5; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="audit"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="audit"] > .bi { color: #e08a82; }
html[data-bs-theme="dark"] .ws-channel-link[data-ws-nav="settings"] > .bi,
html[data-bs-theme="dark"] .ws-rail-item[data-ws-nav="settings"] > .bi { color: #b3ab9b; }
/* Groups of channels: a heading with its colour, one inside another. */
.ws-side-group {
  margin-bottom: .2rem;
}
.ws-side-subgroup {
  margin-left: .6rem;
  padding-left: .3rem;
  border-left: 2px solid var(--ws-group, var(--bs-border-color));
}
.ws-group-title {
  justify-content: flex-start;
  gap: .15rem;
  padding: 0 .25rem .1rem 0;
  border-radius: var(--ws-radius-xs);
}
.ws-group-toggle {
  display: flex;
  align-items: center;
  gap: .35rem;
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
  padding: .2rem .25rem;
  border-radius: var(--ws-radius-xs);
}
.ws-group-toggle:hover {
  background: var(--bs-secondary-bg);
}
.ws-group-chevron {
  font-size: .65rem;
  opacity: .7;
}
.ws-group-icon {
  color: var(--ws-group, var(--bs-secondary-color));
  font-size: .85rem;
}
.ws-group-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ws-group, var(--bs-secondary-color));
}
.ws-group-access {
  display: inline-flex;
  align-items: center;
  gap: .15rem;
  font-size: .68rem;
  font-weight: 600;
  opacity: .75;
}
.ws-group-more {
  opacity: 0;
  padding: 0 .3rem;
}
.ws-group-title:hover .ws-group-more,
.ws-group-more:focus-visible {
  opacity: .85;
}
.ws-group-new {
  font-size: .78rem;
  padding: .1rem .5rem;
  text-decoration: none;
}
.ws-group-empty {
  padding-top: .15rem;
  padding-bottom: .25rem;
  border-radius: var(--ws-radius-xs);
}
.ws-drop-into {
  outline: 2px dashed var(--ws-group, var(--ws-accent));
  outline-offset: -2px;
}
.ws-drop-before {
  box-shadow: inset 0 2px 0 var(--ws-accent);
}
.ws-drop-after {
  box-shadow: inset 0 -2px 0 var(--ws-accent);
}
.ws-group-title.ws-dragging {
  opacity: .45;
}
/* The palette: twenty solid, matte colours and none. */
.ws-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
}
.ws-swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: 2px solid transparent;
  border-radius: var(--ws-radius-sm);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .08);
}
.ws-swatch.active {
  border-color: var(--bs-emphasis-color);
  box-shadow: inset 0 0 0 2px var(--bs-body-bg);
}
.ws-swatch-none {
  background: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
}
/* Scheduled actions: days, follow-ups, counts, the words filled in. */
.ws-sa-days {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
}
.ws-sa-days .btn {
  min-width: 2.6rem;
  padding: .15rem .4rem;
}
.ws-sa-when-more {
  display: grid;
  grid-template-columns: minmax(0, 12rem) minmax(0, 12rem);
  gap: .5rem;
  align-items: start;
  margin-top: .35rem;
  padding-left: 1.75rem;
}
.ws-sa-when-days {
  margin-top: .35rem;
  padding-left: 1.75rem;
}
.ws-sa-when-trigger {
  padding: .5rem .65rem;
  border: 1px dashed var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  margin-bottom: .5rem;
}
.ws-sa-follows {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  margin-bottom: .35rem;
}
.ws-sa-follow {
  padding: .5rem .6rem;
  border: 1px solid var(--bs-border-color);
  border-left: 3px solid var(--ws-accent);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
}
.ws-sa-follow-head {
  display: flex;
  align-items: center;
  gap: .35rem;
  margin-bottom: .4rem;
}
.ws-sa-follow-head .form-select {
  max-width: 14rem;
}
.ws-sa-follow-head .ws-sa-remove {
  margin-left: auto;
}
.ws-sa-metric-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: .1rem .75rem;
}
.ws-sa-metric {
  display: flex;
  flex-direction: column;
  gap: .35rem;
}
.ws-sa-words summary {
  cursor: pointer;
}
.ws-sa-words-list {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  padding: .4rem 0 0 .25rem;
}
.ws-sa-follow-line.ws-sa-on-failed b {
  color: var(--bs-danger-text-emphasis);
}
.ws-sa-follow-line.ws-sa-on-skipped b {
  color: var(--bs-secondary-color);
}
/* The pinned message, under the channel's greeting. */
.ws-pin-bar {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin: .5rem .75rem 0;
  padding: .35rem .5rem .35rem .65rem;
  border: 1px solid var(--bs-border-color);
  border-left: 3px solid var(--ws-accent);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
  font-size: .85rem;
}
.ws-pin-icon {
  color: var(--ws-accent);
}
.ws-pin-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  border: 0;
  background: transparent;
  color: inherit;
  text-align: left;
  padding: 0;
}
.ws-pin-by {
  flex-shrink: 0;
  font-size: .72rem;
  color: var(--bs-secondary-color);
}
.ws-msg-pinned {
  color: var(--ws-accent);
  font-size: .75rem;
}
/* Messages chosen together. */
.ws-msg-choosable {
  cursor: pointer;
  border-radius: var(--ws-radius-sm);
  padding-left: 2rem;
}
/* The tick box sits in the padding: .ws-msg is a grid, a pseudo element in
   it would take a cell of its own. */
.ws-msg-choosable::before {
  content: "";
  position: absolute;
  left: .5rem;
  top: .55rem;
  width: 1rem;
  height: 1rem;
  border: 2px solid var(--bs-border-color);
  border-radius: var(--ws-radius-xs);
}
.ws-msg-selected {
  background: rgba(var(--ws-accent-rgb), .1);
}
.ws-msg-selected::before {
  border-color: var(--ws-accent);
  background: var(--ws-accent);
  box-shadow: inset 0 0 0 2px var(--bs-body-bg);
}
.ws-select-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem;
  padding: .5rem .75rem;
  border-top: 1px solid var(--bs-border-color);
  background: var(--bs-body-bg);
}
.ws-select-count {
  font-size: .85rem;
  font-weight: 600;
  margin-right: .25rem;
}
.ws-selecting > :not(.ws-select-bar):not(.ws-jump-end) {
  display: none !important;
}
.ws-pane-selecting .ws-msg-tools {
  display: none !important;
}
.ws-msg-quote-more {
  margin-top: -.15rem;
}
/* A forward: the copies it carries. */
.ws-fwd-list {
  display: flex;
  flex-direction: column;
  gap: .4rem;
  margin-top: .3rem;
}
.ws-fwd {
  padding: .45rem .6rem;
  border: 1px solid var(--bs-border-color);
  border-left: 3px solid var(--bs-secondary-color);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
}
.ws-fwd-head {
  display: flex;
  align-items: center;
  font-size: .75rem;
  color: var(--bs-secondary-color);
  margin-bottom: .2rem;
}
.ws-fwd-who {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: .82rem;
  margin-bottom: .15rem;
}
.ws-fwd-who img {
  border-radius: 50%;
  object-fit: cover;
}
.ws-fwd-body {
  font-size: .9rem;
}
.ws-fwd-preview {
  max-height: 12rem;
  overflow: auto;
  padding: .4rem .6rem;
  margin-bottom: .75rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  font-size: .82rem;
}
.ws-fwd-preview-line {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The customer of a channel and the records tied to it. */
.ws-customer {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem;
  vertical-align: middle;
}
.ws-customer-tie .ws-chip {
  opacity: .75;
  font-size: .85em;
}
.ws-customer-tie .ws-chip:hover {
  opacity: 1;
}
.ws-customer-links {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  margin-bottom: .6rem;
}
.ws-customer-links:empty {
  display: none;
}
.ws-customer-links-title {
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--bs-secondary-color);
}
.ws-customer-link {
  display: flex;
  align-items: center;
  gap: .5rem;
}
/* A block with a title, and the list of titled blocks to pull in. */
.ws-block-titled {
  margin: .25rem 0;
}
.ws-block-title {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: .8rem;
  font-weight: 700;
  color: var(--bs-secondary-color);
  margin-bottom: .2rem;
}
.ws-block-title .bi {
  color: var(--ws-accent);
}
.ws-pull-list {
  display: flex;
  flex-direction: column;
  gap: .35rem;
}
.ws-pull-item {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  width: 100%;
  padding: .5rem .6rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  background: transparent;
  color: inherit;
  text-align: left;
}
.ws-pull-item:hover {
  background: var(--bs-tertiary-bg);
}
.ws-pull-icon {
  color: var(--ws-accent);
  margin-top: .15rem;
}
.ws-pull-words {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
/* The file manager's files under the paperclip (workspace.js libraryPicker). */
.ws-lib-trail {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ws-space-1);
  margin-bottom: var(--ws-space-2);
  font-size: .875rem;
}
.ws-lib-trail:empty {
  display: none;
}
.ws-lib-sep {
  font-size: .7rem;
  color: var(--bs-tertiary-color);
}
.ws-lib-section {
  margin-top: var(--ws-space-2);
  font-size: .8rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
}
.ws-lib-row {
  align-items: center;
}
.ws-lib-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}
.ws-lib-thumb {
  flex: 0 0 auto;
  width: var(--ws-avatar);
  height: var(--ws-avatar);
  object-fit: cover;
  border-radius: var(--ws-radius-xs);
  background: var(--bs-tertiary-bg);
}
.ws-lib-row .ws-pull-icon {
  flex: 0 0 auto;
  width: var(--ws-avatar);
  margin-top: 0;
  font-size: 1.35rem;
  text-align: center;
}
.ws-lib-refused {
  opacity: .6;
  cursor: not-allowed;
}
/* Versions of a conversation. */
.ws-era-row {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .6rem;
  margin-bottom: .35rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
}
.ws-era-current {
  background: var(--bs-tertiary-bg);
}
.ws-count {
  flex-shrink: 0;
  min-width: 1.35rem;
  padding: 0 .35rem;
  border-radius: 999px;
  font-size: .72rem;
  line-height: 1.35rem;
  text-align: center;
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
}
.ws-count.ws-count-mention {
  background: var(--bs-danger);
  color: #fff;
}
.ws-center {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}
.ws-head {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .6rem 1rem;
  border-bottom: 1px solid var(--bs-border-color);
  min-height: 3.4rem;
}
.ws-head-title {
  flex: 1 1 auto;
  min-width: 0;
}
/* The way back to the channel list on a phone: a labelled back arrow, so it
   is not taken for a second main menu button under the panel's own. */
.ws-channels-toggle {
  display: inline-flex;
  align-items: center;
  gap: .1rem;
  flex-shrink: 0;
  padding: .25rem .6rem .25rem .35rem;
  border: 0;
  border-radius: 999px;
  background: rgba(var(--ws-accent-rgb), .12);
  color: var(--ws-accent);
  font-size: .8rem;
  font-weight: 600;
  line-height: 1.2;
}
.ws-channels-toggle:hover,
.ws-channels-toggle:focus-visible {
  background: rgba(var(--ws-accent-rgb), .2);
}
@media (max-width: 359.98px) {
  .ws-channels-toggle-label {
    display: none;
  }
}
.ws-head-title h2 {
  font-size: 1.1rem;
  letter-spacing: -.01em;
  font-weight: 700;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-head-title .ws-topic {
  font-size: .8rem;
  color: var(--bs-secondary-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-head-actions {
  display: flex;
  gap: .25rem;
  flex-shrink: 0;
  align-items: center;
}
.ws-avatars {
  display: inline-flex;
}
.ws-avatars img {
  width: var(--ws-avatar-sm);
  height: var(--ws-avatar-sm);
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--bs-body-bg);
  margin-left: -.45rem;
}
.ws-avatars img:first-child {
  margin-left: 0;
}
.ws-tabs {
  display: flex;
  gap: .25rem;
  padding: .35rem 1rem 0;
  border-bottom: 1px solid var(--bs-border-color);
  overflow-x: auto;
}
.ws-tabs button {
  border: 0;
  background: transparent;
  padding: .4rem .7rem;
  font-size: .85rem;
  color: var(--bs-secondary-color);
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.ws-tabs button.active {
  color: var(--ws-accent);
  border-bottom-color: var(--ws-accent);
  font-weight: 600;
}
.ws-tabs button:hover {
  color: var(--bs-emphasis-color);
}
/* What a tab holds, before it is opened: a count of the decisions and the
   open tasks, a dot when the summary is written. */
.ws-tab-mark {
  display: inline-block;
  min-width: 1.15rem;
  margin-left: .35rem;
  padding: 0 .35rem;
  border-radius: var(--ws-radius);
  background: var(--ws-soft);
  color: var(--ws-accent);
  font-size: .68rem;
  font-weight: 700;
  line-height: 1.15rem;
  text-align: center;
  vertical-align: .05em;
}
.ws-tabs button.active .ws-tab-mark {
  background: var(--ws-accent);
  color: #fff;
}
.ws-tab-mark.ws-tab-dot {
  min-width: 0;
  width: .45rem;
  height: .45rem;
  padding: 0;
  margin-left: .3rem;
  line-height: 0;
  vertical-align: .15em;
  background: var(--ws-accent);
}
.ws-pane {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: .75rem 1rem;
}
/* Only the pane gives way when the screen is short: the header, the tabs and
   the composer keep their height (the tabs scroll sideways, so without this
   they were squeezed to a sliver). */
.ws-center > .ws-head,
.ws-center > .ws-tabs,
.ws-center > .ws-composer-wrap,
.ws-center > .alert {
  flex-shrink: 0;
}
.ws-center > .ws-brief {
  flex-shrink: 0;
  max-height: 40%;
  overflow-y: auto;
}
.ws-tabs {
  scrollbar-width: none;
}
.ws-tabs::-webkit-scrollbar {
  display: none;
}
/* The hint is hidden on narrow screens; the send button still goes right. */
.ws-composer-tools > .btn-primary:last-child {
  margin-left: auto;
}
.ws-pane-messages {
  padding-bottom: .25rem;
}
.ws-day {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin: .9rem 0 .4rem;
  font-size: .75rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
}
.ws-day::before,
.ws-day::after {
  content: "";
  flex: 1 1 auto;
  border-top: 1px solid var(--bs-border-color-translucent);
}
.ws-msg {
  position: relative;
  display: grid;
  grid-template-columns: 2.2rem minmax(0, 1fr);
  gap: .6rem;
  padding: .3rem .5rem;
  border-radius: var(--ws-radius-sm);
}
.ws-msg:hover {
  background: rgba(var(--bs-emphasis-color-rgb), .035);
}
.ws-msg.ws-msg-follow {
  padding-top: 0;
}
.ws-msg.ws-msg-follow .ws-msg-avatar {
  visibility: hidden;
  height: 0;
  overflow: hidden;
}
.ws-msg.ws-msg-follow .ws-msg-meta {
  display: none;
}
.ws-msg-main {
  grid-column: 2;
  min-width: 0;
}
.ws-msg.ws-msg-highlight {
  background: rgba(var(--bs-warning-rgb), .18);
}
.ws-msg-avatar img {
  width: var(--ws-avatar);
  height: var(--ws-avatar);
  border-radius: 50%;
  object-fit: cover;
}
.ws-msg-meta {
  font-size: .8rem;
  color: var(--bs-secondary-color);
  display: flex;
  gap: .5rem;
  align-items: baseline;
}
.ws-msg-meta b {
  color: var(--bs-body-color);
  font-size: .9rem;
}
.ws-msg-body {
  font-size: .92rem;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.ws-msg-body code {
  font-size: .85em;
  background: var(--bs-secondary-bg);
  padding: 0 .25rem;
  border-radius: .25rem;
}
.ws-msg-deleted .ws-msg-body {
  font-style: italic;
  color: var(--bs-secondary-color);
}
.ws-msg-system {
  display: block;
  text-align: center;
  font-size: .8rem;
  color: var(--bs-secondary-color);
  padding: .25rem 0;
}
.ws-msg-system .ws-chip {
  font-size: .78rem;
}
.ws-msg-flag {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  padding: 0 .4rem;
  border-radius: var(--ws-radius-xs);
}
.ws-msg-flag.ws-flag-decision {
  background: rgba(var(--bs-success-rgb), .15);
  color: var(--bs-success-text-emphasis);
}
.ws-msg-flag.ws-flag-note {
  background: rgba(var(--bs-info-rgb), .15);
  color: var(--bs-info-text-emphasis);
}
.ws-msg-decision,
.ws-msg-note {
  border-left: 3px solid var(--bs-success);
}
.ws-msg-note {
  border-left-color: var(--bs-info);
}
/* ── Workspace: status updates and check-ins (assets/js/workspace_status.js) ── */
.ws-msg-status {
  border-left: 3px solid var(--bs-success);
}
.ws-msg-status-at_risk {
  border-left-color: var(--bs-warning);
}
.ws-msg-status-off_track {
  border-left-color: var(--bs-danger);
}
.ws-status-pill {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  margin-right: .35rem;
  padding: 0 .5rem;
  border: 0;
  border-radius: var(--ws-radius);
  font-size: .72rem;
  font-weight: 600;
  line-height: 1.5;
  vertical-align: middle;
}
.ws-status-on_track {
  background: rgba(var(--bs-success-rgb), .14);
  color: var(--bs-success-text-emphasis);
}
.ws-status-at_risk {
  background: rgba(var(--bs-warning-rgb), .2);
  color: var(--bs-warning-text-emphasis);
}
.ws-status-off_track {
  background: rgba(var(--bs-danger-rgb), .14);
  color: var(--bs-danger-text-emphasis);
}
.ws-status-facts {
  margin-bottom: 1rem;
  padding: .6rem .7rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
}
.ws-status-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
}
.ws-status-chip {
  display: inline-flex;
  align-items: center;
  padding: .1rem .5rem;
  border-radius: var(--ws-radius);
  font-size: .75rem;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
}
.ws-status-chip-warn {
  border-color: rgba(var(--bs-warning-rgb), .6);
  color: var(--bs-warning-text-emphasis);
}
.ws-status-stale-task {
  display: flex;
  align-items: center;
  gap: .4rem;
  width: 100%;
  padding: .2rem 0;
  border: 0;
  background: transparent;
  font-size: .8rem;
  text-align: left;
  color: var(--bs-body-color);
}
.ws-status-stale-task:hover .ws-grow {
  text-decoration: underline;
}
.ws-status-choices {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
}
.ws-status-choice {
  border: 1px solid transparent;
  border-radius: var(--ws-radius);
}
.btn-check:checked + .ws-status-choice {
  border-color: currentColor;
  box-shadow: 0 0 0 1px currentColor;
}
.ws-checkin {
  max-width: 34rem;
  margin-top: .4rem;
  padding: .55rem .65rem .5rem;
  border: 1px solid var(--bs-border-color);
  border-left: 3px solid rgb(var(--ws-accent-rgb));
  border-radius: var(--ws-radius-sm);
  background: var(--bs-body-bg);
}
.ws-checkin-head {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin-bottom: .4rem;
  font-size: .8rem;
}
.ws-checkin-head > .bi {
  color: rgb(var(--ws-accent-rgb));
}
.ws-checkin-count {
  margin-left: auto;
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
.ws-checkin-answers {
  margin: 0 0 .4rem;
  padding: 0;
  list-style: none;
}
.ws-checkin-answer {
  display: flex;
  gap: .5rem;
  padding: .35rem 0;
  border-top: 1px solid var(--bs-border-color-translucent);
}
.ws-checkin-answer:first-child {
  border-top: 0;
}
.ws-checkin-face {
  flex: 0 0 1.6rem;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
}
.ws-checkin-text {
  min-width: 0;
  font-size: .85rem;
}
.ws-checkin-who {
  display: flex;
  gap: .4rem;
  align-items: baseline;
}
.ws-checkin-time {
  font-size: .72rem;
  color: var(--bs-secondary-color);
}
.ws-checkin-body {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.ws-checkin-waiting {
  margin-bottom: .4rem;
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
.ws-checkin-form-foot {
  display: flex;
  justify-content: flex-end;
  margin-top: .35rem;
}
.ws-msg-tools {
  position: absolute;
  top: -.6rem;
  right: .5rem;
  display: none;
  gap: .1rem;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  padding: .1rem;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .08);
}
.ws-msg:hover .ws-msg-tools,
.ws-msg:focus-within .ws-msg-tools {
  display: flex;
}
.ws-msg-tools button {
  border: 0;
  background: transparent;
  width: 1.8rem;
  height: 1.7rem;
  border-radius: var(--ws-radius-xs);
  color: var(--bs-secondary-color);
}
.ws-msg-tools button:hover {
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
}
.ws-msg-tools .ws-tool-emoji {
  font-size: .95rem;
  line-height: 1;
  padding: 0;
}
/* A message's text to select and copy by hand. 16px keeps iOS from zooming
   in on the field. */
.ws-copy-area {
  font-size: 16px;
  resize: vertical;
  max-height: 60vh;
}

/* Reactions under a message: who left one shows on hover. */
.ws-reactions {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  margin-top: .3rem;
}
.ws-react {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  height: 1.6rem;
  padding: 0 .45rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 999px;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  font-size: .78rem;
  line-height: 1;
}
.ws-react:hover {
  border-color: rgba(var(--ws-accent-rgb), .6);
}
.ws-react-emoji {
  font-size: .95rem;
}
.ws-react-count {
  font-weight: 600;
  color: var(--bs-secondary-color);
}
.ws-react.ws-react-mine {
  background: rgba(var(--ws-accent-rgb), .12);
  border-color: rgba(var(--ws-accent-rgb), .55);
}
.ws-react.ws-react-mine .ws-react-count {
  color: rgb(var(--ws-accent-rgb));
}
.ws-react.ws-react-readonly {
  cursor: default;
}
.ws-react.ws-react-readonly:hover {
  border-color: var(--bs-border-color);
}
.ws-react.ws-react-add {
  color: var(--bs-secondary-color);
  opacity: 0;
  transition: opacity .1s;
}
.ws-msg:hover .ws-react-add,
.ws-msg:focus-within .ws-react-add,
.ws-msg.ws-msg-active .ws-react-add {
  opacity: 1;
}

/* The emoji row at the top of a message's menu, and the emoji picker. */
.ws-ctx-emoji {
  display: flex;
  gap: .1rem;
  padding: .1rem .15rem .3rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
  margin-bottom: .2rem;
}
.ws-ctx-emoji-item,
.ws-emoji-item,
.ws-emoji-tab {
  border: 0;
  background: transparent;
  border-radius: var(--ws-radius-xs);
  line-height: 1;
  padding: 0;
  color: var(--bs-secondary-color);
}
.ws-ctx-emoji-item {
  width: 2rem;
  height: 2rem;
  font-size: 1.1rem;
}
.ws-ctx-emoji-item:hover,
.ws-ctx-emoji-item:focus-visible,
.ws-emoji-item:hover,
.ws-emoji-item:focus-visible,
.ws-emoji-tab:hover {
  background: var(--bs-secondary-bg);
}
.ws-ctx-emoji-item.active {
  background: rgba(var(--ws-accent-rgb), .16);
}
.ws-ctx.contextmenu.popover.ws-emoji-picker {
  width: 19.5rem;
  max-width: calc(100vw - 16px);
}
.ws-emoji {
  padding: .35rem;
}
.ws-emoji-search {
  margin-bottom: .35rem;
}
.ws-emoji-tabs {
  display: flex;
  gap: .05rem;
  padding-bottom: .3rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
.ws-emoji-tab {
  flex: 1 1 0;
  height: 1.9rem;
  font-size: 1rem;
  opacity: .6;
}
.ws-emoji-tab.active {
  opacity: 1;
  background: rgba(var(--ws-accent-rgb), .14);
}
.ws-emoji-title {
  font-size: .72rem;
  font-weight: 700;
  color: var(--bs-secondary-color);
  padding: .35rem .15rem .2rem;
}
.ws-emoji-grid {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  align-content: start;
  height: 12.6rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.ws-emoji-item {
  height: 2.1rem;
  font-size: 1.25rem;
}

/* Tables and checklists written in a message. */
.ws-msg-body .ws-text + .ws-table-wrap,
.ws-msg-body .ws-text + .ws-checklist,
.ws-msg-body .ws-table-wrap + .ws-text,
.ws-msg-body .ws-checklist + .ws-text {
  margin-top: .3rem;
}
.ws-table-wrap {
  max-width: 100%;
  overflow-x: auto;
  margin: .15rem 0 .3rem;
}
.ws-table-wrap .table.ws-table {
  width: auto;
  min-width: 40%;
  margin: 0;
  font-size: .85rem;
  border: 1px solid var(--bs-border-color);
}
.ws-table > :not(caption) > * > * {
  padding: .3rem .6rem;
  min-width: 4rem;
  max-width: 24rem;
  overflow-wrap: normal;
  vertical-align: top;
}
.ws-table thead th {
  background: var(--bs-tertiary-bg);
  font-weight: 600;
}
.ws-checklist {
  list-style: none;
  padding: 0;
  margin: 0 0 .15rem;
}
.ws-check {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 .5rem;
  padding: .1rem 0;
}
.ws-check label {
  display: inline-flex;
  align-items: baseline;
  gap: .45rem;
  margin: 0;
  cursor: pointer;
}
.ws-check .form-check-input {
  flex-shrink: 0;
  margin: 0;
  position: relative;
  top: .15em;
}
.ws-check .form-check-input:disabled {
  opacity: .7;
}
.ws-check.ws-checked .ws-check-text {
  text-decoration: line-through;
  color: var(--bs-secondary-color);
}
.ws-check-who {
  font-size: .72rem;
  color: var(--bs-secondary-color);
}

/* A poll in the conversation. */
.ws-poll {
  max-width: 34rem;
  margin-top: .4rem;
  padding: .55rem .65rem .45rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-body-bg);
}
.ws-poll-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem;
  margin-bottom: .45rem;
  font-size: .8rem;
}
.ws-poll-head > .bi {
  color: rgb(var(--ws-accent-rgb));
}
.ws-poll-tag {
  padding: 0 .4rem;
  border-radius: var(--ws-radius-xs);
  font-size: .68rem;
  font-weight: 600;
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}
.ws-poll-when {
  margin-left: auto;
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
.ws-poll-options {
  display: grid;
  gap: .3rem;
}
.ws-poll-option {
  position: relative;
  display: flex;
  align-items: center;
  gap: .5rem;
  margin: 0;
  padding: .4rem .55rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-xs);
  overflow: hidden;
  font-size: .88rem;
  cursor: pointer;
}
.ws-poll-option > * {
  position: relative;
}
.ws-poll-option > .ws-poll-bar {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  background: rgba(var(--ws-accent-rgb), .1);
  transition: width .25s ease;
}
.ws-poll-option .form-check-input {
  flex-shrink: 0;
  margin: 0;
}
.ws-poll-label {
  flex: 1 1 auto;
  min-width: 0;
}
.ws-poll-count {
  font-size: .75rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}
.ws-poll-option.ws-poll-mine {
  border-color: rgba(var(--ws-accent-rgb), .55);
}
.ws-poll-option.ws-poll-winner {
  border-color: var(--bs-success);
}
.ws-poll-option.ws-poll-winner > .ws-poll-bar {
  background: rgba(var(--bs-success-rgb), .16);
}
.ws-poll-closed .ws-poll-option {
  cursor: default;
}
.ws-poll-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem;
  margin-top: .4rem;
  font-size: .78rem;
  color: var(--bs-secondary-color);
}
.ws-poll-note {
  padding: .5rem .6rem;
  border-radius: var(--ws-radius-sm);
  font-size: .8rem;
  color: var(--bs-secondary-color);
  background: var(--bs-tertiary-bg);
}

/* The table size picker above the composer. */
.ws-ctx.contextmenu.popover.ws-table-picker {
  min-width: 0;
}
.ws-table-pick {
  padding: .45rem;
}
.ws-table-pick-size {
  margin-bottom: .35rem;
  font-size: .75rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
}
.ws-table-pick-grid {
  display: grid;
  grid-template-columns: repeat(6, 1.4rem);
  gap: .2rem;
}
.ws-table-pick-cell {
  width: 1.4rem;
  height: 1.4rem;
  padding: 0;
  border: 1px solid var(--bs-border-color);
  border-radius: .2rem;
  background: var(--bs-body-bg);
}
.ws-table-pick-cell.active {
  border-color: rgba(var(--ws-accent-rgb), .7);
  background: rgba(var(--ws-accent-rgb), .25);
}
@media (pointer: coarse) {
  .ws-emoji-item {
    height: 2.5rem;
  }
  .ws-react {
    height: 1.9rem;
  }
}

/* How far a task's checklist has got: on its card, in lists, on the board,
   under the channel list it came from and in its drawer. */
.ws-progress.progress {
  height: .45rem;
  background: var(--bs-secondary-bg);
}
.ws-progress .progress-bar {
  background: rgb(var(--ws-accent-rgb));
  transition: width .25s ease;
}
.ws-progress.ws-progress-full .progress-bar {
  background: var(--bs-success);
}
.ws-progress.ws-progress-sm {
  height: .3rem;
  width: 3.5rem;
  flex-shrink: 0;
}
.ws-task-progress {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin: .35rem 0 .1rem;
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
.ws-task-progress .progress {
  flex: 1 1 auto;
  max-width: 14rem;
  height: .4rem;
  background: var(--bs-secondary-bg);
}
.ws-task-progress .progress-bar {
  background: rgb(var(--ws-accent-rgb));
}
.ws-task-progress.ws-task-progress-full .progress-bar {
  background: var(--bs-success);
}
.ws-task-progress-hint {
  font-size: .78rem;
  color: var(--bs-success-text-emphasis);
}
.ws-task-card-notes {
  font-size: .75rem;
  color: var(--bs-secondary-color);
  margin-top: .15rem;
}
.ws-row-progress {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-size: .75rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}
.ws-row-notes {
  font-size: .75rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}
.ws-board-item.ws-item-progress {
  background-image: linear-gradient(to right, rgba(var(--ws-accent-rgb), .55) var(--ws-progress, 0%), transparent var(--ws-progress, 0%));
  background-size: 100% 2px;
  background-position: left bottom;
  background-repeat: no-repeat;
}
.ws-list-tasks {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
  margin-top: .3rem;
}
.ws-list-task {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  max-width: 100%;
  padding: .2rem .55rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  font-size: .78rem;
}
.ws-list-task:hover {
  border-color: rgba(var(--ws-accent-rgb), .6);
}
.ws-list-task > .bi {
  color: rgb(var(--ws-accent-rgb));
}
.ws-list-task-number {
  font-weight: 600;
  color: var(--bs-secondary-color);
}
.ws-list-task-title {
  min-width: 0;
  max-width: 16rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-list-task-share {
  font-weight: 600;
  color: var(--bs-secondary-color);
}
.ws-list-task.ws-list-task-closed .ws-list-task-title {
  text-decoration: line-through;
  color: var(--bs-secondary-color);
}

/* The task drawer: progress, lists, notes. */
.ws-task-link-note {
  display: flex;
  align-items: flex-start;
  margin-bottom: 1rem;
  padding: .55rem .7rem;
  border-radius: var(--ws-radius-sm);
  font-size: .84rem;
  background: rgba(var(--ws-accent-rgb), .08);
}
.ws-task-work {
  margin-bottom: 1rem;
  padding: .65rem .75rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
}
.ws-task-work-head {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  margin-bottom: .35rem;
  font-size: .85rem;
}
.ws-task-work-done {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-top: .5rem;
  font-size: .84rem;
  color: var(--bs-success-text-emphasis);
}
.ws-task-list {
  margin-top: .65rem;
}
.ws-task-list-head {
  display: flex;
  align-items: center;
  gap: .35rem;
  margin-bottom: .2rem;
  font-size: .75rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
}
.ws-task-notes {
  margin-top: .5rem;
  padding-top: .75rem;
  border-top: 1px solid var(--bs-border-color);
}
.ws-task-notes-head {
  display: flex;
  align-items: center;
  margin-bottom: .5rem;
  font-size: .9rem;
}
.ws-task-note-list {
  display: grid;
  gap: .6rem;
  margin-bottom: .6rem;
}
.ws-task-note {
  display: grid;
  grid-template-columns: 1.8rem minmax(0, 1fr);
  gap: .5rem;
}
.ws-task-note-avatar {
  width: var(--ws-avatar-sm);
  height: var(--ws-avatar-sm);
  border-radius: 50%;
  object-fit: cover;
}
.ws-task-note-meta {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
.ws-task-note-meta b {
  font-size: .82rem;
  color: var(--bs-body-color);
}
.ws-task-note-tools {
  margin-left: auto;
  opacity: 0;
  transition: opacity .1s;
}
.ws-task-note:hover .ws-task-note-tools,
.ws-task-note:focus-within .ws-task-note-tools {
  opacity: 1;
}
.ws-note-compose {
  position: relative;
}
.ws-note-compose .ws-picker {
  left: 0;
  right: 0;
  width: auto;
  bottom: calc(100% + .25rem);
}
/* The description sits high in the drawer: its picker opens below it. */
.ws-desc-field .ws-picker {
  top: calc(100% + .25rem);
  bottom: auto;
}
/* The formatted box in the task drawer (description and notes). */
.ws-rich.ws-rich-field {
  min-height: 3.2rem;
  max-height: 18rem;
  overflow-y: auto;
  font-size: .9rem;
  line-height: 1.45;
}
.ws-desc-field .ws-rich.ws-rich-field {
  min-height: 4.6rem;
}
/* The channel summary: a page of its own, its picker below the caret line. */
.ws-summary-field .ws-rich.ws-rich-field {
  min-height: 16rem;
  max-height: 60vh;
}
.ws-item-add {
  margin-top: .4rem;
}
.ws-note-actions {
  display: flex;
  align-items: center;
  gap: .25rem;
  margin-top: .35rem;
}
@media (pointer: coarse) {
  .ws-task-note-tools {
    opacity: 1;
  }
}
@media (max-width: 575.98px) {
  .ws-row-progress .ws-progress,
  .ws-list-task .ws-progress {
    display: none;
  }
  .ws-list-task-title {
    max-width: 10rem;
  }
}
.ws-msg-quote {
  display: block;
  max-width: 100%;
  font-size: .8rem;
  color: var(--bs-secondary-color);
  border: 0;
  border-left: 2px solid var(--bs-border-color);
  border-radius: 0;
  background: transparent;
  padding: 0 0 0 .5rem;
  margin-bottom: .2rem;
  overflow: hidden;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}
.ws-msg-quote:hover {
  color: var(--bs-body-color);
  border-left-color: var(--ws-accent);
}
/* The overview: the pressing tasks lead, marked by their priority. */
.ws-home-hot {
  margin-left: auto;
  font-weight: 600;
  color: var(--bs-danger-text-emphasis);
  text-decoration: none;
}
.ws-home-hot:hover {
  text-decoration: underline;
}
.ws-home-row.ws-home-row-hot {
  box-shadow: inset 3px 0 0 var(--bs-warning);
}
.ws-home-row.ws-home-row-urgent {
  box-shadow: inset 3px 0 0 var(--bs-danger);
}
.ws-home-hot-icon {
  color: var(--bs-warning-text-emphasis);
}
.ws-home-row-urgent .ws-home-hot-icon {
  color: var(--bs-danger);
}
.ws-home-priority {
  display: inline-block;
  margin-left: .35rem;
  padding: 0 .4rem;
  border-radius: 999px;
  font-size: .68rem;
  font-weight: 700;
  vertical-align: middle;
  background: rgba(var(--bs-warning-rgb), .2);
  color: var(--bs-warning-text-emphasis);
}
.ws-home-priority-urgent {
  background: rgba(var(--bs-danger-rgb), .16);
  color: var(--bs-danger-text-emphasis);
}
/* Scheduled actions: the card (in its channel, its note and the list) and
   the form. */
.ws-sa-card {
  max-width: 40rem;
  margin: .25rem 0;
  padding: .6rem .75rem;
  border: 1px solid var(--bs-border-color);
  border-left: 3px solid var(--ws-accent);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-body-bg);
  font-size: .85rem;
}
.ws-sa-card.ws-sa-paused {
  border-left-color: var(--bs-warning);
}
.ws-sa-card.ws-sa-failed {
  border-left-color: var(--bs-danger);
}
.ws-sa-card.ws-sa-done,
.ws-sa-card.ws-sa-cancelled {
  border-left-color: var(--bs-secondary-color);
  opacity: .85;
}
.ws-sa-card-head {
  display: flex;
  align-items: center;
  gap: .45rem;
  margin-bottom: .3rem;
}
.ws-sa-card-icon {
  color: var(--ws-accent);
}
.ws-sa-card-name {
  flex: 1 1 auto;
  min-width: 0;
}
.ws-sa-state {
  flex: none;
  padding: .05rem .5rem;
  border-radius: 999px;
  font-size: .7rem;
  font-weight: 600;
  background: rgba(var(--ws-accent-rgb), .14);
  color: var(--ws-accent);
}
.ws-sa-state-paused {
  background: rgba(var(--bs-warning-rgb), .18);
  color: var(--bs-warning-text-emphasis);
}
.ws-sa-state-failed {
  background: rgba(var(--bs-danger-rgb), .14);
  color: var(--bs-danger-text-emphasis);
}
.ws-sa-state-done,
.ws-sa-state-cancelled {
  background: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
}
.ws-sa-line {
  display: flex;
  align-items: baseline;
  gap: .1rem;
  margin: .15rem 0;
  color: var(--bs-body-color);
}
/* A card reads as its flow: each line hangs on a thin rail. */
.ws-sa-card > .ws-sa-line {
  position: relative;
  padding-left: .15rem;
}
.ws-sa-card > .ws-sa-line + .ws-sa-line::before {
  content: "";
  position: absolute;
  left: .42rem;
  top: -.35rem;
  height: .4rem;
  border-left: 1px dashed rgba(var(--ws-accent-rgb), .4);
}
.ws-sa-line > .bi {
  flex: none;
  color: var(--bs-secondary-color);
}
.ws-sa-last-failed {
  color: var(--bs-danger-text-emphasis);
}
.ws-sa-last-skipped {
  color: var(--bs-warning-text-emphasis);
}
.ws-sa-meta {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  margin-top: .3rem;
  font-size: .72rem;
  color: var(--bs-secondary-color);
}
.ws-sa-tools {
  display: flex;
  flex-wrap: wrap;
  gap: .15rem;
  margin-top: .35rem;
}
.ws-sa-history {
  margin-top: .35rem;
  padding-top: .35rem;
  border-top: 1px dashed var(--bs-border-color);
}
.ws-sa-history-row {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  font-size: .76rem;
  margin-bottom: .2rem;
}
.ws-sa-strip {
  margin-bottom: .5rem;
}
.ws-sa-view .ws-sa-filters {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  margin-bottom: .5rem;
}
.ws-sa-list-item {
  margin-bottom: .75rem;
}
.ws-sa-where {
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
.ws-sa-offcanvas.offcanvas-end {
  --bs-offcanvas-width: min(40rem, 100vw);
}
.ws-sa-section {
  margin-top: 1rem;
  padding-top: .75rem;
  border-top: 1px solid var(--bs-border-color-translucent);
}
.ws-sa-section-title {
  font-weight: 700;
  font-size: .85rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--bs-secondary-color);
}
/* The form as a flow: its steps down the step rail (Admin screen step rail
   above), each a box; the outline above them says in words what it will do. */
.ws-sa-flow {
  margin-top: .75rem;
}
.ws-sa-flow .pg-step {
  grid-template-columns: 2.25rem minmax(0, 1fr);
}
.ws-sa-flow .pg-step + .pg-step {
  margin-top: .5rem;
}
.ws-sa-flow .pg-step-rail::before {
  bottom: -.5rem;
}
.ws-sa-flow .pg-step-node {
  border-color: rgba(var(--ws-accent-rgb), .55);
  color: var(--ws-accent);
}
.ws-sa-flow .ws-sa-section {
  margin-top: 0;
  padding: .6rem .75rem .7rem;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius);
  background: var(--bs-body-bg);
}
.ws-sa-step-number {
  display: inline-block;
  margin-right: .35rem;
  color: var(--ws-accent);
}
.ws-sa-when-join {
  max-width: 24rem;
}
.ws-sa-outline {
  margin-top: .5rem;
  padding: .55rem .7rem;
  border-radius: var(--ws-radius);
  background: var(--ws-soft);
  font-size: .8rem;
}
.ws-sa-outline:empty {
  display: none;
}
.ws-sa-outline-row {
  display: grid;
  grid-template-columns: 1.1rem auto minmax(0, 1fr);
  gap: .35rem;
  align-items: baseline;
}
.ws-sa-outline-row + .ws-sa-outline-row {
  margin-top: .2rem;
}
.ws-sa-outline-row .bi {
  color: var(--ws-accent);
}
.ws-sa-outline-row b {
  white-space: nowrap;
}
.ws-sa-outline.ws-sa-outline-error {
  background: rgba(var(--bs-danger-rgb), .08);
  color: var(--bs-danger-text-emphasis);
}
.ws-sa-sentence {
  margin-bottom: .75rem;
  padding: .6rem .75rem;
  border: 1px dashed rgba(var(--ws-accent-rgb), .55);
  border-radius: var(--ws-radius);
  background: var(--ws-soft);
}
.ws-sa-sentence-row {
  display: flex;
  gap: .5rem;
  align-items: flex-start;
}
.ws-sa-sentence-row textarea {
  flex: 1 1 auto;
  min-width: 0;
}
.ws-sa-sentence-row .btn {
  flex: none;
  white-space: nowrap;
}
.ws-sa-sentence-said {
  margin-top: .35rem;
  color: var(--bs-secondary-color);
}
.ws-sa-gallery-wrap {
  margin-bottom: .75rem;
}
.ws-sa-gallery-wrap > summary {
  list-style: none;
  cursor: pointer;
}
.ws-sa-gallery-wrap > summary::-webkit-details-marker {
  display: none;
}
.ws-sa-gallery-title {
  font-weight: 700;
  font-size: .85rem;
  color: var(--bs-secondary-color);
}
.ws-sa-gallery-wrap > summary .ws-sa-gallery-title::after {
  content: " ▾";
}
.ws-sa-gallery-wrap[open] > summary .ws-sa-gallery-title::after {
  content: " ▴";
}
.ws-sa-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12.5rem, 1fr));
  gap: .45rem;
  margin: .5rem 0 .75rem;
}
.ws-sa-template {
  display: flex;
  gap: .55rem;
  align-items: flex-start;
  padding: .5rem .6rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  text-align: left;
  font-size: .8rem;
}
.ws-sa-template:hover,
.ws-sa-template.active {
  border-color: var(--ws-accent);
  background: var(--ws-soft);
}
.ws-sa-template-icon {
  flex: none;
  font-size: 1.05rem;
  color: var(--ws-accent);
}
.ws-sa-template-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.ws-sa-template-text small {
  color: var(--bs-secondary-color);
}
.ws-sa-when {
  display: grid;
  grid-template-columns: auto 1fr 7rem 10rem;
  gap: .5rem;
  align-items: end;
}
.ws-sa-when-icon {
  font-size: 1.2rem;
  color: var(--ws-accent);
  padding-bottom: .35rem;
}
.ws-sa-condition {
  display: flex;
  gap: .35rem;
  align-items: flex-start;
  margin-top: .5rem;
  padding: .5rem;
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
}
.ws-sa-condition-grid {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: .4rem;
}
.ws-sa-field .form-label {
  color: var(--bs-secondary-color);
}
.ws-sa-action-detail {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  margin-top: .5rem;
}
.ws-sa-body .ws-rich.ws-rich-field {
  min-height: 6rem;
}
.ws-sa-field-row {
  display: flex;
  gap: .35rem;
  align-items: flex-start;
  margin-bottom: .35rem;
}
.ws-sa-field-row > .form-select {
  flex: 0 0 11rem;
}
.ws-sa-picker {
  position: relative;
}
.ws-sa-picker-list {
  position: absolute;
  z-index: 10;
  left: 0;
  right: 0;
  top: calc(100% + .15rem);
  max-height: 14rem;
  overflow-y: auto;
  padding: .25rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-body-bg);
  box-shadow: 0 8px 24px -8px rgba(0, 0, 0, .25);
}
.ws-sa-picker-item {
  display: flex;
  align-items: center;
  gap: .4rem;
  width: 100%;
  padding: .3rem .45rem;
  border: 0;
  border-radius: var(--ws-radius-xs);
  background: transparent;
  font-size: .82rem;
  text-align: left;
}
.ws-sa-picker-item:hover {
  background: var(--bs-tertiary-bg);
}
@media (max-width: 575.98px) {
  .ws-sa-when {
    grid-template-columns: 1fr 1fr;
  }
  .ws-sa-when-icon {
    display: none;
  }
  .ws-sa-field-row {
    flex-wrap: wrap;
  }
  .ws-sa-field-row > .form-select {
    flex: 1 1 100%;
  }
}
.ws-delete-scopes .form-check {
  margin-bottom: .45rem;
}
/* Who has seen a message. */
.ws-seen-list {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  margin: .35rem 0 .6rem;
}
.ws-seen-row {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .9rem;
}
/* A picture or a name that opens the person's account (edit_user.php). */
a.ws-person-link {
  color: inherit;
  text-decoration: none;
}
a.ws-person-link:hover b,
a.ws-person-link:hover div,
a.ws-person-link:hover span,
a.ws-person-link:focus-visible b {
  text-decoration: underline;
}
a.ws-person-link:hover img {
  box-shadow: 0 0 0 2px var(--bs-primary);
}
.ws-seen-row a.ws-person-link {
  display: inline-flex;
  min-width: 0;
}
.ws-msg-avatar a.ws-person-link {
  display: block;
  line-height: 0;
}
.ws-seen-row img {
  flex: none;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-seen-row span {
  min-width: 0;
  overflow-wrap: anywhere;
}
/* Changed after it was sent. */
.ws-msg-edited {
  display: inline-block;
  margin-left: .35rem;
  font-size: .7rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
  vertical-align: baseline;
}
.ws-msg-edited .bi {
  font-size: .65rem;
}
/* The messages a reply answers, lit up for a moment when it is followed. */
@keyframes ws-msg-flash {
  0%, 35% { background: rgba(var(--ws-accent-rgb), .28); box-shadow: inset 3px 0 0 var(--ws-accent); }
  100% { background: transparent; box-shadow: inset 3px 0 0 transparent; }
}
.ws-msg.ws-msg-flash {
  animation: ws-msg-flash 2.6s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .ws-msg.ws-msg-flash {
    animation: none;
    background: rgba(var(--ws-accent-rgb), .2);
  }
}
.ws-msg-file {
  margin-top: .35rem;
}
.ws-msg-file img {
  display: block;
  max-width: min(26rem, 100%);
  max-height: 20rem;
  min-width: 4rem;
  min-height: 3rem;
  object-fit: contain;
  border-radius: var(--ws-radius-sm);
  border: 1px solid var(--bs-border-color-translucent);
  background: var(--bs-tertiary-bg);
}
.ws-file-link {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .35rem .6rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  text-decoration: none;
  color: var(--bs-body-color);
  font-size: .85rem;
}
.ws-file-ext {
  font-size: .68rem;
  font-weight: 700;
  text-transform: uppercase;
  background: var(--bs-secondary-bg);
  border-radius: var(--ws-radius-xs);
  padding: .05rem .35rem;
}
.ws-chip {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  max-width: 100%;
  vertical-align: baseline;
  padding: .02rem .5rem;
  border-radius: 999px;
  font-size: .85em;
  line-height: 1.5;
  background: rgba(var(--bs-primary-rgb), .12);
  color: var(--bs-primary-text-emphasis);
  text-decoration: none;
  white-space: nowrap;
}
.ws-chip > span {
  overflow: hidden;
  text-overflow: ellipsis;
}
a.ws-chip:hover {
  background: rgba(var(--bs-primary-rgb), .22);
}
.ws-chip-user,
.ws-chip-dept {
  background: rgba(var(--bs-warning-rgb), .18);
  color: var(--bs-warning-text-emphasis);
}
.ws-chip-task {
  background: rgba(var(--bs-success-rgb), .14);
  color: var(--bs-success-text-emphasis);
}
.ws-chip-muted {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}
.ws-task-card {
  margin-top: .35rem;
  max-width: 36rem;
  border: 1px solid var(--bs-border-color-translucent);
  border-left: 4px solid var(--ws-accent);
  border-radius: var(--ws-radius-sm);
  padding: .6rem .85rem;
  background: var(--bs-tertiary-bg);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
}
.ws-task-card.ws-task-done {
  border-left-color: var(--bs-success);
  opacity: .8;
}
.ws-task-card.ws-task-done .ws-task-card-title {
  text-decoration: line-through;
}
.ws-task-card.ws-task-cancelled {
  border-left-color: var(--bs-secondary);
  opacity: .7;
}
.ws-task-card.ws-task-waiting {
  border-left-color: var(--bs-warning);
}
.ws-task-card-head {
  display: flex;
  gap: .4rem;
  align-items: center;
  font-size: .75rem;
}
.ws-task-number {
  font-weight: 700;
  color: var(--bs-secondary-color);
}
.ws-task-status {
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
  font-weight: 600;
}
.ws-task-priority {
  background: var(--bs-danger);
}
.ws-task-card-title {
  font-weight: 600;
  margin: .15rem 0;
}
.ws-task-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  font-size: .8rem;
  color: var(--bs-secondary-color);
}
.ws-task-card-people {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin-top: .35rem;
  font-size: .8rem;
}
.ws-task-person {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
}
.ws-task-person img {
  width: 1.2rem;
  height: 1.2rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-task-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  margin-top: .45rem;
}
.ws-composer-wrap {
  position: relative;
  padding: .5rem 1rem .75rem;
}
/* Back down to the newest line, over the end of the conversation. */
.ws-jump-end {
  position: absolute;
  right: 1.5rem;
  bottom: calc(100% + .5rem);
  z-index: 4;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .3rem;
  min-width: 2.4rem;
  height: 2.4rem;
  padding: 0 .7rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 999px;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  box-shadow: 0 4px 14px -4px rgba(0, 0, 0, .25);
  font-size: 1rem;
}
.ws-jump-end[hidden] {
  display: none;
}
.ws-jump-end:hover {
  background: var(--bs-tertiary-bg);
}
.ws-jump-end.ws-jump-news {
  border-color: var(--ws-accent);
  color: var(--ws-accent);
}
.ws-jump-count {
  min-width: 1.2rem;
  padding: 0 .35rem;
  border-radius: 999px;
  background: var(--ws-accent);
  color: #fff;
  font-size: .72rem;
  font-weight: 700;
  line-height: 1.2rem;
}
.ws-jump-count[hidden] {
  display: none;
}
/* A room a guest reads (includes/workspace/guests.php). */
.ws-guest-bar-text {
  flex: 1 1 18rem;
  min-width: 0;
}
.ws-guest-bar .ws-guest-state {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem .75rem;
}
.ws-guest-online {
  color: var(--bs-success-text-emphasis);
  font-weight: 600;
}
.ws-guest-online::before {
  content: "";
  display: inline-block;
  width: .5rem;
  height: .5rem;
  margin-right: .3rem;
  border-radius: 50%;
  background: var(--bs-success);
  vertical-align: middle;
}
.ws-guest-badge {
  background: var(--bs-warning-bg-subtle);
  color: var(--bs-warning-text-emphasis);
  font-weight: 600;
  font-size: .7rem;
}
.ws-guest-room .ws-composer {
  border-color: var(--bs-warning-border-subtle);
}
.ws-guest-duration {
  margin: .35rem 0 0 1.5rem;
  max-width: 14rem;
}
.ws-composer {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius);
  background: var(--bs-body-bg);
  padding: .45rem .55rem .35rem;
  box-shadow: var(--ws-shadow);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.ws-composer:focus-within {
  border-color: rgba(var(--ws-accent-rgb), .65);
  box-shadow: var(--ws-shadow-accent);
}
/* The frame above is the writing field's focus mark, so the field inside
   draws no ring of its own. A text field matches :focus-visible on a mouse
   click too, and the workspace-wide ring rule (body.workspace *:focus-visible)
   outranks the field's outline: 0, which drew a second ring inside the frame. */
.ws-composer .ws-rich:focus-visible,
.ws-composer > textarea:focus-visible {
  outline: 0;
  box-shadow: none;
}
.ws-composer textarea {
  display: block;
  width: 100%;
  resize: none;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--bs-body-color);
  font-size: .92rem;
  line-height: 1.45;
  min-height: 1.6rem;
  max-height: 12rem;
  padding: .15rem .25rem;
}
.ws-composer-tools {
  display: flex;
  align-items: center;
  gap: .15rem;
}
.ws-composer-tools .ws-grow {
  flex: 1 1 auto;
}
.ws-composer-hint {
  font-size: .72rem;
  color: var(--bs-secondary-color);
}

/* The writing box: formatted text, chips and cards, and still a chat field.
   (assets/js/workspace_editor.js) */
.ws-rich {
  position: relative;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  cursor: text;
}
.ws-composer .ws-rich {
  display: block;
  width: 100%;
  border: 0;
  border-radius: 0;
  outline: 0;
  box-shadow: none;
  background: transparent;
  color: var(--bs-body-color);
  font-size: .92rem;
  line-height: 1.45;
  min-height: 1.6rem;
  max-height: 12rem;
  overflow-y: auto;
  padding: .15rem .25rem;
}
.ws-rich.is-empty::before,
.ws-cell-edit.is-empty::before {
  content: attr(data-placeholder);
  position: absolute;
  color: var(--bs-secondary-color);
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: calc(100% - .5rem);
}
.ws-cell-edit.is-empty::before {
  position: static;
  display: inline-block;
}
.ws-rich-surface code {
  font-family: var(--bs-font-monospace);
  font-size: .85em;
  background: var(--bs-secondary-bg);
  padding: 0 .25rem;
  border-radius: .25rem;
}
.ws-rich-surface a {
  color: var(--bs-link-color);
  text-decoration: underline;
  cursor: text;
}
.ws-rich-chip {
  cursor: default;
  margin: 0 .05rem;
  user-select: all;
}
.ws-rich-block {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  max-width: 100%;
  margin: .15rem 0;
  padding: .3rem .3rem .3rem .6rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
  font-size: .8rem;
  line-height: 1.3;
  white-space: nowrap;
  vertical-align: middle;
  cursor: pointer;
  user-select: none;
  transition: border-color .15s ease;
}
.ws-rich-block:hover {
  border-color: rgba(var(--ws-accent-rgb), .6);
}
.ws-rich-block > .bi {
  flex-shrink: 0;
  font-size: 1rem;
  color: rgba(var(--ws-accent-rgb), 1);
}
.ws-rich-block-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.ws-rich-block-text > b {
  font-weight: 600;
}
.ws-rich-block-text > span {
  max-width: 22rem;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--bs-secondary-color);
}
.ws-rich-block-remove {
  flex-shrink: 0;
  border: 0;
  background: transparent;
  color: var(--bs-secondary-color);
  padding: .2rem .3rem;
  border-radius: var(--ws-radius-xs);
  line-height: 1;
}
.ws-rich-block-remove:hover {
  background: var(--bs-secondary-bg);
  color: var(--bs-danger);
}

/* In a note a card shows what it holds, drawn as in a message; a click on it
   still opens its window. */
.ws-rich-block.ws-rich-block-shown {
  position: relative;
  display: inline-block;
  width: 100%;
  margin: .1rem 0;
  padding: .3rem 2rem .3rem .5rem;
  border-color: transparent;
  background: transparent;
  font-size: inherit;
  line-height: inherit;
  white-space: normal;
  vertical-align: top;
  transition: border-color .15s ease, background-color .15s ease;
}
.ws-rich-block.ws-rich-block-shown:hover {
  border-color: rgba(var(--ws-accent-rgb), .45);
  background: var(--bs-tertiary-bg);
}
.ws-rich-block-shown > .bi,
.ws-rich-block-shown > .ws-rich-block-text {
  display: none;
}
.ws-rich-block-view {
  display: block;
  min-width: 0;
}
.ws-rich-block-view a,
.ws-rich-block-view input,
.ws-rich-block-view button {
  pointer-events: none;
}
.ws-rich-block-view > :last-child {
  margin-bottom: 0;
}
/* The panel gives every table in a card a bottom margin (important). */
.card .ws-table-wrap .table.ws-table,
.card .ws-calc-block .table.ws-table {
  margin-bottom: 0 !important;
}
.ws-rich-block-shown > .ws-rich-block-remove {
  position: absolute;
  top: .3rem;
  right: .3rem;
}
@media (hover: hover) {
  .ws-rich-block-shown > .ws-rich-block-remove {
    opacity: 0;
    transition: opacity .15s ease;
  }
  .ws-rich-block-shown:hover > .ws-rich-block-remove {
    opacity: 1;
  }
}

/* The bar over a selection, and the link tip. */
.ws-format-bar,
.ws-link-tip {
  position: fixed;
  z-index: 1096;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-body-bg);
  animation: wsCtxIn .1s ease-out;
}
.ws-format-bar {
  display: flex;
  gap: .1rem;
  padding: .2rem;
}
.ws-format-tool {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: var(--ws-radius-xs);
  background: transparent;
  color: var(--bs-body-color);
  font-size: 1rem;
}
.ws-format-tool:hover {
  background: var(--bs-secondary-bg);
}
.ws-format-tool.active {
  background: rgba(var(--ws-accent-rgb), .15);
  color: rgba(var(--ws-accent-rgb), 1);
}
.ws-link-tip {
  width: min(24rem, calc(100vw - 16px));
  padding: .35rem;
}
.ws-link-tip .form-control {
  min-width: 0;
}
.ws-link-tip .btn {
  flex-shrink: 0;
}

/* The design windows: table, checklist, code. */
.ws-table-tools {
  position: sticky;
  top: calc(-1 * var(--bs-modal-padding, 1rem));
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .15rem;
  margin: calc(-1 * var(--bs-modal-padding, 1rem)) calc(-1 * var(--bs-modal-padding, 1rem)) .6rem;
  padding: .45rem var(--bs-modal-padding, 1rem);
  border-bottom: 1px solid var(--bs-border-color);
  background: var(--bs-modal-bg, var(--bs-body-bg));
}
/* Under the title field the tools start in the flow, not over it. */
.ws-block-title-field + .ws-table-tools {
  margin-top: 0;
}
.ws-table-tools-gap {
  align-self: stretch;
  width: 1px;
  margin: .2rem .35rem;
  background: var(--bs-border-color);
}
.ws-tool-danger:hover:not(:disabled) {
  color: var(--bs-danger) !important;
}
.ws-table-edit-wrap {
  max-width: 100%;
  overflow: auto;
}
.table.ws-table-edit {
  width: auto;
  min-width: 100%;
  margin: 0;
  border: 1px solid var(--bs-border-color);
  font-size: .88rem;
}
.table.ws-table-edit > :not(caption) > * > * {
  min-width: 8rem;
  max-width: 24rem;
  padding: 0;
  vertical-align: top;
}
.ws-cell-edit {
  min-height: 2.1rem;
  padding: .4rem .6rem;
  outline: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.ws-table-edit .ws-cell-edit:focus {
  background: var(--bs-body-bg);
  box-shadow: inset 0 0 0 2px rgba(var(--ws-accent-rgb), .55);
}
.ws-table-edit thead .ws-cell-edit {
  font-weight: 600;
}
.ws-check-edit {
  display: flex;
  flex-direction: column;
  gap: .1rem;
}
.ws-check-edit-row {
  display: flex;
  align-items: flex-start;
  gap: .35rem;
  padding: .1rem .25rem;
  border-radius: var(--ws-radius-sm);
}
.ws-check-edit-row:focus-within {
  background: var(--bs-tertiary-bg);
}
.ws-check-edit-row > .form-check-input {
  flex-shrink: 0;
  margin: .6rem .15rem 0;
}
.ws-check-edit-row > .ws-cell-edit {
  flex: 1 1 auto;
  min-width: 0;
}
.ws-check-edit-row > .btn {
  flex-shrink: 0;
  padding: .3rem .45rem;
  opacity: .45;
}
.ws-check-edit-row:hover > .btn,
.ws-check-edit-row:focus-within > .btn {
  opacity: 1;
}
.ws-code-language {
  max-width: 12rem;
}
.ws-code-edit {
  min-height: 16rem;
  font-family: var(--bs-font-monospace);
  font-size: .85rem;
  line-height: 1.5;
  white-space: pre;
  overflow: auto;
  tab-size: 4;
  resize: vertical;
}

/* A block of code in a message: its own box, scrolled inside rather than
   stretching the conversation. */
.ws-code {
  max-width: 100%;
  margin: .25rem 0 .35rem;
  overflow: hidden;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
}
.ws-code-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .15rem .25rem .15rem .7rem;
  border-bottom: 1px solid var(--bs-border-color);
  font-size: .72rem;
  color: var(--bs-secondary-color);
}
.ws-code-copy {
  border: 0;
  border-radius: var(--ws-radius-xs);
  background: transparent;
  color: inherit;
  padding: .2rem .4rem;
  line-height: 1;
}
.ws-code-copy:hover {
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
}
.ws-code-copy.copied {
  color: var(--bs-success);
}
.ws-code pre {
  max-height: 18rem;
  margin: 0;
  padding: .55rem .75rem;
  overflow: auto;
  font-size: .8rem;
  line-height: 1.5;
  white-space: pre;
  overflow-wrap: normal;
  tab-size: 4;
}
.ws-msg-body .ws-code code {
  padding: 0;
  border-radius: 0;
  background: transparent;
  font-size: inherit;
  white-space: inherit;
}

/* 16px keeps iOS from zooming in on the field when it is tapped. */
@media (pointer: coarse) {
  .ws-composer .ws-rich {
    font-size: 16px;
  }
}
@media (max-width: 767.98px) {
  .ws-composer .ws-rich {
    min-height: 2.9rem;
  }
  .ws-rich-block-text > span {
    max-width: 12rem;
  }
}
.ws-reply-bar {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .8rem;
  color: var(--bs-secondary-color);
  padding: 0 .25rem .3rem;
}
.ws-reply-bar span {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A channel shared outside the team (guests.php): the people in the
   manage drawer, and the two ways it can be shared. */
.ws-bulk-reach {
  margin: .15rem 0 .25rem;
  font-size: .8rem;
}
.ws-bulk-progress {
  height: .45rem;
  margin: .4rem 0 .2rem;
}
.ws-share-row {
  padding: .6rem 0;
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
.ws-share-choice + .ws-share-choice {
  margin-top: .4rem;
}
/* Scheduled messages (scheduled_messages.php): the send button's second
   half, the line above the box that says some wait, and their cards. */
.ws-composer-tools > .ws-send-group {
  margin-left: auto;
}
.ws-send-group .ws-send-later {
  padding-left: .55rem;
  padding-right: .65rem;
  border-left: 1px solid rgba(255, 255, 255, .35);
}
.ws-sm-bar {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  margin: 0 0 .35rem;
  padding: .3rem .6rem;
  border: 1px dashed rgba(var(--ws-accent-rgb), .45);
  border-radius: var(--ws-radius-sm);
  background: var(--ws-soft);
  color: var(--bs-body-color);
  font-size: .8rem;
  text-align: left;
}
.ws-sm-bar:hover {
  border-style: solid;
}
.ws-sm-bar .bi {
  color: var(--ws-accent);
}
.ws-sm-bar-link {
  color: var(--ws-accent);
  font-weight: 600;
  white-space: nowrap;
}
.ws-sm-list {
  display: flex;
  flex-direction: column;
  gap: .6rem;
}
.ws-sm-card {
  padding: .6rem .75rem;
  border: 1px solid var(--bs-border-color);
  border-left: 3px solid var(--ws-accent);
  border-radius: var(--ws-radius);
  background: var(--bs-body-bg);
}
.ws-sm-card.ws-sm-failed {
  border-left-color: var(--bs-danger);
}
.ws-sm-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem;
  font-size: .85rem;
}
.ws-sm-icon {
  color: var(--ws-accent);
}
.ws-sm-failed .ws-sm-icon {
  color: var(--bs-danger);
}
.ws-sm-where {
  color: var(--bs-secondary-color);
  text-decoration: none;
}
.ws-sm-body {
  margin-top: .35rem;
}
.ws-sm-tools {
  display: flex;
  flex-wrap: wrap;
  gap: .15rem;
  margin-top: .4rem;
}
.ws-picker {
  position: absolute;
  left: 1rem;
  bottom: calc(100% - .25rem);
  z-index: 20;
  width: min(26rem, calc(100% - 2rem));
  max-height: 18rem;
  display: flex;
  flex-direction: column;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .15);
  overflow: hidden;
}
/* One row of kinds that scrolls sideways: there are too many to wrap. */
.ws-picker-types {
  display: flex;
  flex-wrap: nowrap;
  gap: .25rem;
  padding: .4rem;
  border-bottom: 1px solid var(--bs-border-color);
  overflow-x: auto;
  scrollbar-width: thin;
}
.ws-picker-types button {
  flex: 0 0 auto;
  white-space: nowrap;
  border: 1px solid var(--bs-border-color);
  background: transparent;
  border-radius: 999px;
  font-size: .75rem;
  padding: .05rem .55rem;
  color: var(--bs-body-color);
}
.ws-picker-types button.active {
  background: var(--bs-primary);
  border-color: var(--bs-primary);
  color: #fff;
}
.ws-picker-list {
  overflow-y: auto;
  padding: .25rem;
}
.ws-picker-item {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  border: 0;
  background: transparent;
  text-align: left;
  border-radius: var(--ws-radius-xs);
  padding: .35rem .5rem;
  color: var(--bs-body-color);
}
.ws-picker-item.active,
.ws-picker-item:hover {
  background: rgba(var(--bs-primary-rgb), .12);
}
.ws-picker-item img {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-picker-item .ws-picker-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: .88rem;
}
.ws-picker-item .ws-picker-meta {
  font-size: .75rem;
  color: var(--bs-secondary-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 45%;
}
.ws-picker-empty {
  padding: .6rem .75rem;
  font-size: .85rem;
  color: var(--bs-secondary-color);
}
.ws-preview {
  border: 1px solid rgba(var(--bs-primary-rgb), .4);
  background: rgba(var(--bs-primary-rgb), .06);
  border-radius: var(--ws-radius-sm);
  padding: .6rem .75rem;
  margin: 0 0 .5rem;
  font-size: .88rem;
}
.ws-preview dl {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: .15rem .75rem;
  margin: .35rem 0;
}
.ws-preview dt {
  font-weight: 600;
  color: var(--bs-secondary-color);
}
.ws-preview dd {
  margin: 0;
}
.ws-warning {
  display: flex;
  gap: .4rem;
  align-items: flex-start;
  font-size: .85rem;
  margin: .2rem 0;
}
.ws-warning.ws-warning-hard {
  color: var(--bs-danger-text-emphasis);
}
.ws-warning.ws-warning-warning {
  color: var(--bs-warning-text-emphasis);
}
.ws-warning.ws-warning-info {
  color: var(--bs-secondary-color);
}
.ws-ephemeral {
  border: 1px dashed var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  padding: .6rem .75rem;
  margin: .5rem 0;
  font-size: .88rem;
  background: var(--bs-tertiary-bg);
}
.ws-ephemeral-title {
  font-weight: 700;
  margin-bottom: .35rem;
}
.ws-help dt {
  margin-top: .35rem;
}
.ws-help dd {
  margin: 0 0 .2rem;
}
.ws-plan-mini th {
  padding-right: .75rem;
  font-weight: 600;
  white-space: nowrap;
}
.ws-plan-mini td {
  padding: .1rem .5rem .1rem 0;
}
.ws-load {
  display: inline-block;
  min-width: 3rem;
  text-align: center;
  font-size: .75rem;
  font-weight: 700;
  border-radius: var(--ws-radius-xs);
  padding: 0 .3rem;
}
.ws-load-ok {
  background: rgba(var(--bs-success-rgb), .15);
  color: var(--bs-success-text-emphasis);
}
.ws-load-full {
  background: rgba(var(--bs-warning-rgb), .22);
  color: var(--bs-warning-text-emphasis);
}
.ws-load-over {
  background: rgba(var(--bs-danger-rgb), .18);
  color: var(--bs-danger-text-emphasis);
}
.ws-load-away {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}
.ws-empty {
  text-align: center;
  color: var(--bs-secondary-color);
  padding: 2.5rem 1rem;
}
.ws-empty i {
  font-size: 2rem;
  display: block;
  margin-bottom: .5rem;
}
.ws-summary-view {
  max-width: 52rem;
  line-height: 1.6;
}
.ws-list-row {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .55rem .75rem;
  border-bottom: 1px solid var(--bs-border-color);
  cursor: pointer;
}
.ws-list-row:hover {
  background: var(--bs-tertiary-bg);
}
.ws-list-row .ws-grow {
  flex: 1 1 auto;
  min-width: 0;
}
.ws-list-row .ws-row-title {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-list-row .ws-row-sub {
  font-size: .8rem;
  color: var(--bs-secondary-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-list-row.ws-row-done .ws-row-title {
  text-decoration: line-through;
  color: var(--bs-secondary-color);
}
.ws-status-dot {
  width: .65rem;
  height: .65rem;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--bs-primary);
}
.ws-status-dot.ws-s-doing {
  background: var(--bs-info);
}
.ws-status-dot.ws-s-waiting {
  background: var(--bs-warning);
}
.ws-status-dot.ws-s-done {
  background: var(--bs-success);
}
.ws-status-dot.ws-s-cancelled {
  background: var(--bs-secondary);
}
.ws-people-select {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  max-height: 12rem;
  overflow-y: auto;
  padding: .25rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
}
.ws-person-toggle {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  border: 1px solid var(--bs-border-color);
  background: transparent;
  border-radius: 999px;
  padding: .1rem .55rem .1rem .15rem;
  font-size: .82rem;
  color: var(--bs-body-color);
}
.ws-person-toggle img {
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-person-toggle.active {
  border-color: var(--ws-accent);
  background: rgba(var(--ws-accent-rgb), .14);
  font-weight: 600;
}
.ws-refs-field {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  align-items: center;
}
.ws-refs-field .ws-chip button {
  border: 0;
  background: transparent;
  color: inherit;
  padding: 0 0 0 .15rem;
  line-height: 1;
}
.ws-ref-search-box {
  position: relative;
}
.ws-ref-search-box .ws-picker {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + .25rem);
  bottom: auto;
  width: auto;
}

/* The planning board: people down the side, days across. */
.ws-board-wrap {
  overflow-x: auto;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius);
  background: var(--bs-body-bg);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 10px 30px -14px rgba(0, 0, 0, .16);
}
.ws-board {
  display: grid;
  min-width: 56rem;
}
.ws-board-cell,
.ws-board-head,
.ws-board-person {
  border-right: 1px solid var(--bs-border-color);
  border-bottom: 1px solid var(--bs-border-color);
  min-width: 0;
}
.ws-board-head {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bs-tertiary-bg);
  font-size: .8rem;
  font-weight: 700;
  padding: .45rem .5rem;
  text-align: center;
}
/* The corner above the names stays put both ways. */
.ws-board > .ws-board-head:first-child {
  left: 0;
  z-index: 3;
}
.ws-board-head.ws-today {
  color: var(--ws-accent);
  box-shadow: inset 0 -2px 0 var(--ws-accent);
}
.ws-board-cell:hover {
  background: var(--ws-soft);
}
.ws-board-head.ws-weekend,
.ws-board-cell.ws-weekend {
  background: var(--bs-tertiary-bg);
}
.ws-board-person {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--bs-body-bg);
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .45rem .6rem;
  font-size: .85rem;
}
.ws-board-person img {
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-board-person .ws-grow {
  min-width: 0;
}
.ws-board-person b {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-board-person small {
  color: var(--bs-secondary-color);
}
.ws-board-cell {
  position: relative;
  padding: .3rem .3rem 1.4rem;
  min-height: 5rem;
}
.ws-board-cell.ws-drop {
  background: rgba(var(--bs-primary-rgb), .1);
}
.ws-board-cell.ws-cell-away {
  background: repeating-linear-gradient(135deg, var(--bs-tertiary-bg), var(--bs-tertiary-bg) 6px, var(--bs-body-bg) 6px, var(--bs-body-bg) 12px);
}
.ws-board-bar {
  position: absolute;
  left: .3rem;
  right: .3rem;
  bottom: .35rem;
  height: .5rem;
  border-radius: 999px;
  background: var(--bs-secondary-bg);
  overflow: hidden;
}
.ws-board-bar span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--bs-success);
}
.ws-board-bar.ws-bar-full span {
  background: var(--bs-warning);
}
.ws-board-bar.ws-bar-over span {
  background: var(--bs-danger);
}
.ws-board-pct {
  position: absolute;
  right: .35rem;
  bottom: .9rem;
  font-size: .68rem;
  color: var(--bs-secondary-color);
}
.ws-board-item {
  display: block;
  width: 100%;
  border: 0;
  text-align: left;
  font-size: .75rem;
  line-height: 1.3;
  border-radius: var(--ws-radius-xs);
  padding: .15rem .35rem;
  margin-bottom: .2rem;
  background: rgba(var(--bs-primary-rgb), .12);
  color: var(--bs-body-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: grab;
}
.ws-board-item.ws-item-due {
  font-weight: 700;
}
.ws-board-item.ws-item-urgent {
  box-shadow: inset 3px 0 0 var(--bs-danger);
}
.ws-board-item.ws-item-event {
  background: rgba(var(--bs-info-rgb), .15);
  cursor: pointer;
}
.ws-board-item.ws-item-leave {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
  cursor: pointer;
}
/* A coming copy of a repeating task: where it will run, not handed out yet. */
.ws-board-item.ws-item-upcoming {
  background: transparent;
  border: 1px dashed rgba(var(--bs-primary-rgb), .45);
  color: var(--bs-secondary-color);
  font-weight: 400;
  cursor: pointer;
}
.ws-board-item.ws-item-upcoming.ws-item-due {
  font-weight: 600;
}
/* A coming copy waiting for an overdue one: it comes only if that is done. */
.ws-board-item.ws-item-upcoming.ws-item-waits {
  border-color: rgba(var(--bs-warning-rgb), .75);
  opacity: .75;
}
.ws-board-add {
  position: absolute;
  top: .15rem;
  right: .15rem;
  border: 0;
  background: transparent;
  color: var(--bs-secondary-color);
  opacity: 0;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: var(--ws-radius-xs);
}
.ws-board-cell:hover .ws-board-add,
.ws-board-add:focus {
  opacity: 1;
}
.ws-board-add:hover {
  background: var(--bs-secondary-bg);
}
/* The plan board's legend dots. Not .ws-swatch: that is the colour picker's
   button (above), and a second rule under the same name overrode its size,
   shape and centring. */
.ws-legend-swatch {
  display: inline-block;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 999px;
  line-height: 1;
}
.ws-swatch-ok {
  background: var(--bs-success);
}
.ws-swatch-full {
  background: var(--bs-warning);
}
.ws-swatch-over {
  background: var(--bs-danger);
}
.ws-swatch-away {
  background: repeating-linear-gradient(135deg, var(--bs-secondary-bg), var(--bs-secondary-bg) 3px, var(--bs-body-bg) 3px, var(--bs-body-bg) 6px);
  border: 1px solid var(--bs-border-color);
}
.ws-conflict {
  display: flex;
  gap: .5rem;
  align-items: flex-start;
  padding: .4rem 0;
  border-bottom: 1px solid var(--bs-border-color);
  font-size: .85rem;
}

/* The Workspace button and drawer on other screens. */
.ws-record-drawer .ws-record-section {
  margin-bottom: 1.25rem;
}
.ws-record-drawer .ws-record-title {
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  margin-bottom: .4rem;
}
.ws-record-item {
  display: block;
  padding: .45rem .5rem;
  border-radius: var(--ws-radius-xs);
  text-decoration: none;
  color: var(--bs-body-color);
  font-size: .85rem;
}
.ws-record-item:hover {
  background: var(--bs-tertiary-bg);
}
.ws-record-item small {
  display: block;
  color: var(--bs-secondary-color);
}
/* The record's fields, edited in the drawer (quick_edit.php). */
.ws-qe-glance {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem .75rem;
}
.ws-qe-glance-item {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.ws-qe-form .ws-sa-field {
  margin-bottom: .5rem;
}
.ws-qe-form .ws-sa-field .form-label {
  margin-bottom: .15rem;
}
.ws-side-card .card-body {
  padding: .75rem;
}


/* ── Files: pictures, video, sound, PDF, and the rest ── */
.ws-image-link {
  display: inline-block;
  position: relative;
  cursor: zoom-in;
  text-decoration: none;
  border-radius: var(--ws-radius-sm);
}
.ws-image-link .ws-image-name {
  display: block;
  font-size: .72rem;
  color: var(--bs-secondary-color);
  margin-top: .2rem;
  max-width: 26rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-image-link:hover img {
  filter: brightness(.95);
}
.ws-media {
  max-width: min(28rem, 100%);
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius-sm);
  overflow: hidden;
  background: var(--bs-tertiary-bg);
}
.ws-media video {
  display: block;
  width: 100%;
  max-height: 18rem;
  background: #000;
}
.ws-media audio {
  display: block;
  width: 100%;
  padding: .4rem .5rem 0;
}
.ws-media-bar {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .25rem .5rem;
  font-size: .78rem;
  color: var(--bs-secondary-color);
}
.ws-file-card {
  display: flex;
  align-items: center;
  gap: .6rem;
  max-width: min(28rem, 100%);
  padding: .5rem .6rem;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
}
.ws-file-card .ws-file-name {
  display: block;
  font-size: .85rem;
  font-weight: 600;
  color: var(--bs-body-color);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-file-card .ws-file-ext,
.ws-file-row .ws-file-ext {
  min-width: 2.4rem;
  text-align: center;
  padding: .45rem .3rem;
  border-radius: var(--ws-radius-xs);
  background: rgba(var(--ws-accent-rgb), .14);
  color: var(--ws-accent);
}
.ws-file-card .ws-ext-pdf {
  background: rgba(var(--bs-danger-rgb), .14);
  color: var(--bs-danger-text-emphasis);
}
.ws-section-title {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  margin: .5rem 0 .5rem;
}
.ws-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  gap: .6rem;
  margin-bottom: 1.25rem;
}
.ws-gallery-tile {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  padding: 0;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius-sm);
  overflow: hidden;
  background: var(--bs-tertiary-bg);
  cursor: zoom-in;
}
.ws-gallery-tile img,
.ws-gallery-tile video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .25s ease;
}
.ws-gallery-tile:hover img,
.ws-gallery-tile:hover video {
  transform: scale(1.04);
}
.ws-gallery-video {
  cursor: pointer;
}
.ws-gallery-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.2rem;
  color: rgba(255, 255, 255, .92);
  text-shadow: 0 2px 10px rgba(0, 0, 0, .45);
}
.ws-gallery-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  padding: 1.2rem .5rem .35rem;
  font-size: .7rem;
  color: #fff;
  text-align: left;
  background: linear-gradient(transparent, rgba(0, 0, 0, .62));
}
.ws-gallery-caption b,
.ws-gallery-caption span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-file-list {
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius-sm);
  overflow: hidden;
}
.ws-file-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .45rem .6rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
.ws-file-row:last-child {
  border-bottom: 0;
}
.ws-file-row .ws-file-link {
  border: 0;
  padding: 0;
  min-width: 0;
  max-width: 55%;
}
.ws-file-row audio {
  height: 2rem;
  max-width: 16rem;
}
.ws-preview-modal .modal-body {
  background: #111;
  min-height: 60vh;
}
.ws-preview-frame {
  display: block;
  width: 100%;
  height: 78vh;
  border: 0;
  background: #fff;
}
.ws-preview-video {
  display: block;
  width: 100%;
  max-height: 80vh;
  background: #000;
}
.ws-preview-office {
  height: 78vh;
  background: var(--bs-body-bg);
}

/* ── Word and Excel previews (assets/js/pg_office_view.js) ──
   One look in the file manager's window and Quick Look and in the
   Workspace's preview: a workbook as tabs over a grid, a document as a
   page of text. */
.office-view {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  font-size: .85rem;
  color: var(--bs-body-color);
}
.office-view .empty-note {
  margin: auto;
}
.office-book {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}
.office-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  padding: .4rem .5rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
  background: var(--bs-tertiary-bg);
}
.office-tab {
  border: 1px solid transparent;
  border-radius: 999px;
  padding: .15rem .7rem;
  font: inherit;
  font-size: .8rem;
  color: var(--bs-secondary-color);
  background: transparent;
}
.office-tab.active {
  color: var(--bs-body-color);
  background: var(--bs-body-bg);
  border-color: var(--bs-border-color);
}
.office-tab-hidden {
  font-style: italic;
  opacity: .7;
}
.office-pane {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  background: var(--bs-body-bg);
}
.office-sheet {
  border-collapse: separate;
  border-spacing: 0;
  table-layout: auto;
  min-width: 100%;
}
.office-sheet th,
.office-sheet td {
  border-right: 1px solid var(--bs-border-color-translucent);
  border-bottom: 1px solid var(--bs-border-color-translucent);
  padding: .15rem .45rem;
  white-space: pre;
  max-width: 24rem;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: top;
}
.office-sheet thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  text-align: center;
  font-weight: 600;
  color: var(--bs-secondary-color);
  background: var(--bs-tertiary-bg);
}
.office-sheet tbody th {
  position: sticky;
  left: 0;
  z-index: 1;
  text-align: right;
  font-weight: 500;
  color: var(--bs-secondary-color);
  background: var(--bs-tertiary-bg);
}
.office-sheet .office-corner {
  left: 0;
  z-index: 3;
}
.office-sheet .office-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* A formula whose result the file does not hold: worked out by the
   spreadsheet program that opens it (includes/fn/office.php). */
.office-sheet td.office-calc::before {
  content: "\0192x";
  font-style: italic;
  color: var(--bs-secondary-color);
  opacity: .7;
}
/* A workbook offered for editing (pg_office_view.js). */
.office-sheet[role="grid"] td {
  cursor: cell;
}
.office-sheet td:focus {
  outline: none;
}
.office-sheet td.office-selected {
  box-shadow: inset 0 0 0 2px var(--bs-primary);
}
.office-sheet td.office-edited {
  background: var(--bs-warning-bg-subtle);
}
.office-sheet[role="grid"] td.office-locked {
  cursor: default;
  color: var(--bs-secondary-color);
}
.office-sheet td.office-editing {
  padding: 0;
  overflow: visible;
  position: relative;
}
.office-editor {
  display: block;
  width: 100%;
  min-width: 10rem;
  margin: 0;
  padding: .15rem .45rem;
  border: 0;
  outline: 2px solid var(--bs-primary);
  outline-offset: -2px;
  font: inherit;
  line-height: inherit;
  white-space: pre;
  resize: none;
  overflow: hidden;
  color: var(--bs-body-color);
  background: var(--bs-body-bg);
}
.office-status:empty {
  display: none;
}
.office-tab-edited::after {
  content: "";
  display: inline-block;
  width: .4rem;
  height: .4rem;
  margin-left: .35rem;
  border-radius: 50%;
  vertical-align: middle;
  background: var(--bs-warning);
}
.office-doc {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 1.25rem 1.5rem;
  line-height: 1.5;
  background: var(--bs-body-bg);
}
.office-doc h1, .office-doc h2, .office-doc h3, .office-doc h4, .office-doc h5, .office-doc h6 {
  margin: 1rem 0 .4rem;
  font-weight: 600;
}
.office-doc h1 { font-size: 1.5rem; }
.office-doc h2 { font-size: 1.25rem; }
.office-doc h3 { font-size: 1.1rem; }
.office-doc p {
  margin: 0 0 .6rem;
}
.office-doc blockquote {
  margin: .6rem 0;
  padding-left: .9rem;
  border-left: 3px solid var(--bs-border-color);
  color: var(--bs-secondary-color);
}
.office-doc .office-list {
  margin: 0 0 .6rem;
  padding-left: 1.6rem;
}
.office-doc .office-lvl-1 { margin-left: 1.2rem; }
.office-doc .office-lvl-2 { margin-left: 2.4rem; }
.office-doc .office-lvl-3 { margin-left: 3.6rem; }
.office-doc .office-lvl-4, .office-doc .office-lvl-5, .office-doc .office-lvl-6, .office-doc .office-lvl-7, .office-doc .office-lvl-8 { margin-left: 4.8rem; }
.office-doc .office-table {
  border-collapse: collapse;
  margin: .6rem 0;
  max-width: 100%;
}
.office-doc .office-table td {
  border: 1px solid var(--bs-border-color);
  padding: .25rem .5rem;
  vertical-align: top;
}
.office-doc .office-table td p:last-child {
  margin-bottom: 0;
}
.office-doc .office-img {
  max-width: 100%;
  height: auto;
}
.office-doc .office-tabchar {
  display: inline-block;
  min-width: 2em;
}
.office-doc .office-page {
  margin: 1.25rem 0;
  border: 0;
  border-top: 1px dashed var(--bs-border-color);
}
.office-doc .office-lost {
  color: var(--bs-secondary-color);
  font-style: italic;
}
/* A document offered for editing (pg_office_view.js): a paragraph is
   picked, then typed in a box that takes the place of its text. */
.office-doc [data-p] {
  border-radius: .2rem;
}
.office-doc [data-p]:focus {
  outline: none;
}
.office-doc-edit .office-para {
  cursor: text;
}
.office-doc-edit .office-para:hover {
  background: var(--bs-tertiary-bg);
}
.office-doc .office-selected {
  box-shadow: 0 0 0 2px var(--bs-primary);
}
.office-doc .office-edited,
.office-doc-edit .office-edited:hover {
  background: var(--bs-warning-bg-subtle);
}
.office-doc-edit .office-locked {
  cursor: default;
}
.office-doc .office-para-editor {
  min-width: 0;
  padding: 0;
  outline-offset: 2px;
  text-align: inherit;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  tab-size: 4;
}
.office-note {
  padding: .5rem .75rem;
  font-size: .8rem;
  color: var(--bs-secondary-color);
  border-top: 1px solid var(--bs-border-color-translucent);
}

/* Files dragged over a conversation. */
.ws-center {
  position: relative;
}
.ws-drop-overlay {
  position: absolute;
  inset: .5rem;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--ws-accent);
  border-radius: var(--ws-radius);
  background: rgba(var(--ws-accent-rgb), .1);
  backdrop-filter: blur(2px);
  pointer-events: none;
}
.ws-drop-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .25rem;
  text-align: center;
  color: var(--bs-emphasis-color);
}
.ws-drop-inner .bi {
  font-size: 2.4rem;
  color: var(--ws-accent);
}
.ws-drop-inner small {
  color: var(--bs-secondary-color);
  max-width: 22rem;
}

/* The channel's greeting. */
.ws-brief {
  position: relative;
  display: flex;
  gap: .8rem;
  align-items: flex-start;
  margin: .6rem 1rem 0;
  padding: .7rem 2.4rem .7rem .8rem;
  border: 1px solid rgba(var(--ws-accent-rgb), .22);
  border-radius: var(--ws-radius);
  background: linear-gradient(120deg, rgba(var(--ws-accent-rgb), .12), rgba(var(--ws-accent-rgb), .03) 60%, transparent);
}
.ws-brief-icon {
  flex: 0 0 auto;
  width: 2.3rem;
  height: 2.3rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.15rem;
  color: #fff;
  background: linear-gradient(135deg, var(--ws-accent), rgba(var(--ws-accent-rgb), .65));
  box-shadow: 0 4px 12px -4px rgba(var(--ws-accent-rgb), .7);
}
.ws-brief-greeting {
  font-weight: 700;
  color: var(--bs-emphasis-color);
}
.ws-brief-line {
  font-size: .88rem;
  color: var(--bs-body-color);
  line-height: 1.45;
}
.ws-brief-hint {
  font-size: .8rem;
  color: var(--bs-secondary-color);
  margin-top: .2rem;
}
.ws-brief-action {
  margin-top: .45rem;
  border-radius: 999px;
  padding: .15rem .7rem;
  font-size: .8rem;
  color: var(--ws-accent);
  border: 1px solid rgba(var(--ws-accent-rgb), .45);
  background: var(--bs-body-bg);
}
.ws-brief-action:hover {
  background: rgba(var(--ws-accent-rgb), .12);
  color: var(--ws-accent);
}
.ws-brief-close {
  position: absolute;
  top: .55rem;
  right: .6rem;
  font-size: .65rem;
}
.ws-brief-buttons {
  display: flex;
  flex-wrap: wrap;
  column-gap: .4rem;
}
.ws-brief-buttons:empty,
.ws-brief-recap:empty {
  display: none;
}
/* Pinegrap AI's summary of what was missed. */
.ws-brief-recap {
  margin-top: .45rem;
  padding-left: .65rem;
  border-left: 2px solid rgba(var(--ws-accent-rgb), .45);
}
.ws-brief-recap-text {
  font-size: .88rem;
  line-height: 1.5;
  white-space: pre-line;
  color: var(--bs-body-color);
}
.ws-brief-recap-by {
  font-size: .72rem;
  color: var(--bs-secondary-color);
  margin-top: .15rem;
}

/* Context menus: the panel's own look, placed at the pointer. */
.ws-ctx.contextmenu.popover {
  position: fixed;
  z-index: 1095;
  max-width: 19rem;
  min-width: 12rem;
  border-radius: var(--ws-radius-sm);
  animation: wsCtxIn .1s ease-out;
}
@keyframes wsCtxIn {
  from { opacity: 0; transform: translateY(-3px); }
  to { opacity: 1; transform: none; }
}
.ws-ctx .ws-ctx-item {
  display: flex;
  align-items: center;
  width: 100%;
  padding: .32rem .6rem;
  font-size: .86rem;
  border-radius: var(--ws-radius-xs);
}
.ws-ctx .ws-ctx-item:hover,
.ws-ctx .ws-ctx-item:focus {
  background: rgba(var(--ws-accent-rgb, 94, 53, 177), .12);
}
.ws-ctx .ws-ctx-item .bi {
  width: 1.1rem;
  opacity: .8;
}
.ws-ctx .ws-ctx-danger {
  color: var(--bs-danger-text-emphasis) !important;
}
.ws-ctx .ws-ctx-checked {
  font-weight: 600;
}
.ws-ctx-header {
  padding: .3rem .6rem .35rem;
  font-size: .72rem;
  font-weight: 700;
  color: var(--bs-secondary-color);
  max-width: 18rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  border-bottom: 1px solid var(--bs-border-color-translucent);
  margin-bottom: .2rem;
}
.ws-tool-danger:hover {
  color: var(--bs-danger) !important;
}

/* The month calendar. */
.ws-cal-toolbar .ws-cal-title {
  font-size: 1.15rem;
  font-weight: 700;
  margin: 0 .5rem;
  letter-spacing: -.01em;
  text-transform: capitalize;
}
.ws-cal {
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius);
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 10px 30px -14px rgba(0, 0, 0, .16);
}
.ws-cal-head,
.ws-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
}
.ws-cal-head > div {
  padding: .45rem .6rem;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  background: var(--bs-tertiary-bg);
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
.ws-cal-day {
  min-height: 8.2rem;
  padding: .35rem .4rem .45rem;
  border-right: 1px solid var(--bs-border-color-translucent);
  border-bottom: 1px solid var(--bs-border-color-translucent);
  cursor: pointer;
  min-width: 0;
  transition: background .12s ease;
}
.ws-cal-day:nth-child(7n) {
  border-right: 0;
}
.ws-cal-day:hover {
  background: var(--ws-soft);
}
.ws-cal-day.ws-cal-weekend {
  background: rgba(var(--bs-emphasis-color-rgb), .02);
}
.ws-cal-day.ws-cal-out {
  opacity: .45;
}
.ws-cal-day-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: .25rem;
}
.ws-cal-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.7rem;
  height: 1.7rem;
  border-radius: 999px;
  font-size: .82rem;
  font-weight: 700;
}
.ws-cal-today .ws-cal-num {
  background: var(--ws-accent);
  color: #fff;
}
.ws-cal-talk {
  font-size: .7rem;
  color: var(--bs-secondary-color);
  padding: 0 .35rem;
  border-radius: 999px;
  background: var(--bs-tertiary-bg);
}
.ws-cal-task,
.ws-cal-event,
.ws-cal-channel {
  display: flex;
  align-items: center;
  gap: .3rem;
  width: 100%;
  margin-bottom: .18rem;
  padding: .12rem .35rem;
  border: 0;
  border-radius: var(--ws-radius-xs);
  font-size: .72rem;
  line-height: 1.3;
  text-align: left;
  text-decoration: none;
  overflow: hidden;
  white-space: nowrap;
}
.ws-cal-task {
  background: rgba(var(--ws-accent-rgb), .1);
  color: var(--bs-body-color);
}
.ws-cal-task:hover {
  background: rgba(var(--ws-accent-rgb), .2);
}
.ws-cal-task .ws-status-dot {
  width: .5rem;
  height: .5rem;
}
.ws-cal-task-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ws-cal-task.ws-cal-late {
  background: rgba(var(--bs-danger-rgb), .12);
  color: var(--bs-danger-text-emphasis);
}
.ws-cal-task.ws-cal-hot {
  box-shadow: inset 2px 0 0 var(--bs-danger);
}
.ws-cal-task.ws-cal-closed .ws-cal-task-title {
  text-decoration: line-through;
  opacity: .7;
}
.ws-cal-task.ws-cal-upcoming {
  background: transparent;
  box-shadow: inset 0 0 0 1px rgba(var(--ws-accent-rgb), .4);
  color: var(--bs-secondary-color);
}
.ws-cal-task.ws-cal-upcoming:hover {
  background: rgba(var(--ws-accent-rgb), .08);
}
.ws-cal-task.ws-cal-upcoming .bi {
  font-size: .7rem;
}
.ws-cal-task.ws-cal-upcoming.ws-cal-waits {
  box-shadow: inset 0 0 0 1px rgba(var(--bs-warning-rgb), .75);
  opacity: .75;
}
/* A task that runs across days: one piece a day, the pieces joined over the
   cell padding and border into one bar; an empty gap keeps the lane's height
   on a day it has nothing. */
.ws-cal-day {
  --ws-cal-pad: .4rem;
}
.ws-cal-task.ws-cal-span,
.ws-cal-lane-gap {
  height: 1.3rem;
}
.ws-cal-lane-gap {
  margin-bottom: .18rem;
}
.ws-cal-task.ws-cal-span-l {
  width: calc(100% + var(--ws-cal-pad));
  margin-left: calc(-1 * var(--ws-cal-pad));
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  box-shadow: none;
}
.ws-cal-task.ws-cal-span-r {
  width: calc(100% + var(--ws-cal-pad) + 1px);
  margin-right: calc(-1 * var(--ws-cal-pad) - 1px);
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.ws-cal-task.ws-cal-span-l.ws-cal-span-r {
  width: calc(100% + 2 * var(--ws-cal-pad) + 1px);
}
.ws-list-row.ws-row-upcoming .ws-row-title {
  color: var(--bs-secondary-color);
}

/* The dates and the repeat of a task, one box in the task drawer. */
.ws-when {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  padding: .6rem .75rem .55rem;
  background: var(--bs-tertiary-bg);
}
.ws-when-head {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin-bottom: .45rem;
  font-size: .8rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
}
.ws-when .form-label {
  margin-bottom: .2rem;
  font-size: .8rem;
}
.ws-when-custom,
.ws-when-end {
  margin-top: .5rem;
}
.ws-when-summary {
  margin-top: .55rem;
  padding: .45rem .6rem;
  border-radius: var(--ws-radius-xs);
  background: rgba(var(--ws-accent-rgb), .08);
  font-size: .8rem;
}
.ws-when-rule {
  font-weight: 600;
}
.ws-when-span {
  color: var(--bs-secondary-color);
}
.ws-when-next {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .3rem;
  margin-top: .3rem;
}
.ws-when-date {
  padding: .05rem .4rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 999px;
  background: var(--bs-body-bg);
  white-space: nowrap;
}
.ws-when-warn {
  margin-top: .35rem;
  color: var(--bs-warning-text-emphasis);
}
.ws-when-dayoff {
  margin: -.25rem 0 .5rem;
  font-size: .8rem;
}
.ws-when-dayoff .ws-when-warn {
  margin-top: 0;
}
.ws-when-date.ws-when-moved {
  border-style: dashed;
}
.ws-when-date .bi {
  margin-right: .1rem;
}

/* The working calendar card of the settings screen. */
.ws-feed-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .45rem 0;
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
.ws-next-off li {
  padding: .15rem 0;
}
.ws-feed-days {
  padding: .35rem 0 .45rem 1.6rem;
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
.ws-feed-days summary {
  cursor: pointer;
  color: var(--bs-secondary-color);
}
.ws-feed-days ul {
  max-height: 16rem;
  overflow-y: auto;
}
.ws-feed-day {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  padding: .15rem 0;
}
.ws-feed-day-off .ws-grow {
  color: var(--bs-secondary-color);
  text-decoration: line-through;
}
.ws-when-state {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  margin-top: .5rem;
  font-size: .8rem;
}
.ws-avatars-sm img {
  width: var(--ws-avatar-xs);
  height: var(--ws-avatar-xs);
  border-width: 1px;
  margin-left: -.3rem;
}
.ws-cal-event {
  background: rgba(var(--bs-info-rgb), .14);
  color: var(--bs-info-text-emphasis);
}
.ws-cal-event.ws-kind-leave,
.ws-cal-event.ws-kind-holiday {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}
.ws-cal-event.ws-kind-visit {
  background: rgba(var(--bs-warning-rgb), .16);
  color: var(--bs-warning-text-emphasis);
}
.ws-cal-event-dot {
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--bs-info);
}
.ws-cal-event-dot.ws-kind-leave,
.ws-cal-event-dot.ws-kind-holiday {
  background: var(--bs-secondary);
}
.ws-cal-event-dot.ws-kind-visit {
  background: var(--bs-warning);
}
.ws-cal-channel {
  color: var(--ws-accent);
  background: transparent;
  padding-left: .2rem;
}
.ws-cal-channel:hover {
  background: var(--ws-soft);
}
.ws-cal-more {
  font-size: .7rem;
  color: var(--bs-secondary-color);
  padding-left: .35rem;
}
.ws-cal-side .card {
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius);
}
.ws-side-card-title {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  margin-bottom: .6rem;
}
.ws-figures {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .5rem;
}
.ws-figure {
  display: flex;
  flex-direction: column;
  padding: .45rem .5rem;
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
}
.ws-figure b {
  font-size: 1.25rem;
  line-height: 1.1;
}
.ws-figure span {
  font-size: .7rem;
  color: var(--bs-secondary-color);
}
.ws-figure.ws-fig-ok b {
  color: var(--bs-success-text-emphasis);
}
.ws-figure.ws-fig-bad b {
  color: var(--bs-danger-text-emphasis);
}
.ws-extreme {
  display: flex;
  align-items: center;
  gap: .55rem;
  width: 100%;
  padding: .45rem .5rem;
  margin-bottom: .4rem;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius-sm);
  background: transparent;
  text-align: left;
  color: var(--bs-body-color);
}
.ws-extreme:hover {
  background: var(--ws-soft);
}
.ws-extreme img {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-extreme small {
  display: block;
  font-size: .7rem;
  color: var(--bs-secondary-color);
}
.ws-extreme b {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-extreme-icon {
  font-size: 1.1rem;
}
.ws-extreme-most .ws-extreme-icon {
  color: var(--bs-danger);
}
.ws-extreme-least .ws-extreme-icon {
  color: var(--bs-success);
}
.ws-extreme-count {
  font-size: .78rem;
  font-weight: 700;
  white-space: nowrap;
}
.ws-person-row {
  display: flex;
  align-items: center;
  gap: .55rem;
  width: 100%;
  padding: .4rem .35rem;
  border: 0;
  border-radius: var(--ws-radius-sm);
  background: transparent;
  text-align: left;
  color: var(--bs-body-color);
  font-size: .84rem;
}
.ws-person-row:hover,
.ws-person-row.active {
  background: var(--ws-soft);
}
.ws-person-row.active {
  box-shadow: inset 3px 0 0 var(--ws-accent);
}
.ws-person-row img {
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-meter {
  display: flex;
  height: .35rem;
  margin: .2rem 0 .15rem;
  border-radius: 999px;
  background: var(--bs-secondary-bg);
  overflow: hidden;
}
.ws-meter-done {
  background: var(--bs-success);
}
.ws-meter-open {
  background: var(--ws-accent);
}

/* The settings screen. */
.ws-grow {
  flex: 1 1 auto;
  min-width: 0;
}
.ws-dept-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .5rem 0;
  border-bottom: 1px solid var(--bs-border-color);
}
.ws-dept-swatch {
  flex: 0 0 auto;
  width: .85rem;
  height: .85rem;
  border-radius: 50%;
}
.ws-dept-form {
  padding: .75rem;
  margin: .5rem 0 1rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-tertiary-bg);
}
.ws-dept-people {
  max-height: 18rem;
  overflow-y: auto;
}
/* An MCP server's tools: a code host lists a hundred of them. */
.ws-mcp-tools {
  max-height: 28rem;
  overflow-y: auto;
}
.ws-days .btn {
  padding: .1rem .35rem;
  font-size: .72rem;
}
/* The team table runs to the card's edges: its outer cells get the card's
   side padding, and the job title keeps a usable width when the table
   scrolls sideways on a narrow screen. */
#ws-people .table > :not(caption) > * > :first-child {
  padding-left: var(--bs-card-spacer-x);
}
#ws-people .table > :not(caption) > * > :last-child {
  padding-right: var(--bs-card-spacer-x);
}
#ws-people input[name$="[title]"] {
  min-width: 9rem;
}
.ws-task-card-hidden {
  color: var(--bs-secondary-color);
  font-size: .85rem;
}

/* A drawer of ours is open: the chat launcher sits over its footer. */
body.ws-drawer-open #pg-chat-root .pg-chat-launcher {
  display: none;
}

@media (max-width: 767.98px) {
  /* The channel screen fills what can be seen of the window, under the
     panel's top bar; the script keeps --ws-fit-* on the visual viewport so
     the on-screen keyboard does not push the header and tabs away. */
  .ws-app {
    position: fixed;
    top: var(--ws-fit-top, var(--pg-navbar-h, 3.125rem));
    left: 0;
    right: 0;
    z-index: 1010;
    grid-template-columns: minmax(0, 1fr);
    height: var(--ws-fit-h, calc(100vh - 3.125rem));
    min-height: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  body.workspace_channels {
    overflow: hidden;
  }
  .ws-head {
    gap: .5rem;
    padding: .4rem .6rem;
    min-height: 3rem;
  }
  .ws-head-title h2 {
    font-size: 1rem;
  }
  .ws-tabs {
    gap: 0;
    padding: .1rem .35rem 0;
  }
  .ws-tabs button {
    padding: .55rem .6rem;
    font-size: .84rem;
  }
  .ws-pane {
    padding: .5rem .65rem;
  }
  .ws-composer-wrap {
    padding: .4rem .5rem calc(.45rem + env(safe-area-inset-bottom, 0px));
  }
  /* 16px keeps iOS from zooming in on the field when it is tapped. */
  .ws-composer textarea {
    font-size: 16px;
    min-height: 2.9rem;
  }
  .ws-composer-tools .btn {
    padding: .3rem .5rem;
  }
  body.ws-keyboard .ws-center > .ws-brief {
    display: none;
  }
  body.workspace_channels #pg-chat-root .pg-chat-launcher {
    right: 12px;
    bottom: calc(var(--ws-composer-h, 6rem) + 12px);
    width: 46px;
    height: 46px;
    font-size: 18px;
  }
  body.workspace_channels.ws-keyboard #pg-chat-root .pg-chat-launcher {
    display: none;
  }
  /* Filter pills stay on one line and scroll sideways. */
  body.workspace .pg-toolbar .btn-group {
    flex-wrap: nowrap;
    max-width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
  }
  body.workspace .pg-toolbar .btn-group > .btn {
    flex: 0 0 auto;
    white-space: nowrap;
  }
  /* The board: a narrow name column, so more than one day fits. */
  .ws-board {
    min-width: 0;
    --ws-board-person: 7.25rem;
    --ws-board-day: 6.25rem;
  }
  .ws-board-person {
    gap: .35rem;
    padding: .35rem .4rem;
    font-size: .78rem;
  }
  .ws-board-person img {
    width: 1.5rem;
    height: 1.5rem;
  }
  .ws-board-person small {
    display: none;
  }
  .ws-app .ws-side {
    display: none;
  }
  .ws-app.ws-show-side .ws-side {
    display: flex;
    border-right: 0;
  }
  /* A discussion covers the channel until it is closed. */
  .ws-app.ws-thread-open {
    grid-template-columns: minmax(0, 1fr);
  }
  .ws-app.ws-thread-open .ws-thread-panel {
    position: absolute;
    inset: 0;
    z-index: 6;
    border-left: 0;
  }
  .ws-thread-resizer {
    display: none;
  }
  .ws-app.ws-show-side .ws-center {
    display: none;
  }
  .ws-msg-tools,
  .ws-msg:hover .ws-msg-tools {
    display: none;
    position: static;
    flex-wrap: wrap;
    justify-content: flex-end;
    border: 0;
    box-shadow: none;
    background: transparent;
    grid-column: 2;
  }
  .ws-msg.ws-msg-active .ws-msg-tools {
    display: flex;
  }
  .ws-cal-day {
    --ws-cal-pad: .2rem;
    min-height: 4.5rem;
    padding: .2rem;
  }
  .ws-cal-task.ws-cal-span,
  .ws-cal-lane-gap {
    height: 1.1rem;
  }
  .ws-cal-task-title,
  .ws-cal-event,
  .ws-cal-channel {
    font-size: .62rem;
  }
  .ws-cal-task .ws-avatars {
    display: none;
  }
  .ws-brief {
    gap: .55rem;
    margin: .4rem .5rem 0;
    padding: .5rem 2rem .5rem .65rem;
  }
  .ws-brief-icon {
    display: none;
  }
  .ws-brief-greeting {
    font-size: .9rem;
  }
  .ws-brief-line {
    font-size: .8rem;
  }
  .ws-brief-action {
    margin-top: .3rem;
  }
}
@media (max-width: 767.98px) and (max-height: 620px) {
  /* A short screen (a phone on its side) keeps the greeting to one line. */
  .ws-brief-line ~ .ws-brief-line,
  .ws-brief-action {
    display: none;
  }
}
@media (max-width: 575.98px) {
  /* On a phone a day is a tap target: what is in it shows as bars, the day drawer tells the rest. */
  .ws-cal-day {
    --ws-cal-pad: .15rem;
    min-height: 3.8rem;
    padding: .2rem .15rem;
  }
  .ws-cal-task.ws-cal-span,
  .ws-cal-lane-gap {
    height: .32rem;
  }
  .ws-cal-lane-gap {
    margin-bottom: .16rem;
  }
  .ws-cal-day-top {
    flex-wrap: wrap;
    gap: .1rem;
  }
  .ws-cal-num {
    min-width: 1.45rem;
    height: 1.45rem;
    font-size: .74rem;
  }
  .ws-cal-talk {
    font-size: .6rem;
    padding: 0 .25rem;
  }
  .ws-cal-talk .bi {
    display: none;
  }
  .ws-cal-task,
  .ws-cal-event,
  .ws-cal-channel {
    height: .32rem;
    padding: 0;
    margin-bottom: .16rem;
    font-size: 0;
    border-radius: 999px;
    pointer-events: none;
  }
  .ws-cal-task > *,
  .ws-cal-channel > * {
    display: none;
  }
  .ws-cal-task {
    background: rgba(var(--ws-accent-rgb), .6);
  }
  .ws-cal-task.ws-cal-late {
    background: rgba(var(--bs-danger-rgb), .7);
  }
  .ws-cal-task.ws-cal-upcoming {
    background: rgba(var(--ws-accent-rgb), .25);
    box-shadow: none;
  }
  .ws-cal-task.ws-cal-upcoming.ws-cal-waits {
    background: rgba(var(--bs-warning-rgb), .4);
  }
  .ws-cal-event {
    background: rgba(var(--bs-info-rgb), .6);
  }
  .ws-cal-event.ws-kind-leave,
  .ws-cal-event.ws-kind-holiday {
    background: var(--bs-secondary);
  }
  .ws-cal-event.ws-kind-visit {
    background: rgba(var(--bs-warning-rgb), .7);
  }
  .ws-cal-channel {
    background: rgba(var(--ws-accent-rgb), .25);
  }
  .ws-cal-more {
    font-size: .58rem;
    padding-left: .1rem;
  }
}
/* Asking Claude: where a request stands, the tasks an answer proposes, and
   the settings card. */
.ws-claude-state {
  display: flex;
  align-items: center;
  gap: .35rem;
  margin-top: .3rem;
  font-size: .76rem;
  color: var(--bs-secondary-color);
}
.ws-claude-state.ws-claude-running > .bi,
.ws-claude-state.ws-claude-sent > .bi {
  color: rgb(var(--ws-accent-rgb));
}
.ws-claude-state.ws-claude-answered > .bi {
  color: var(--bs-success);
}
.ws-claude-state.ws-claude-failed,
.ws-claude-state.ws-claude-cancelled {
  color: var(--bs-danger-text-emphasis);
}
.ws-drafts {
  display: grid;
  gap: .4rem;
  max-width: 34rem;
  margin-top: .45rem;
}
.ws-drafts-title {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}
.ws-draft {
  padding: .5rem .7rem;
  border: 1px dashed rgba(var(--ws-accent-rgb), .55);
  border-radius: var(--ws-radius-sm);
  background: rgba(var(--ws-accent-rgb), .05);
  font-size: .84rem;
}
.ws-draft.ws-draft-accepted,
.ws-draft.ws-draft-dismissed {
  border-style: solid;
  border-color: var(--bs-border-color);
  background: transparent;
}
.ws-draft.ws-draft-dismissed .ws-draft-head b {
  text-decoration: line-through;
  color: var(--bs-secondary-color);
}
.ws-draft-head {
  display: flex;
  align-items: baseline;
  gap: .4rem;
}
.ws-draft-head > .bi {
  color: rgb(var(--ws-accent-rgb));
}
.ws-draft-meta,
.ws-draft-done {
  margin-top: .15rem;
  font-size: .76rem;
  color: var(--bs-secondary-color);
}
.ws-draft-text {
  margin-top: .25rem;
  white-space: pre-line;
}
.ws-draft-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  margin-top: .45rem;
}
.ws-change.ws-change-applied,
.ws-change.ws-change-dismissed,
.ws-change.ws-change-stale,
.ws-change.ws-change-failed {
  border-style: solid;
  border-color: var(--bs-border-color);
  background: transparent;
}
.ws-change-record {
  min-width: 0;
  overflow-wrap: anywhere;
}
.ws-change-type {
  margin-left: auto;
  font-size: .72rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}
.ws-change-fields {
  display: grid;
  grid-template-columns: minmax(6rem, max-content) 1fr;
  gap: .15rem .6rem;
  margin: .35rem 0 0;
}
.ws-change-fields dt {
  font-weight: 500;
  color: var(--bs-secondary-color);
}
.ws-change-fields dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}
.ws-change-from {
  color: var(--bs-secondary-color);
  text-decoration: line-through;
}
.ws-change-arrow {
  margin: 0 .3rem;
  color: var(--bs-secondary-color);
}
.ws-change-to {
  font-weight: 600;
}
.ws-change-warning {
  margin-top: .35rem;
  font-size: .78rem;
  color: var(--bs-warning-text-emphasis);
}
.ws-change-action {
  flex: none;
  padding: .05rem .45rem;
  border-radius: var(--ws-radius);
  font-size: .7rem;
  font-weight: 600;
  white-space: nowrap;
  color: var(--bs-primary-text-emphasis);
  background: var(--bs-primary-bg-subtle);
}
.ws-change-do-delete .ws-change-action {
  color: var(--bs-danger-text-emphasis);
  background: var(--bs-danger-bg-subtle);
}
.ws-change-held {
  color: var(--bs-secondary-color);
}
.ws-change-fields .ws-change-to .ws-chip {
  margin: 0 .2rem .2rem 0;
  font-weight: 500;
}
@media (max-width: 575.98px) {
  .ws-change-fields {
    grid-template-columns: 1fr;
  }
  .ws-change-fields dd {
    margin-bottom: .25rem;
  }
}
.ws-claude-copy {
  display: flex;
  align-items: flex-start;
  gap: .35rem;
}
.ws-claude-copy .form-control {
  font-size: .76rem;
}
.ws-claude-steps {
  display: grid;
  gap: 1rem;
  padding-left: 1.2rem;
}
/* The pictures of the steps for connecting Claude (claude_setup.php). */
.ws-claude-steps .ws-ui {
  font-weight: 600;
  color: var(--bs-emphasis-color);
  white-space: nowrap;
}
.ws-claude-shot {
  max-width: 380px;
  margin: .1rem 0 .65rem;
}
.ws-claude-shot svg,
.ws-claude-shot img {
  display: block;
  width: 100%;
  height: auto;
}
.ws-claude-shot img {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
}
.ws-claude-shot svg {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
.ws-claude-shot figcaption {
  margin-top: .25rem;
  font-size: .72rem;
  color: var(--bs-secondary-color);
}
.ws-claude-shot .s-frame { fill: var(--bs-body-bg); stroke: var(--bs-border-color); }
.ws-claude-shot .s-bar { fill: var(--bs-tertiary-bg); }
.ws-claude-shot .s-dot { fill: var(--bs-border-color); }
.ws-claude-shot .s-address,
.ws-claude-shot .s-field,
.ws-claude-shot .s-dialog { fill: var(--bs-body-bg); stroke: var(--bs-border-color); }
.ws-claude-shot .s-soft,
.ws-claude-shot .s-panel { fill: var(--bs-tertiary-bg); stroke: var(--bs-border-color); }
.ws-claude-shot .s-line,
.ws-claude-shot .s-chip,
.ws-claude-shot .s-code { fill: var(--bs-secondary-bg); }
.ws-claude-shot .s-dim { fill: rgba(0, 0, 0, .16); }
.ws-claude-shot .s-ink { fill: var(--bs-body-color); }
.ws-claude-shot .s-mute { fill: var(--bs-secondary-color); }
.ws-claude-shot .s-white { fill: #fff; }
.ws-claude-shot .s-acc { fill: var(--bs-primary); }
.ws-claude-shot .s-acc-soft { fill: rgba(var(--bs-primary-rgb), .14); }
.ws-claude-shot .s-acc-ink { fill: var(--bs-primary); }
.ws-claude-shot .s-warn { fill: var(--bs-warning-text-emphasis); }
.ws-claude-shot .s-bad { fill: var(--bs-danger); }
.ws-claude-shot .s-claude { fill: #c96442; }
.ws-claude-shot .s-ring { fill: none; stroke: #f59f00; stroke-width: 2; }
.ws-claude-status {
  position: sticky;
  top: 1rem;
}
.ws-claude-recent-row {
  display: flex;
  align-items: flex-start;
  gap: .5rem;
  padding: .4rem 0;
  border-bottom: 1px solid var(--bs-border-color);
  font-size: .84rem;
}
.ws-claude-recent-row > .bi {
  margin-top: .15rem;
  color: var(--bs-secondary-color);
}
/* The decision timeline: a day heading, then the day's decisions on a line
   of dots - the time on its left, the decision's card on its right. */
.ws-tl-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem 1rem;
  font-size: .85rem;
  color: var(--bs-secondary-color);
  margin-bottom: .75rem;
}
.ws-tl-hidden {
  font-size: .85rem;
}
.ws-tl-hidden summary {
  cursor: pointer;
}
.ws-tl-day + .ws-tl-day {
  margin-top: 1.25rem;
}
.ws-tl-date {
  font-size: .78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--bs-secondary-color);
  margin: 0 0 .5rem 5.75rem;
}
.ws-tl-item {
  display: grid;
  grid-template-columns: 3rem 1.75rem minmax(0, 1fr);
  column-gap: .5rem;
}
.ws-tl-time {
  text-align: right;
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
  color: var(--bs-secondary-color);
  padding-top: .95rem;
}
.ws-tl-line {
  position: relative;
}
.ws-tl-line::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  background: var(--bs-border-color);
}
.ws-tl-item:first-child .ws-tl-line::before {
  top: 1.35rem;
}
.ws-tl-item:last-child .ws-tl-line::before {
  bottom: calc(100% - 1.35rem);
}
.ws-tl-item:only-child .ws-tl-line::before {
  display: none;
}
.ws-tl-dot {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  margin: .55rem auto 0;
  border-radius: 50%;
  border: 2px solid var(--bs-success);
  background: var(--bs-body-bg);
  color: var(--bs-success);
  font-size: .78rem;
}
.ws-tl-note .ws-tl-dot {
  border-color: var(--bs-info);
  color: var(--bs-info);
}
.ws-tl-by-claude .ws-tl-dot {
  border-color: var(--ws-accent);
  color: var(--ws-accent);
}
.ws-tl-card {
  padding: .65rem .85rem;
  margin-bottom: .75rem;
}
.ws-tl-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .35rem .6rem;
  margin-bottom: .35rem;
  font-size: .82rem;
}
.ws-tl-channel {
  display: inline-flex;
  align-items: center;
  gap: .15rem;
  font-weight: 600;
  color: var(--bs-body-color);
  text-decoration: none;
}
.ws-tl-channel:hover {
  text-decoration: underline;
}
.ws-tl-ai {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  font-size: .72rem;
  font-weight: 600;
  padding: 0 .4rem;
  border-radius: var(--ws-radius-xs);
  background: var(--ws-soft);
  color: var(--ws-accent);
}
.ws-tl-file {
  display: inline-block;
  margin-top: .5rem;
  font-size: .85rem;
}
.ws-tl-file img {
  display: block;
  max-width: 100%;
  max-height: 8rem;
  border-radius: var(--ws-radius-sm);
}
.ws-tl-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem .75rem;
  margin-top: .5rem;
  font-size: .78rem;
  color: var(--bs-secondary-color);
}
.ws-tl-who {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  color: var(--bs-body-color);
}
.ws-tl-who img {
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-tl-more {
  text-align: center;
  padding: .25rem 0 1rem;
}
.ws-tl-side {
  position: sticky;
  top: 1rem;
}
.ws-tl-side .list-group-item {
  font-size: .85rem;
}
@media (max-width: 575.98px) {
  .ws-tl-item {
    grid-template-columns: 2.4rem 1.4rem minmax(0, 1fr);
    column-gap: .35rem;
  }
  .ws-tl-date {
    margin-left: 4.5rem;
  }
  .ws-tl-dot {
    width: 1.35rem;
    height: 1.35rem;
    font-size: .7rem;
  }
  .ws-tl-card {
    padding: .55rem .65rem;
  }
}
/* The foot of the sidebar: the settings, however long the list above. */
.ws-side-foot {
  flex: 0 0 auto;
  padding: .4rem .5rem;
  border-top: 1px solid var(--bs-border-color);
}
/* The rail at the left edge of the workspace screens other than the channels,
   and the drawer it opens with the whole list. */
.ws-shell {
  display: flex;
  align-items: flex-start;
}
.ws-shell-main {
  flex: 1 1 auto;
  min-width: 0;
}
.ws-rail {
  position: sticky;
  top: 3.125rem;
  flex: 0 0 3.25rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .2rem;
  height: calc(100vh - 3.125rem);
  padding: .5rem 0;
  overflow-y: auto;
  scrollbar-width: none;
  border-right: 1px solid var(--bs-border-color);
  background: rgba(var(--ws-accent-rgb), .05);
}
@supports (height: 100dvh) {
  .ws-rail { height: calc(100dvh - 3.125rem); }
}
.ws-rail-item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2.25rem;
  height: 2.25rem;
  border: 0;
  border-radius: var(--ws-radius-sm);
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: 1.05rem;
  text-decoration: none;
}
.ws-rail-item:hover {
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
}
.ws-rail-item.active {
  background: rgba(var(--ws-accent-rgb), .16);
  color: var(--ws-accent);
}
.ws-rail-open {
  color: var(--bs-body-color);
}
.ws-rail-sep {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1px;
  margin: .25rem 0;
  background: var(--bs-border-color);
}
.ws-rail-grow {
  flex: 1 1 auto;
}
.ws-rail-initial {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: var(--ws-radius-xs);
  background: rgba(var(--ws-accent-rgb), .14);
  color: var(--ws-accent);
  font-size: .8rem;
  font-weight: 700;
}
.ws-rail-channel[data-ws-colored] .ws-rail-initial {
  background: var(--ws-ch);
  color: #fff;
}
.ws-rail-badge {
  position: absolute;
  top: .05rem;
  right: .05rem;
  min-width: 1rem;
  padding: 0 .25rem;
  border-radius: 999px;
  background: var(--bs-danger);
  color: #fff;
  font-size: .6rem;
  line-height: 1rem;
  text-align: center;
}
.ws-rail-dot {
  position: absolute;
  top: .2rem;
  right: .2rem;
  width: .55rem;
  height: .55rem;
  border-radius: 50%;
  border: 2px solid var(--bs-body-bg);
  background: var(--bs-secondary-color);
}
.ws-rail-dot.ws-rail-dot-mention {
  background: var(--bs-danger);
}
.ws-nav-drawer {
  --bs-offcanvas-width: min(19rem, 88vw);
}
.ws-nav-drawer .ws-side-scroll {
  padding-top: .25rem;
}
@media (max-width: 575.98px) {
  .ws-rail {
    flex-basis: 2.75rem;
  }
  .ws-rail-item {
    width: 2rem;
    height: 2rem;
    font-size: .95rem;
  }
}
/* The overview: what is coming, what was written to the person, what this
   place is, and beside it the latest decisions. */
.ws-home {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 1rem;
}
.ws-home-hello {
  font-size: 1.15rem;
  font-weight: 600;
  margin: 0 0 .75rem;
}
.ws-home .col-md-6 > .ws-home-card {
  height: 100%;
}
.ws-home-card + .ws-home-card {
  margin-top: 1rem;
}
.ws-home-card .card-header {
  background: transparent;
}
.ws-home-counts {
  display: flex;
  gap: .75rem;
  padding: .1rem .5rem .35rem;
  font-size: .8rem;
  color: var(--bs-secondary-color);
}
.ws-home-empty {
  padding: .5rem;
  font-size: .85rem;
  color: var(--bs-secondary-color);
}
.ws-home-row,
.ws-home-tl-item {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  width: 100%;
  border: 0;
  border-radius: var(--ws-radius-sm);
  background: transparent;
  color: var(--bs-body-color);
  text-align: left;
  padding: .45rem .5rem;
}
button.ws-home-row:hover,
.ws-home-tl-item:hover {
  background: var(--bs-secondary-bg);
}
.ws-home-row > .bi {
  margin-top: .15rem;
}
.ws-home-row-text {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
}
.ws-home-row-title {
  font-size: .9rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-home-row-meta {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--bs-secondary-color);
}
.ws-home-row-meta b {
  color: var(--bs-body-color);
}
.ws-home-row-quote {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: .85rem;
}
.ws-home-unread .ws-home-row-meta b::after {
  content: "";
  display: inline-block;
  width: .45rem;
  height: .45rem;
  margin-left: .3rem;
  border-radius: 50%;
  background: var(--bs-danger);
  vertical-align: middle;
}
.ws-home-face img {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-home-about {
  margin-top: 1rem;
}
.ws-home-about > summary {
  cursor: pointer;
  list-style: none;
  font-size: .9rem;
  font-weight: 600;
}
.ws-home-about > summary::-webkit-details-marker {
  display: none;
}
.ws-home-about > summary::after {
  content: "\F282";
  float: right;
  font-family: bootstrap-icons;
  transition: transform .15s ease;
}
.ws-home-about[open] > summary::after {
  transform: rotate(180deg);
}
.ws-home-tile {
  height: 100%;
  padding: .6rem .75rem;
  border-radius: var(--ws-radius-sm);
  background: var(--ws-soft);
}
.ws-home-tile-title {
  margin-bottom: .2rem;
  font-size: .88rem;
  font-weight: 600;
}
.ws-home-start {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  margin-top: 1rem;
  padding: .75rem 1rem;
  border: 1px dashed var(--bs-border-color);
  border-radius: var(--ws-radius);
}
.ws-home-start-text {
  flex: 1 1 12rem;
  font-size: .9rem;
  font-weight: 600;
}
.ws-home-tl {
  position: relative;
}
.ws-home-tl::before {
  content: "";
  position: absolute;
  top: 1rem;
  bottom: 1rem;
  left: calc(1.2rem - 1px);
  width: 2px;
  background: var(--bs-border-color);
}
.ws-home-tl-dot {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  border: 2px solid var(--bs-success);
  background: var(--bs-body-bg);
  color: var(--bs-success);
  font-size: .7rem;
}
.ws-home-tl-item.ws-tl-by-claude .ws-home-tl-dot {
  border-color: var(--ws-accent);
  color: var(--ws-accent);
}
.ws-home-join .btn {
  flex: 0 0 auto;
  align-self: center;
}
/* Worked-out figures: a formula cell and a hesap block show their value,
   the formula in the tooltip. */
.ws-calc {
  font-variant-numeric: tabular-nums;
  border-bottom: 1px dotted rgba(var(--ws-accent-rgb), .6);
  cursor: help;
}
.ws-calc-error {
  color: var(--bs-danger);
  border-bottom-color: var(--bs-danger);
}
.ws-calc-block {
  display: inline-block;
  min-width: min(22rem, 100%);
  max-width: 100%;
  margin: .25rem 0;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
  overflow: hidden;
}
.ws-calc-block-head {
  padding: .25rem .6rem;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--ws-accent);
  background: var(--ws-soft);
}
.ws-calc-table {
  margin: 0;
}
.ws-calc-table th {
  font-weight: 500;
}
.ws-calc-derived th,
.ws-calc-derived td {
  font-weight: 700;
}
.ws-calc-remark td {
  font-size: .8rem;
  color: var(--bs-secondary-color);
}
/* A hesap block's groups: the heading, the lines in it, the subtotal; a
   blank line as a gap. */
.ws-calc-group th {
  padding-top: .45rem;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--bs-secondary-color);
}
.ws-calc-in th,
.ws-calc-remark.ws-calc-in td {
  padding-left: 1.25rem;
}
.ws-calc-total th,
.ws-calc-total td {
  font-weight: 700;
  border-top: 1px solid var(--bs-border-color-translucent);
  background: var(--bs-tertiary-bg);
}
.ws-calc-func {
  margin-left: .25rem;
  font-size: .72rem;
  font-weight: 500;
  color: var(--bs-secondary-color);
}
.ws-calc-gap td {
  height: .6rem;
  padding: 0;
  border-bottom: 0;
}
/* A figure a reader may try another value for (assets/js/workspace_editor.js). */
.ws-msg-body [data-ws-calc-input] {
  cursor: text;
  border-bottom-style: dashed;
}
.ws-msg-body [data-ws-calc-input]:hover {
  background: rgba(var(--ws-accent-rgb), .08);
}
.ws-rich [data-ws-calc-input] {
  cursor: inherit;
}
.ws-calc-live-input {
  display: inline-block;
  width: 7.5rem;
  padding: 0 .35rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.ws-calc-block.ws-calc-tried {
  border-color: rgba(var(--ws-accent-rgb), .55);
}
.ws-calc-block-head {
  display: flex;
  align-items: center;
  gap: .3rem;
}
.ws-calc-reset {
  margin-left: auto;
  padding: 0;
  font-size: .72rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
}
.ws-calc.ws-calc-tried,
.ws-calc-tried .ws-calc-derived .ws-calc,
.ws-calc-tried .ws-calc-total .ws-calc {
  color: var(--ws-accent);
}
/* The calculation window: its tools, settings and the names to pick */
.ws-calc-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .15rem;
  margin-bottom: .4rem;
}
.ws-calc-tools .ws-calc-op {
  min-width: 2rem;
  font-family: var(--bs-font-monospace);
  font-weight: 600;
}
.ws-calc-settings {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .35rem .6rem;
  margin-bottom: .4rem;
}
.ws-calc-settings .form-select {
  width: auto;
}
.ws-calc-settings .form-control {
  width: 7rem;
}
.ws-calc-names {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem;
  margin-top: .5rem;
}
.ws-calc-names-label {
  margin-right: .25rem;
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
.ws-calc-name {
  padding: .05rem .5rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 999px;
  font-size: .75rem;
}
.ws-calc-name:hover {
  border-color: var(--ws-accent);
  color: var(--ws-accent);
}
/* A note's earlier versions */
.ws-nb-versions-grid {
  display: grid;
  grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
}
.ws-nb-versions-list {
  max-height: 65vh;
  overflow-y: auto;
}
.ws-nb-versions-view {
  min-width: 0;
  min-height: 8rem;
  padding: .75rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-sm);
}
@media (max-width: 767.98px) {
  .ws-nb-versions-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .ws-nb-versions-list {
    max-height: 30vh;
  }
}
/* Notes brought in from other apps: the drop area, the files and what each becomes */
.ws-nb-import-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ws-space-1);
  padding: 2rem var(--ws-space-4);
  border: 2px dashed var(--bs-border-color);
  border-radius: var(--ws-radius);
  text-align: center;
  cursor: pointer;
  transition: border-color .15s ease, background-color .15s ease;
}
.ws-nb-import-drop > .bi {
  font-size: 2rem;
  color: var(--ws-accent);
}
.ws-nb-import-drop:hover,
.ws-nb-import-drop.ws-nb-import-over {
  border-color: var(--ws-accent);
  background-color: rgba(var(--ws-accent-rgb), .06);
}
.ws-nb-import-list {
  max-height: 55vh;
  overflow-y: auto;
  border-radius: var(--ws-radius-sm);
}
.ws-nb-import-row {
  display: flex;
  gap: var(--ws-space-2);
  align-items: flex-start;
  cursor: pointer;
}
.ws-nb-import-row .form-check-input {
  flex: none;
  margin-top: .2rem;
}
.ws-nb-import-row.ws-nb-import-off {
  cursor: default;
}
.ws-nb-import-row.ws-nb-import-off .ws-nb-import-main > .fw-semibold {
  opacity: .6;
}
.ws-nb-import-main {
  flex: 1 1 auto;
  min-width: 0;
}
.ws-nb-import-path {
  overflow: hidden;
  font-size: .75rem;
  color: var(--bs-secondary-color);
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-nb-import-state {
  font-size: .8rem;
  color: var(--bs-secondary-color);
}
.ws-nb-import-kind.ws-nb-import-new,
.ws-nb-import-badge.ws-nb-import-new {
  color: var(--bs-success-text-emphasis);
}
.ws-nb-import-kind.ws-nb-import-update,
.ws-nb-import-badge.ws-nb-import-update {
  color: var(--ws-accent);
}
.ws-nb-import-kind.ws-nb-import-error,
.ws-nb-import-badge.ws-nb-import-error {
  color: var(--bs-danger-text-emphasis);
}
.ws-nb-import-badge {
  font-weight: 500;
  background-color: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
}
.ws-nb-import-warn {
  font-size: .75rem;
  color: var(--bs-warning-text-emphasis);
}
.ws-table-edit td[data-ws-result]::after,
.ws-table-edit th[data-ws-result]::after {
  content: attr(data-ws-result);
  display: block;
  font-size: .72rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--ws-accent);
}
.ws-table-edit .ws-cell-error::after {
  color: var(--bs-danger);
}
/* Workspace notes: the lists, the note, its writing box */
.ws-nb-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}
.ws-nb-intro {
    display: flex;
    gap: .6rem;
    align-items: flex-start;
    padding: .75rem;
    border: 1px dashed var(--bs-border-color);
    border-radius: var(--ws-radius-sm);
}
.ws-nb-intro > .bi {
    font-size: 1.4rem;
    opacity: .6;
}
.ws-nb-section {
    display: flex;
    flex-direction: column;
    gap: .2rem;
}
.ws-nb-section-title {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: 0 .35rem .2rem;
    font-size: .75rem;
    font-weight: 600;
    letter-spacing: .04em;
    color: var(--bs-secondary-color);
    text-transform: uppercase;
}
.ws-nb-count {
    padding: 0 .4rem;
    border-radius: var(--ws-radius);
    background: var(--bs-tertiary-bg);
    font-size: .7rem;
}
.ws-nb-section-empty {
    padding: .25rem .35rem;
    font-size: .8125rem;
    color: var(--bs-secondary-color);
}
.ws-nb-item {
    padding: .5rem .6rem;
    border: 1px solid transparent;
    border-radius: var(--ws-radius-sm);
    cursor: pointer;
}
.ws-nb-item:hover,
.ws-nb-item:focus-visible {
    background: var(--bs-tertiary-bg);
    outline: 0;
}
.ws-nb-item.active {
    border-color: var(--bs-primary-border-subtle);
    background: var(--bs-primary-bg-subtle);
}
.ws-nb-item-head {
    display: flex;
    align-items: center;
    gap: .4rem;
}
.ws-nb-item-head > .bi {
    flex: none;
    color: var(--bs-secondary-color);
}
.ws-nb-item-head > .ws-nb-item-pin {
    color: var(--bs-primary);
}
.ws-nb-item-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ws-nb-item-time {
    flex: none;
    font-size: .75rem;
    color: var(--bs-secondary-color);
}
/* Changed by somebody else, or answered by Claude, since it was last open. */
.ws-nb-item-unread .ws-nb-item-name {
    color: var(--bs-emphasis-color);
}
.ws-nb-item-unread .ws-nb-item-time {
    color: var(--ws-accent);
    font-weight: 600;
}
.ws-nb-item-dot {
    flex: none;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: var(--ws-accent);
}
.ws-nb-item-claude {
    flex: none;
    font-size: .72rem;
    white-space: nowrap;
    color: var(--bs-secondary-color);
}
.ws-nb-item-claude-answered {
    color: var(--ws-accent);
    font-weight: 600;
}
.ws-nb-item-more {
    flex: none;
    padding: 0 .2rem;
    border: 0;
    background: transparent;
    color: var(--bs-secondary-color);
    opacity: 0;
}
.ws-nb-item:hover .ws-nb-item-more,
.ws-nb-item.active .ws-nb-item-more,
.ws-nb-item-more:focus-visible {
    opacity: 1;
}
.ws-nb-item-excerpt {
    display: -webkit-box;
    margin-top: .15rem;
    overflow: hidden;
    font-size: .8125rem;
    color: var(--bs-secondary-color);
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.ws-nb-item-meta {
    display: flex;
    align-items: center;
    gap: .4rem;
    min-width: 0;
    margin-top: .25rem;
    font-size: .75rem;
    color: var(--bs-secondary-color);
}
.ws-nb-faces {
    display: inline-flex;
    flex: none;
    align-items: center;
}
.ws-nb-face {
    position: relative;
    margin-right: -.35rem;
    border: 2px solid var(--bs-body-bg);
    border-radius: 50%;
    line-height: 0;
}
.ws-nb-face img,
.ws-nb-face .ws-avatar {
    width: var(--ws-avatar-sm);
    height: var(--ws-avatar-sm);
    border-radius: 50%;
}
.ws-nb-face-edit::after {
    content: "\F4CB";
    position: absolute;
    right: -.3rem;
    bottom: -.3rem;
    padding: .05rem;
    border-radius: 50%;
    background: var(--bs-body-bg);
    font-family: bootstrap-icons;
    font-size: .6rem;
    line-height: 1;
    color: var(--bs-primary);
}
.ws-nb-face-more,
.ws-nb-face-channel {
    margin-left: .55rem;
    font-size: .75rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}
.ws-nb-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    padding: 3rem 1rem;
    color: var(--bs-secondary-color);
    text-align: center;
}
.ws-nb-placeholder > .bi {
    font-size: 2rem;
    opacity: .6;
}
.ws-nb-card {
    position: relative;
}
.ws-nb-card.ws-nb-dropping {
    outline: 2px dashed var(--bs-primary);
    outline-offset: 4px;
}
.ws-nb-card.ws-nb-dropping::after {
    content: attr(data-drop-label);
    position: absolute;
    inset: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: inherit;
    background: rgba(var(--bs-primary-rgb), .08);
    font-weight: 600;
    color: var(--bs-primary-text-emphasis);
    pointer-events: none;
}
.ws-nb-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem;
    background: transparent;
}
.ws-nb-head .ws-nb-faces {
    margin-right: .6rem;
}
.ws-nb-head .ws-nb-title {
    flex: 1 1 12rem;
    min-width: 0;
    padding: .25rem;
    border: 0;
    background: transparent;
    font-size: 1.125rem;
    font-weight: 600;
    box-shadow: none;
}
.ws-nb-status {
    margin-right: .25rem;
    font-size: .75rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}
.ws-nb-status-unsaved {
    color: var(--bs-warning-text-emphasis);
}
.ws-nb-info {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem 1rem;
    padding: .4rem 1rem;
    border-bottom: 1px solid var(--bs-border-color-translucent);
    font-size: .8125rem;
    color: var(--bs-secondary-color);
}
.ws-nb-info-part > .bi {
    margin-right: .3rem;
}
.ws-nb-tools {
    position: sticky;
    top: 0;
    z-index: 3;
    display: flex;
    flex-wrap: wrap;
    gap: .25rem .75rem;
    margin: -.25rem 0 .5rem;
    padding: .25rem 0;
    background: var(--bs-body-bg);
}
.ws-nb-tool-group {
    display: flex;
    gap: .1rem;
}
.ws-nb-tool-group + .ws-nb-tool-group {
    padding-left: .75rem;
    border-left: 1px solid var(--bs-border-color-translucent);
}
.ws-nb-editor {
    position: relative;
}
.ws-rich.ws-rich-field.ws-nb-text {
    min-height: 55vh;
    max-height: none;
    padding: 1rem 1.1rem;
    font-size: .95rem;
    line-height: 1.6;
}
.ws-nb-claude {
    display: flex;
    align-items: center;
    margin-top: .5rem;
}
.ws-nb-read {
    min-height: 8rem;
}
.ws-nb-shares {
    margin-top: 1rem;
}
.ws-nb-share-row {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .25rem 0;
}
.ws-nb-share-row img,
.ws-nb-share-row .ws-avatar {
    width: var(--ws-avatar-sm);
    height: var(--ws-avatar-sm);
    border-radius: 50%;
}
@media (max-width: 991.98px) {
    .ws-nb-open .ws-nb-list-col,
    #ws-root:not(.ws-nb-open) .ws-nb-pane-col {
        display: none;
    }
}
/* A note shared in a channel, as its card in the conversation */
.ws-nb-msg-card {
    max-width: 34rem;
    padding: .6rem .75rem;
    border: 1px solid var(--bs-border-color);
    border-left: 3px solid var(--bs-primary);
    border-radius: var(--ws-radius-sm);
    background: var(--bs-tertiary-bg);
}
.ws-nb-msg-card-head {
    display: flex;
    align-items: center;
    gap: .4rem;
}
.ws-nb-msg-card-kind {
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .04em;
    color: var(--bs-secondary-color);
    text-transform: uppercase;
}
.ws-nb-msg-card-text {
    display: -webkit-box;
    margin-top: .25rem;
    overflow: hidden;
    font-size: .875rem;
    color: var(--bs-secondary-color);
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
}
/* The note drawn (tables, checklists): no line clamp, which would cut a
   table mid-row; a long note scrolls inside the card instead. */
.ws-nb-msg-card-text.ws-nb-msg-card-html {
    display: block;
    max-height: 18rem;
    overflow: auto;
    color: var(--bs-body-color);
    -webkit-line-clamp: unset;
}
.ws-nb-msg-card-html > :last-child {
    margin-bottom: 0;
}
.ws-nb-msg-card-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem .75rem;
    margin-top: .4rem;
    font-size: .75rem;
    color: var(--bs-secondary-color);
}
/* The result of a line that ends with "=" */
.ws-calc.ws-calc-inline {
    margin-left: .25rem;
    padding: 0 .35rem;
    border-radius: .25rem;
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}
.ws-calc.ws-calc-inline.ws-calc-error {
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
}
.ws-inline-result {
    margin-left: .3rem;
    padding: 0 .35rem;
    border-radius: .25rem;
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    user-select: none;
}
.ws-inline-result.ws-inline-error {
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
}
/* The table window as a sheet: formula bar, column letters, row numbers */
.ws-sheet-bar {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .5rem;
}
.ws-sheet-ref {
    flex: none;
    min-width: 3rem;
    padding: .25rem .5rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--ws-radius-xs);
    background: var(--bs-tertiary-bg);
    font-family: var(--bs-font-monospace);
    font-size: .8125rem;
    font-weight: 600;
    text-align: center;
}
.ws-sheet-formula {
    font-family: var(--bs-font-monospace);
}
.ws-sheet-result {
    flex: none;
    font-family: var(--bs-font-monospace);
    font-size: .8125rem;
    color: var(--bs-success-text-emphasis);
    white-space: nowrap;
}
.ws-sheet-result.ws-sheet-error-text {
    color: var(--bs-danger-text-emphasis);
}
.ws-table-sheet .ws-sheet-letters th,
.ws-table-sheet th.ws-sheet-num {
    padding: .15rem .45rem;
    background: var(--bs-tertiary-bg);
    font-size: .75rem;
    font-weight: 600;
    color: var(--bs-secondary-color);
    text-align: center;
    vertical-align: middle;
    user-select: none;
}
.ws-table-sheet .ws-sheet-corner,
.ws-table-sheet th.ws-sheet-num {
    width: 2.5rem;
}
.ws-table-sheet .ws-sheet-col.active,
.ws-table-sheet th.ws-sheet-num.active {
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
}
.ws-cell-edit.ws-cell-computed {
    color: var(--bs-success-text-emphasis);
    font-variant-numeric: tabular-nums;
    text-align: right;
}
.ws-cell-edit.ws-cell-error {
    color: var(--bs-danger-text-emphasis);
}
.ws-calc-preview {
    margin-top: .75rem;
}
/* Workspace: an edited file's earlier versions, the text window */
.ws-file-versions {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-top: .3rem;
    padding: 0;
    border: 0;
    background: transparent;
    font-size: .75rem;
    color: var(--bs-secondary-color);
}
.ws-file-versions:hover {
    color: var(--bs-primary);
    text-decoration: underline;
}
.ws-text-area {
    min-height: 60vh;
    font-family: var(--bs-font-monospace);
    font-size: .875rem;
    white-space: pre;
    resize: vertical;
}
.ws-text-view {
    min-height: 60vh;
}
/* Mentioned here, not in the channel */
.ws-msg-invite {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    max-width: 34rem;
    margin-top: .35rem;
    padding: .3rem .5rem;
    border-radius: var(--ws-radius-xs);
    background: var(--bs-warning-bg-subtle);
    font-size: .8125rem;
    color: var(--bs-warning-text-emphasis);
}
/* The search box of the whole workspace (assets/js/workspace_palette.js) */
.ws-palette .modal-dialog {
    margin-top: 8vh;
}
.ws-palette-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .55rem .9rem;
    border-bottom: 1px solid var(--bs-border-color);
}
.ws-palette-glass {
    color: var(--bs-secondary-color);
    font-size: 1.1rem;
}
.ws-palette-input,
.ws-palette-input:focus {
    padding: .35rem 0;
    border: 0;
    background: transparent;
    box-shadow: none;
    font-size: 1.05rem;
}
.ws-palette-esc {
    background: var(--bs-secondary-bg);
    color: var(--bs-secondary-color);
    font-size: .7rem;
}
.ws-palette-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem .75rem;
    padding: .45rem .9rem;
    border-bottom: 1px solid var(--bs-border-color);
}
.ws-palette-kinds {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem;
}
.ws-palette-kind {
    --bs-btn-padding-y: .1rem;
    --bs-btn-padding-x: .55rem;
    --bs-btn-font-size: .75rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 999px;
    color: var(--bs-secondary-color);
}
.ws-palette-kind.active {
    border-color: transparent;
    background: rgba(var(--ws-accent-rgb), .14);
    color: var(--ws-accent);
}
.ws-palette-list {
    max-height: min(60vh, 34rem);
    overflow-y: auto;
    padding: .35rem 0;
}
.ws-palette-group {
    display: flex;
    align-items: center;
    gap: .25rem;
    padding: .55rem .9rem .2rem;
    color: var(--bs-secondary-color);
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
}
.ws-palette-count {
    margin-left: .25rem;
    font-weight: 400;
}
.ws-palette-forget {
    font-size: .7rem;
    letter-spacing: 0;
    text-transform: none;
}
.ws-palette-item {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .4rem .9rem;
    cursor: pointer;
}
.ws-palette-item.active {
    background: rgba(var(--ws-accent-rgb), .1);
}
.ws-palette-icon {
    flex: 0 0 1.6rem;
    color: var(--bs-secondary-color);
    text-align: center;
}
.ws-palette-face {
    flex: 0 0 auto;
    border-radius: 50%;
}
.ws-palette-main {
    flex: 1 1 auto;
    min-width: 0;
}
.ws-palette-title,
.ws-palette-sub {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ws-palette-sub {
    color: var(--bs-secondary-color);
    font-size: .78rem;
}
.ws-palette-done .ws-palette-title {
    color: var(--bs-secondary-color);
    text-decoration: line-through;
}
.ws-palette-date {
    flex: 0 0 auto;
    color: var(--bs-secondary-color);
    font-size: .75rem;
}
.ws-palette-mark {
    padding: 0;
    border-radius: .15rem;
    background: rgba(var(--bs-warning-rgb), .35);
    color: inherit;
}
.ws-palette-empty,
.ws-palette-note {
    padding: .9rem;
    color: var(--bs-secondary-color);
    font-size: .875rem;
}
.ws-palette-note {
    padding-bottom: .2rem;
}
.ws-palette-foot {
    padding: .4rem .9rem;
    border-top: 1px solid var(--bs-border-color);
    color: var(--bs-secondary-color);
    font-size: .72rem;
}
/* A channel's board (assets/js/workspace_board_channel.js) */
.ws-cb-bar {
    margin-bottom: .6rem;
}
.ws-cb-bar > .btn-group {
    flex-wrap: wrap;
}
.ws-pane .ws-cb-picker {
    display: none;
}
.ws-cb-cols {
    display: flex;
    align-items: flex-start;
    gap: .75rem;
    padding-bottom: .5rem;
    overflow-x: auto;
}
.ws-cb-col {
    display: flex;
    flex: 0 0 17rem;
    flex-direction: column;
    min-width: 0;
    border: 2px solid transparent;
    border-radius: var(--ws-radius);
    background: var(--bs-tertiary-bg);
}
.ws-cb-col.ws-cb-drop {
    border-color: rgba(var(--ws-accent-rgb), .55);
    background: rgba(var(--ws-accent-rgb), .07);
}
.ws-cb-col-head {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .5rem .6rem .35rem;
    font-size: .8125rem;
    font-weight: 600;
}
.ws-cb-col-icon {
    color: var(--bs-secondary-color);
}
.ws-cb-col-title {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ws-cb-col-count {
    color: var(--bs-secondary-color);
    font-size: .75rem;
    font-weight: 400;
}
.ws-cb-face {
    border-radius: 50%;
}
.ws-cb-add {
    --bs-btn-padding-y: 0;
    --bs-btn-padding-x: .3rem;
}
.ws-cb-list {
    display: flex;
    flex-direction: column;
    gap: .45rem;
    min-height: 2.5rem;
    padding: 0 .5rem .5rem;
}
.ws-cb-empty {
    padding: .6rem .2rem;
    color: var(--bs-secondary-color);
    font-size: .75rem;
    text-align: center;
}
.ws-cb-more {
    align-self: flex-start;
    margin: 0 .5rem .4rem;
    font-size: .75rem;
}
.ws-cb-card {
    position: relative;
    padding: .5rem .6rem .5rem .75rem;
    border: 1px solid var(--bs-border-color-translucent);
    border-radius: var(--ws-radius-sm);
    background: var(--bs-body-bg);
    box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
    cursor: pointer;
    font-size: .8125rem;
}
.ws-cb-card::before {
    content: "";
    position: absolute;
    top: .4rem;
    bottom: .4rem;
    left: 0;
    width: 3px;
    border-radius: 3px;
    background: transparent;
}
.ws-cb-card.ws-cb-pri-urgent::before {
    background: var(--bs-danger);
}
.ws-cb-card.ws-cb-pri-high::before {
    background: var(--bs-warning);
}
.ws-cb-card.ws-cb-pri-low::before {
    background: var(--bs-border-color);
}
.ws-cb-card:hover {
    border-color: rgba(var(--ws-accent-rgb), .35);
}
/* The keyboard moves cards here, so the card it is on has to show. */
.ws-cb-card:focus-visible {
    outline: 2px solid var(--ws-accent);
    outline-offset: 1px;
}
.ws-cb-card.ws-cb-lifted {
    outline: 2px dashed var(--ws-accent);
    outline-offset: 1px;
}
.ws-cb-card.ws-cb-dragging {
    opacity: .5;
}
.ws-cb-closed .ws-cb-title {
    color: var(--bs-secondary-color);
    text-decoration: line-through;
}
.ws-cb-card-top {
    display: flex;
    align-items: center;
    gap: .35rem;
    margin-bottom: .15rem;
    color: var(--bs-secondary-color);
    font-size: .7rem;
}
.ws-cb-number {
    font-weight: 600;
}
.ws-cb-priority {
    background: var(--bs-danger-bg-subtle);
    color: var(--bs-danger-text-emphasis);
}
.ws-cb-status {
    margin-left: auto;
}
.ws-cb-title {
    font-weight: 500;
    line-height: 1.3;
    overflow-wrap: anywhere;
}
.ws-cb-sub {
    margin-top: .2rem;
    color: var(--bs-secondary-color);
    font-size: .72rem;
}
.ws-cb-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem .6rem;
    margin-top: .35rem;
    color: var(--bs-secondary-color);
    font-size: .72rem;
}
.ws-cb-faces {
    margin-left: auto;
}
.ws-cb-faces img {
    width: 1.35rem;
    height: 1.35rem;
}
.ws-cb-more-faces {
    margin-left: .2rem;
    font-size: .68rem;
}
.ws-cb-progress {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
}
.ws-cb-progress-bar {
    display: inline-block;
    width: 2.6rem;
    height: .3rem;
    overflow: hidden;
    border-radius: var(--ws-radius-xs);
    background: var(--bs-secondary-bg);
}
.ws-cb-progress-bar > span {
    display: block;
    height: 100%;
    background: var(--bs-success);
}
.ws-cb-thumb {
    display: block;
    width: 100%;
    max-height: 7rem;
    margin-bottom: .35rem;
    border-radius: var(--ws-radius-xs);
    object-fit: cover;
}
.ws-cb-decision .ws-cb-kind-icon {
    color: var(--bs-success);
}
/* A phone shows one column, picked above the board. */
@media (max-width: 767.98px) {
    .ws-pane .ws-cb-picker {
        display: block;
    }
    .ws-cb-cols {
        display: block;
    }
    .ws-cb-col {
        display: none;
    }
    .ws-cb-col.ws-cb-col-current {
        display: flex;
    }
}
/* Workspace: the site's events (includes/workspace/watch.php, assets/js/workspace_events.js) */
.ws-sa-when-event {
    max-width: 24rem;
}
.ws-sa-when-event-filter {
    margin-top: .5rem;
}
.ws-form-card {
    max-width: 34rem;
    margin-top: .4rem;
    padding: .45rem .65rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--ws-radius-sm);
    background: var(--bs-body-bg);
    font-size: .8125rem;
}
.ws-form-card-head {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin-bottom: .3rem;
    font-weight: 600;
    color: var(--bs-body-color);
    text-decoration: none;
}
a.ws-form-card-head:hover span {
    text-decoration: underline;
}
.ws-form-card-head small {
    margin-left: auto;
    font-weight: 400;
    color: var(--bs-secondary-color);
}
.ws-form-card-fields {
    display: grid;
    grid-template-columns: minmax(6rem, max-content) 1fr;
    gap: .15rem .75rem;
    margin: 0;
}
.ws-form-card-fields dt {
    font-weight: 400;
    color: var(--bs-secondary-color);
}
.ws-form-card-fields dd {
    margin: 0;
    overflow-wrap: anywhere;
}
/* ── Workspace: channel templates (assets/js/workspace_templates.js) ── */
.ws-tpl-row {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .5rem 0;
    border-bottom: 1px solid var(--bs-border-color);
}
.ws-tpl-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: var(--ws-radius-sm);
    background: var(--bs-secondary-bg);
    color: var(--bs-body-color);
}
.ws-tpl-icon[style] {
    color: #fff;
}
.ws-tpl-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: .5rem;
}
.ws-tpl-card {
    display: flex;
    align-items: flex-start;
    gap: .6rem;
    padding: .5rem .6rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--ws-radius-sm);
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    text-align: start;
}
.ws-tpl-card:hover {
    border-color: var(--bs-primary-border-subtle);
}
.ws-tpl-card.active {
    border-color: var(--bs-primary);
    box-shadow: 0 0 0 .15rem var(--bs-primary-bg-subtle);
}
.ws-tpl-card-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}
.ws-tpl-card-name {
    font-weight: 600;
}
.ws-tpl-card-desc,
.ws-tpl-card-count {
    font-size: .8125rem;
    color: var(--bs-secondary-color);
}
.ws-tpl-item {
    margin-bottom: .75rem;
    padding: .6rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--ws-radius-sm);
    background: var(--bs-body-bg);
}
.ws-tpl-item-head {
    display: flex;
    align-items: center;
    gap: .4rem;
}
.ws-tpl-item-n {
    font-weight: 600;
    color: var(--bs-secondary-color);
}
.ws-tpl-handle {
    cursor: grab;
}
.ws-tpl-dragging {
    opacity: .5;
}
.ws-tpl-after {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
}
.ws-tpl-note-body {
    font-family: var(--bs-font-monospace);
    font-size: .8125rem;
}
.ws-tpl-rich {
    min-height: 5rem;
}
/* ── Workspace: time spent on tasks (assets/js/workspace_task_time.js) ── */
.ws-task-time,
.ws-task-links {
    margin: 1rem 0;
    padding-top: .75rem;
    border-top: 1px solid var(--bs-border-color-translucent);
}
.ws-task-time-head,
.ws-task-links-head {
    display: flex;
    align-items: center;
    gap: .35rem;
    font-size: .9rem;
}
.ws-task-time-bar {
    height: 4px;
    margin: .35rem 0 .5rem;
}
.ws-task-time-over .progress-bar {
    background-color: var(--bs-danger);
}
.ws-task-time-timer {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .25rem;
    margin: .5rem 0;
    font-size: .85rem;
}
.ws-task-time-clock {
    font-family: var(--bs-font-monospace);
    font-variant-numeric: tabular-nums;
}
.ws-task-time-add {
    margin-bottom: .5rem;
}
.ws-task-time-list {
    display: grid;
    gap: .4rem;
}
.ws-task-time-row {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .85rem;
}
.ws-task-time-row img {
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    flex: 0 0 auto;
}
.ws-task-time-row-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
}
.ws-task-time-amount {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.ws-task-time-total {
    text-align: end;
    font-size: .8rem;
    font-weight: 600;
}
.ws-task-time-tag,
.ws-task-links-tag {
    font-size: .75rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}
.ws-task-time-stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .75rem;
}
.ws-task-time-week {
    display: flex;
    gap: .5rem;
    padding: .5rem .75rem;
    background: var(--bs-tertiary-bg);
    font-size: .85rem;
}
.ws-task-time-board {
    display: block;
    color: var(--bs-secondary-color);
}
/* ── Workspace: tasks that wait for other tasks (workspace_task_links.js) ── */
.ws-task-links-row {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin-top: .3rem;
    font-size: .85rem;
}
.ws-task-links-done a,
.ws-task-links-done span.ws-grow {
    color: var(--bs-secondary-color);
    text-decoration: line-through;
}
.ws-task-links-results {
    position: absolute;
    z-index: 5;
    left: 0;
    right: 0;
    max-height: 14rem;
    overflow-y: auto;
}
.ws-task-card-blocked {
    margin-top: .15rem;
    font-size: .75rem;
    color: var(--bs-warning-text-emphasis);
}
/* ── Workspace: a task handed to an assistant (workspace_task_delegate.js) ── */
.ws-task-delegate {
    margin: 1rem 0;
    padding-top: .75rem;
    border-top: 1px solid var(--bs-border-color-translucent);
}
.ws-task-delegate-head {
    display: flex;
    align-items: center;
    gap: .35rem;
    margin-bottom: .5rem;
    font-size: .9rem;
}
.ws-task-delegate-with {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .5rem .65rem;
    border: 1px solid var(--bs-border-color);
    border-left: 3px solid rgb(var(--ws-accent-rgb));
    border-radius: var(--ws-radius-sm);
    background: var(--bs-body-bg);
    font-size: .85rem;
}
.ws-task-delegate-face {
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    flex: 0 0 auto;
}
.ws-task-delegate-ask {
    font-size: .85rem;
    white-space: pre-line;
}
.ws-task-delegate-answer {
    margin-top: .3rem;
    padding: .45rem .6rem;
    border-radius: var(--ws-radius-sm);
    background: var(--bs-tertiary-bg);
}
.ws-task-delegate-form {
    display: grid;
    gap: .4rem;
}
.ws-task-delegate-actions {
    display: flex;
    justify-content: flex-end;
}
.ws-task-delegate-tag {
    font-size: .75rem;
    color: rgb(var(--ws-accent-rgb));
    white-space: nowrap;
}
.ws-task-delegate-tag-waiting {
    color: var(--bs-warning-text-emphasis);
}
.ws-task-delegate-waiting {
    margin-bottom: .5rem;
    padding: .35rem .6rem;
    border-radius: var(--ws-radius-sm);
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
    font-size: .8rem;
}
.ws-task-delegate-changes {
    margin-top: .45rem;
}
.ws-change-link {
    font-weight: 600;
    text-decoration: none;
}
/* ── Workspace: approval requests (assets/js/workspace_approvals.js) ── */
.ws-approval {
  max-width: 34rem;
  margin-top: .4rem;
  padding: .55rem .65rem .45rem;
  border: 1px solid var(--bs-border-color);
  border-left: 3px solid rgb(var(--ws-accent-rgb));
  border-radius: var(--ws-radius-sm);
  background: var(--bs-body-bg);
}
.ws-approval-approved {
  border-left-color: var(--bs-success);
}
.ws-approval-rejected {
  border-left-color: var(--bs-danger);
}
.ws-approval-expired,
.ws-approval-withdrawn {
  border-left-color: var(--bs-secondary-color);
}
.ws-approval-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem;
  margin-bottom: .45rem;
  font-size: .8rem;
}
.ws-approval-head > .bi {
  color: rgb(var(--ws-accent-rgb));
}
.ws-approval-tag {
  padding: 0 .4rem;
  border-radius: var(--ws-radius-xs);
  font-size: .68rem;
  font-weight: 600;
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}
.ws-approval-asker,
.ws-approval-when {
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
.ws-approval-when {
  margin-left: auto;
}
.ws-approval-record {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  max-width: 100%;
  margin-bottom: .45rem;
  padding: .2rem .55rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ws-radius-xs);
  background: var(--bs-tertiary-bg);
  font-size: .82rem;
  color: var(--bs-body-color);
}
.ws-approval-record:hover {
  border-color: rgba(var(--ws-accent-rgb), .55);
}
.ws-approval-record-label {
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-approval-record-meta {
  font-size: .72rem;
  color: var(--bs-secondary-color);
}
.ws-approval-people {
  display: grid;
  gap: .25rem;
  margin: 0 0 .4rem;
  padding: 0;
  list-style: none;
}
.ws-approval-person {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .45rem;
  font-size: .85rem;
}
.ws-approval-face {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  object-fit: cover;
}
.ws-approval-name {
  font-weight: 600;
}
.ws-approval-state {
  font-size: .75rem;
  color: var(--bs-secondary-color);
}
.ws-approval-person.ws-approval-approved .ws-approval-state {
  color: var(--bs-success-text-emphasis);
}
.ws-approval-person.ws-approval-rejected .ws-approval-state {
  color: var(--bs-danger-text-emphasis);
}
.ws-approval-note {
  flex-basis: 100%;
  padding-left: 1.95rem;
  font-size: .78rem;
  font-style: italic;
  color: var(--bs-secondary-color);
}
.ws-approval-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem;
  font-size: .78rem;
  color: var(--bs-secondary-color);
}
.ws-approval-guest {
  font-size: .68rem;
  font-weight: 500;
}
/* ── Workspace: the customer asked to approve a task (assets/js/workspace_task_approval.js) ── */
.ws-task-approvals {
  margin: 1rem 0;
  padding-top: .75rem;
  border-top: 1px solid var(--bs-border-color-translucent);
}
.ws-task-approvals-item {
  margin-top: .5rem;
  padding: .5rem .6rem;
  border: 1px solid var(--bs-border-color-translucent);
  border-radius: var(--ws-radius-sm);
}
.ws-task-approvals-item-head {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin-bottom: .3rem;
  font-size: .85rem;
}
.ws-task-approvals-person {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .45rem;
  margin-bottom: .2rem;
  font-size: .82rem;
}
.ws-task-approvals-person.ws-approval-approved .ws-approval-state {
  color: var(--bs-success-text-emphasis);
}
.ws-task-approvals-person.ws-approval-rejected .ws-approval-state {
  color: var(--bs-danger-text-emphasis);
}
.ws-task-approvals-form {
  margin-top: .5rem;
  padding: .6rem;
  border-radius: var(--ws-radius-sm);
  background: var(--bs-tertiary-bg);
}
/* ── Workspace: read receipts (assets/js/workspace_acks.js) ── */
.ws-ack {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem;
  margin-top: .35rem;
  font-size: .78rem;
}
.ws-ack-count {
  padding: 0 .45rem;
  border: 0;
  border-radius: var(--ws-radius);
  background: var(--bs-secondary-bg);
  font-size: .75rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
}
button.ws-ack-count:hover {
  color: var(--bs-body-color);
}
.ws-ack-done .ws-ack-count {
  background: var(--bs-success-bg-subtle);
  color: var(--bs-success-text-emphasis);
}
.ws-ack-mine {
  color: var(--bs-success-text-emphasis);
}
.ws-pin-bar .ws-ack {
  flex: 0 0 auto;
  margin-top: 0;
}
.ws-ack-person {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .3rem 0;
  border-bottom: 1px solid var(--bs-border-color-translucent);
}
/* ── /Workspace ── */
