/* ── dist/main.css ── */
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root,:host{--font-sans:"Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--font-serif:"Georgia", "Times New Roman", Times, serif;--font-mono:"JetBrains Mono", "Fira Code", "SF Mono", Monaco, "Inconsolata", "Roboto Mono", monospace;--color-neutral-200:oklch(92.2% 0 0);--color-neutral-800:oklch(26.9% 0 0);--color-white:#fff;--spacing:.25rem;--breakpoint-sm:40rem;--breakpoint-md:48rem;--breakpoint-lg:64rem;--breakpoint-xl:80rem;--breakpoint-2xl:96rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-lg:1.125rem;--text-xl:1.25rem;--text-2xl:1.5rem;--text-3xl:1.875rem;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-xs:2px;--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-2xl:24px;--shadow-xs:0 1px 2px 0 #0000000a;--shadow-sm:0 1px 3px 0 #0000000f;--shadow-md:0 4px 6px -1px #00000014, 0 2px 4px -2px #0000000f;--shadow-lg:0 10px 15px -3px #00000014, 0 4px 6px -4px #0000000a;--shadow-xl:0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000000f;--shadow-2xl:0 25px 50px -12px #0003;--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-primary:#6366f1;--color-secondary:#10b981;--color-accent:#8b5cf6;--color-success:#10b981;--color-success-dark:#059669;--color-warning:#f59e0b;--color-warning-dark:#d97706;--color-danger:#ef4444;--color-info:#3b82f6;--color-dark:#0f172a;--color-surface:var(--bg-primary);--font-size-xs:.75rem;--font-size-sm:.8125rem;--font-size-base:.875rem;--font-size-md:1rem;--font-size-lg:1.125rem;--font-size-xl:1.25rem;--spacing-xs:.25rem;--spacing-sm:.5rem;--spacing-md:1rem;--spacing-lg:1.5rem;--spacing-xl:2rem;--spacing-2xl:3rem;--radius-full:9999px;--header-height:48px;--sidebar-width:240px;--footer-height:48px;--z-sticky:1100;--z-fixed:1150;--z-widget:1200;--z-sidebar-overlay:1440;--z-sidebar:1450;--z-stream-panel:1550;--z-floating-window:1650;--z-modal-backdrop:2500;--z-modal:2600;--z-dropdown:3200;--z-popover:3300;--z-tooltip:3400;--z-toast:3900;--z-loading:3950;--transition-fast:.15s cubic-bezier(.4, 0, .2, 1);--transition-base:.2s cubic-bezier(.4, 0, .2, 1);--transition-slow:.3s cubic-bezier(.4, 0, .2, 1);--btn-height-xs:28px;--btn-height-sm:36px;--btn-height-md:40px;--btn-height-lg:48px}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:root{color-scheme:light;--bg-primary:#faf9f6;--bg-secondary:#f3f2ee;--bg-tertiary:#eceae4;--bg-elevated:#fff;--bg-hover:#eceae4;--bg-muted:#f3f2ee;--bg-overlay:#0006;--bg-code:#1e1e2e;--text-primary:#1a1a1a;--text-secondary:#5f5e5b;--text-muted:#78746e;--text-light:#a8a6a1;--text-inverse:#fff;--text-code:#e2e8f0;--border-color:#00000014;--border-strong:#00000026;--border-light:#0000000a;--accent-color:#c15f3c;--accent-hover:#a8502f;--accent-subtle:#f5e6df;--status-success:#2e7d5b;--status-success-bg:#e8f5ec;--status-warning:#b8860b;--status-warning-bg:#fef7e0;--status-danger:#c0392b;--status-danger-bg:#fdeceb;--status-info:#2b6cb0;--status-info-bg:#e6f0fa;--shadow-color:0 0 0;--primary:#4f46e5;--primary-dark:#4338ca;--primary-light:#818cf8;--primary-lighter:#a5b4fc;--primary-lightest:#e0e7ff;--secondary:#10b981;--secondary-dark:#059669;--accent:#7c3aed;--accent-light:#f3e8ff;--success:#10b981;--success-dark:#059669;--success-light:#34d399;--success-lighter:#6ee7b7;--success-lightest:#d1fae5;--warning:#f59e0b;--warning-dark:#d97706;--warning-light:#fbbf24;--warning-lighter:#fcd34d;--warning-lightest:#fef3c7;--danger:#ef4444;--danger-dark:#dc2626;--danger-light:#f87171;--danger-lighter:#fca5a5;--danger-lightest:#fee2e2;--info:#3b82f6;--info-dark:#2563eb;--info-light:#60a5fa;--info-lighter:#93c5fd;--info-lightest:#dbeafe;--dark:#0f172a;--gray-900:#111827;--gray-800:#1f2937;--gray-700:#374151;--gray-600:#4b5563;--gray-500:#6b7280;--gray-400:#9ca3af;--gray-300:#d1d5db;--gray-200:#e5e7eb;--gray-100:#f3f4f6;--gray-50:#f9fafb;--white:#fff;--bg-base:var(--bg-primary);--bg-card:var(--bg-elevated);--border:var(--border-color);--border-dark:var(--border-strong);--art-street:#e74c3c;--art-music:#3498db;--art-visual:#9b59b6;--art-design:#2ecc71;--art-event:#f39c12;--art-other:#95a5a6;--shadow-xs:0 1px 2px 0 #0000000d;--shadow-sm:0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a;--shadow:0 1px 3px 0 #0000001a, 0 1px 2px 0 #0000000f;--shadow-md:0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;--shadow-lg:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--shadow-xl:0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a;--shadow-2xl:0 25px 50px -12px #00000040;--shadow-inner:inset 0 2px 4px 0 #0000000d;--header-height:48px;--footer-height:48px;--sidebar-width:240px;--sidebar-left-width:240px;--sidebar-right-width:240px;--sidebar-collapsed:56px;--radius-xs:2px;--radius-sm:4px;--radius:6px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-2xl:24px;--radius-full:9999px;--spacing-xs:.25rem;--spacing-sm:.5rem;--spacing-md:1rem;--spacing-lg:1.5rem;--spacing-xl:2rem;--spacing-2xl:3rem;--spacing-3xl:4rem;--font-sans:"Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--font-mono:"JetBrains Mono", "Fira Code", "SF Mono", Monaco, "Inconsolata", "Roboto Mono", monospace;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-size-xs:.75rem;--font-size-sm:.8125rem;--font-size-base:.875rem;--font-size-md:1rem;--font-size-lg:1.125rem;--font-size-xl:1.25rem;--transition:all .2s cubic-bezier(.4, 0, .2, 1);--transition-fast:all .15s cubic-bezier(.4, 0, .2, 1);--transition-slow:all .3s cubic-bezier(.4, 0, .2, 1);--z-sticky:1100;--z-fixed:1150;--z-widget:1200;--z-sidebar-overlay:1440;--z-sidebar:1450;--z-stream-panel:1550;--z-floating-window:1650;--z-modal-backdrop:2500;--z-modal:2600;--z-dropdown:3200;--z-popover:3300;--z-tooltip:3400;--z-toast:3900;--z-loading:3950;--z-debug-panel:2800;--breakpoint-sm:640px;--breakpoint-md:768px;--breakpoint-lg:1024px;--breakpoint-xl:1280px;--breakpoint-2xl:1536px;--btn-height-xs:28px;--btn-height-sm:36px;--btn-height-md:44px;--btn-height-lg:52px;--btn-padding-xs:.375rem .75rem;--btn-padding-sm:.5rem 1rem;--btn-padding-md:.75rem 1.25rem;--btn-padding-lg:1rem 1.5rem;--btn-font-xs:.75rem;--btn-font-sm:.8125rem;--btn-font-md:.875rem;--btn-font-lg:1rem}:root[data-theme=dark]{color-scheme:dark;--bg-primary:#1a1a1a;--bg-secondary:#242422;--bg-tertiary:#2e2e2c;--bg-elevated:#333331;--bg-hover:#3a3a38;--bg-muted:#2e2e2c;--bg-overlay:#0009;--bg-code:#0f172a;--text-primary:#e8e6e1;--text-secondary:#a8a6a1;--text-muted:#6b6a66;--text-light:#4a4a47;--text-inverse:#1a1a1a;--text-code:#f1f5f9;--border-color:#ffffff14;--border-strong:#ffffff26;--accent-color:#da7756;--accent-hover:#e8906e;--accent-subtle:#3a2a22;--status-success:#34d399;--status-success-bg:#1a2e25;--status-warning:#fbbf24;--status-warning-bg:#2e2815;--status-danger:#f87171;--status-danger-bg:#2e1a1a;--status-info:#60a5fa;--status-info-bg:#1a2535;--shadow-color:0 0 0;--bg-base:var(--bg-primary);--bg-card:var(--bg-elevated);--border:var(--border-color);--border-dark:var(--border-strong);--border-light:#ffffff0a;--gray-50:#2e2e2c;--gray-100:#333331;--gray-200:#3a3a38;--gray-300:#4a4a47;--gray-400:#6b6a66;--gray-500:#9b9a97;--gray-600:#a8a6a1;--gray-700:#c4c3bf;--gray-800:#e0dfdb;--gray-900:#e8e6e1;--dark:#e8e6e1;--white:#1a1a1a;--shadow-xs:0 1px 2px 0 #00000026;--shadow-sm:0 1px 3px 0 #0003, 0 1px 2px -1px #00000026;--shadow:0 1px 3px 0 #0003, 0 1px 2px 0 #00000026;--shadow-md:0 4px 6px -1px #00000040, 0 2px 4px -2px #0003;--shadow-lg:0 10px 15px -3px #0000004d, 0 4px 6px -4px #0003;--shadow-xl:0 20px 25px -5px #00000059, 0 8px 10px -6px #00000040;--shadow-2xl:0 25px 50px -12px #00000080;--shadow-inner:inset 0 2px 4px 0 #00000026}@media(prefers-color-scheme:dark){:root:not([data-theme=light]){color-scheme:dark;--bg-primary:#1a1a1a;--bg-secondary:#242422;--bg-tertiary:#2e2e2c;--bg-elevated:#333331;--bg-hover:#3a3a38;--bg-muted:#2e2e2c;--bg-overlay:#0009;--bg-code:#0f172a;--text-primary:#e8e6e1;--text-secondary:#a8a6a1;--text-muted:#6b6a66;--text-light:#4a4a47;--text-inverse:#1a1a1a;--text-code:#f1f5f9;--border-color:#ffffff14;--border-strong:#ffffff26;--border-light:#ffffff0a;--accent-color:#da7756;--accent-hover:#e8906e;--accent-subtle:#3a2a22;--status-success:#34d399;--status-success-bg:#1a2e25;--status-warning:#fbbf24;--status-warning-bg:#2e2815;--status-danger:#f87171;--status-danger-bg:#2e1a1a;--status-info:#60a5fa;--status-info-bg:#1a2535;--bg-base:#1a1a1a;--bg-card:#333331;--border:#ffffff14;--border-dark:#ffffff26;--gray-50:#2e2e2c;--gray-100:#333331;--gray-200:#3a3a38;--gray-300:#4a4a47;--gray-400:#6b6a66;--gray-500:#9b9a97;--gray-600:#a8a6a1;--gray-700:#c4c3bf;--gray-800:#e0dfdb;--gray-900:#e8e6e1;--dark:#e8e6e1;--white:#1a1a1a;--shadow-xs:0 1px 2px 0 #00000026;--shadow-sm:0 1px 3px 0 #0003, 0 1px 2px -1px #00000026;--shadow:0 1px 3px 0 #0003, 0 1px 2px 0 #00000026;--shadow-md:0 4px 6px -1px #00000040, 0 2px 4px -2px #0003;--shadow-lg:0 10px 15px -3px #0000004d, 0 4px 6px -4px #0003;--shadow-xl:0 20px 25px -5px #00000059, 0 8px 10px -6px #00000040;--shadow-2xl:0 25px 50px -12px #00000080;--shadow-inner:inset 0 2px 4px 0 #00000026}}@media(prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}html{font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizelegibility;font-size:16px}body{background-color:var(--bg-primary);color:var(--text-primary);line-height:1.5}body,body *{transition:background-color .2s,border-color .2s,color .15s,box-shadow .2s}canvas,video,iframe,svg,img,.no-transition,.no-transition *,.leaflet-tile-pane *,.maplibregl-map *,[class*=chart],[class*=vis-],.gridstack-item-content{transition:none!important}h1,h2,h3,h4,h5,h6{color:var(--text-primary);letter-spacing:-.01em;font-weight:600;line-height:1.3}a{color:var(--primary);text-decoration:none;transition:color .15s}a:hover{color:var(--primary-dark)}:focus-visible{outline:2px solid var(--primary);outline-offset:2px;border-radius:2px}[role=button]{cursor:pointer}[role=button]:focus-visible{outline:2px solid var(--primary);outline-offset:2px;border-radius:var(--radius-sm,4px);box-shadow:0 0 0 4px var(--primary)}@supports (color:color-mix(in lab,red,red)){[role=button]:focus-visible{box-shadow:0 0 0 4px color-mix(in srgb,var(--primary) 15%,transparent)}}*{scrollbar-width:thin;scrollbar-color:var(--border-strong) transparent}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}pre,code{font-family:var(--font-mono)}pre{background:var(--bg-code);color:var(--text-code);border-radius:var(--radius-md);padding:.75rem 1rem;font-size:.8125rem;line-height:1.6;overflow-x:auto}code{background:var(--bg-tertiary);color:var(--text-primary);border-radius:var(--radius-sm);padding:.125rem .375rem;font-size:.875em}pre code{color:inherit;font-size:inherit;background:0 0;border-radius:0;padding:0}input,select,textarea{color:var(--text-primary);background-color:var(--bg-elevated);border:1px solid var(--border-color);border-radius:var(--radius-md,8px);font-family:inherit;font-size:.875rem}select:not([class]),input:not([class]):not([type=hidden]):not([type=checkbox]):not([type=radio]){min-height:36px;padding:.5rem .75rem}textarea:not([class]){resize:vertical;min-height:80px;padding:.5rem .75rem}input::placeholder,textarea::placeholder{color:var(--text-muted)}input:focus,select:focus,textarea:focus{border-color:var(--primary);box-shadow:0 0 0 2px var(--primary)}@supports (color:color-mix(in lab,red,red)){input:focus,select:focus,textarea:focus{box-shadow:0 0 0 2px color-mix(in srgb,var(--primary) 15%,transparent)}}input[type=range],input[type=file],input[type=color],input[type=checkbox],input[type=radio]{background-color:initial;border-color:initial;border-radius:initial;box-shadow:initial;min-height:initial;padding:initial}::selection{background:var(--primary)}@supports (color:color-mix(in lab,red,red)){::selection{background:color-mix(in srgb,var(--primary) 25%,transparent)}}::selection{color:var(--text-primary)}}@layer components{.card-pro{background:var(--bg-elevated);border:1px solid var(--border-color);border-radius:var(--radius-lg);box-shadow:var(--shadow-sm);padding:1.25rem;transition:box-shadow .2s,border-color .2s}.card-pro:hover{box-shadow:var(--shadow-md);border-color:var(--border-strong)}.card-pro .card-pro-title{color:var(--text-primary);margin-bottom:.5rem;font-size:.875rem;font-weight:600}.card-pro .card-pro-subtitle{color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:.75rem;font-size:.75rem}.btn-pro{border-radius:var(--radius-md);cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;border:1px solid #0000;justify-content:center;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.8125rem;font-weight:500;line-height:1.5;transition:all .15s;display:inline-flex}.btn-pro:disabled{opacity:.5;cursor:not-allowed}.btn-pro-primary{background:var(--primary);color:var(--text-inverse)}.btn-pro-primary:hover:not(:disabled){background:var(--primary-dark);box-shadow:var(--shadow-sm)}.btn-pro-secondary{background:var(--bg-elevated);color:var(--text-primary);border-color:var(--border-color)}.btn-pro-secondary:hover:not(:disabled){background:var(--bg-hover);border-color:var(--border-strong)}.btn-pro-ghost{color:var(--text-secondary);background:0 0}.btn-pro-ghost:hover:not(:disabled){background:var(--bg-hover);color:var(--text-primary)}.btn-pro-danger{background:var(--danger);color:var(--text-inverse)}.btn-pro-danger:hover:not(:disabled){background:var(--danger-dark)}.input-pro{width:100%;min-height:var(--btn-height-md,44px);border:1px solid var(--border-color);border-radius:var(--radius-md);background:var(--bg-elevated);color:var(--text-primary);padding:.625rem .875rem;font-size:.875rem;line-height:1.5;transition:border-color .15s,box-shadow .15s;display:block}.input-pro::placeholder{color:var(--text-muted)}.input-pro:focus{border-color:var(--primary);box-shadow:0 0 0 3px var(--primary)}@supports (color:color-mix(in lab,red,red)){.input-pro:focus{box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 15%,transparent)}}.input-pro:disabled,.input-pro[readonly]{opacity:.6;cursor:not-allowed;background:var(--bg-secondary)}textarea.input-pro{resize:vertical;min-height:100px}select.input-pro{cursor:pointer}.input-pro-label{color:var(--text-secondary);margin-bottom:.375rem;font-size:.8125rem;font-weight:500;display:block}.badge-pro{border-radius:var(--radius-full);text-transform:uppercase;letter-spacing:.04em;align-items:center;gap:.25rem;padding:.125rem .5rem;font-size:.75rem;font-weight:600;display:inline-flex}.badge-pro-success{background:var(--success)}@supports (color:color-mix(in lab,red,red)){.badge-pro-success{background:color-mix(in srgb,var(--success) 15%,var(--bg-primary))}}.badge-pro-success{color:var(--success)}.badge-pro-warning{background:var(--warning)}@supports (color:color-mix(in lab,red,red)){.badge-pro-warning{background:color-mix(in srgb,var(--warning) 15%,var(--bg-primary))}}.badge-pro-warning{color:var(--warning)}.badge-pro-danger{background:var(--danger)}@supports (color:color-mix(in lab,red,red)){.badge-pro-danger{background:color-mix(in srgb,var(--danger) 15%,var(--bg-primary))}}.badge-pro-danger{color:var(--danger)}.badge-pro-info{background:var(--info)}@supports (color:color-mix(in lab,red,red)){.badge-pro-info{background:color-mix(in srgb,var(--info) 15%,var(--bg-primary))}}.badge-pro-info{color:var(--info)}.badge-pro-neutral{background:var(--bg-tertiary);color:var(--text-secondary)}.divider{border:none;border-top:1px solid var(--border-color);margin:1rem 0}.empty-state-pro{color:var(--text-muted);text-align:center;flex-direction:column;justify-content:center;align-items:center;padding:3rem 1.5rem;display:flex}.empty-state-pro-icon{opacity:.5;margin-bottom:.75rem;font-size:2rem}.empty-state-pro-title{color:var(--text-secondary);margin-bottom:.25rem;font-size:.9375rem;font-weight:600}.empty-state-pro-description{max-width:280px;font-size:.8125rem}.table-pro{border-collapse:collapse;width:100%}.table-pro th{text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);border-bottom:1px solid var(--border-color);text-align:left;padding:.625rem .75rem;font-size:.75rem;font-weight:600}.table-pro td{color:var(--text-primary);border-bottom:1px solid var(--border-light);padding:.625rem .75rem;font-size:.8125rem}.table-pro tbody tr:hover{background:var(--bg-hover)}:root[data-theme=dark] .form-control,:root[data-theme=dark] .form-select{background-color:var(--bg-secondary);color:var(--text-primary);border-color:var(--border-color)}:root[data-theme=dark] .form-control:focus,:root[data-theme=dark] .form-select:focus{background-color:var(--bg-secondary);color:var(--text-primary);border-color:var(--primary);box-shadow:0 0 0 3px var(--primary)}@supports (color:color-mix(in lab,red,red)){:root[data-theme=dark] .form-control:focus,:root[data-theme=dark] .form-select:focus{box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 15%,transparent)}}:root[data-theme=dark] .form-control::placeholder{color:var(--text-muted)}:root[data-theme=dark] .form-label{color:var(--text-secondary)}:root[data-theme=dark] .form-text{color:var(--text-muted)}:root[data-theme=dark] .form-check-input{background-color:var(--bg-tertiary);border-color:var(--border-strong)}:root[data-theme=dark] .form-check-input:checked{background-color:var(--primary);border-color:var(--primary)}:root[data-theme=dark] .card{background-color:var(--bg-elevated);border-color:var(--border-color);color:var(--text-primary)}:root[data-theme=dark] .card-header{background-color:var(--bg-secondary);border-color:var(--border-color);color:var(--text-primary)}:root[data-theme=dark] .card-body{color:var(--text-primary)}:root[data-theme=dark] .card-footer{background-color:var(--bg-secondary);border-color:var(--border-color)}:root[data-theme=dark] .table{color:var(--text-primary);border-color:var(--border-color)}:root[data-theme=dark] .table>thead>tr>th{background-color:var(--bg-secondary);border-color:var(--border-color);color:var(--text-secondary)}:root[data-theme=dark] .table>tbody>tr>td{border-color:var(--border-color)}:root[data-theme=dark] .table-striped>tbody>tr:nth-of-type(odd){background-color:var(--bg-secondary);color:var(--text-primary)}:root[data-theme=dark] .table-hover>tbody>tr:hover{background-color:var(--bg-hover);color:var(--text-primary)}:root[data-theme=dark] .modal-content{background-color:var(--bg-elevated);border-color:var(--border-color);color:var(--text-primary)}:root[data-theme=dark] .modal-header,:root[data-theme=dark] .modal-footer{border-color:var(--border-color)}:root[data-theme=dark] .dropdown-menu{background-color:var(--bg-elevated);border-color:var(--border-color);color:var(--text-primary)}:root[data-theme=dark] .dropdown-item{color:var(--text-primary)}:root[data-theme=dark] .dropdown-item:hover,:root[data-theme=dark] .dropdown-item:focus{background-color:var(--bg-hover);color:var(--text-primary)}:root[data-theme=dark] .dropdown-divider,:root[data-theme=dark] .nav-tabs{border-color:var(--border-color)}:root[data-theme=dark] .nav-tabs .nav-link{color:var(--text-secondary)}:root[data-theme=dark] .nav-tabs .nav-link.active{background-color:var(--bg-elevated);border-color:var(--border-color) var(--border-color) var(--bg-elevated);color:var(--text-primary)}:root[data-theme=dark] .alert{color:var(--text-primary)}:root[data-theme=dark] .alert-info{background-color:var(--info)}@supports (color:color-mix(in lab,red,red)){:root[data-theme=dark] .alert-info{background-color:color-mix(in srgb,var(--info) 15%,var(--bg-primary))}}:root[data-theme=dark] .alert-info{border-color:var(--info)}@supports (color:color-mix(in lab,red,red)){:root[data-theme=dark] .alert-info{border-color:color-mix(in srgb,var(--info) 30%,transparent)}}:root[data-theme=dark] .alert-info{color:var(--info)}:root[data-theme=dark] .alert-warning{background-color:var(--warning)}@supports (color:color-mix(in lab,red,red)){:root[data-theme=dark] .alert-warning{background-color:color-mix(in srgb,var(--warning) 15%,var(--bg-primary))}}:root[data-theme=dark] .alert-warning{border-color:var(--warning)}@supports (color:color-mix(in lab,red,red)){:root[data-theme=dark] .alert-warning{border-color:color-mix(in srgb,var(--warning) 30%,transparent)}}:root[data-theme=dark] .alert-warning{color:var(--warning)}:root[data-theme=dark] .alert-danger{background-color:var(--danger)}@supports (color:color-mix(in lab,red,red)){:root[data-theme=dark] .alert-danger{background-color:color-mix(in srgb,var(--danger) 15%,var(--bg-primary))}}:root[data-theme=dark] .alert-danger{border-color:var(--danger)}@supports (color:color-mix(in lab,red,red)){:root[data-theme=dark] .alert-danger{border-color:color-mix(in srgb,var(--danger) 30%,transparent)}}:root[data-theme=dark] .alert-danger{color:var(--danger)}:root[data-theme=dark] .alert-success{background-color:var(--success)}@supports (color:color-mix(in lab,red,red)){:root[data-theme=dark] .alert-success{background-color:color-mix(in srgb,var(--success) 15%,var(--bg-primary))}}:root[data-theme=dark] .alert-success{border-color:var(--success)}@supports (color:color-mix(in lab,red,red)){:root[data-theme=dark] .alert-success{border-color:color-mix(in srgb,var(--success) 30%,transparent)}}:root[data-theme=dark] .alert-success{color:var(--success)}:root[data-theme=dark] .alert-light{background-color:var(--bg-secondary);border-color:var(--border-color);color:var(--text-primary)}:root[data-theme=dark] .list-group-item{background-color:var(--bg-elevated);border-color:var(--border-color);color:var(--text-primary)}:root[data-theme=dark] .badge.bg-light{background-color:var(--bg-tertiary)!important;color:var(--text-primary)!important}:root[data-theme=dark] .text-dark{color:var(--text-primary)!important}:root[data-theme=dark] .text-muted{color:var(--text-muted)!important}:root[data-theme=dark] .bg-light{background-color:var(--bg-secondary)!important}:root[data-theme=dark] .bg-white{background-color:var(--bg-elevated)!important}:root[data-theme=dark] .border,:root[data-theme=dark] .border-bottom,:root[data-theme=dark] .border-top{border-color:var(--border-color)!important}:root[data-theme=dark] .accordion-button{background-color:var(--bg-secondary);color:var(--text-primary)}:root[data-theme=dark] .accordion-body{background-color:var(--bg-elevated)}:root[data-theme=dark] .breadcrumb{background-color:#0000}:root[data-theme=dark] .breadcrumb-item+.breadcrumb-item:before,:root[data-theme=dark] .breadcrumb-item.active{color:var(--text-muted)}:root[data-theme=dark] .offcanvas{background-color:var(--bg-elevated);color:var(--text-primary)}:root[data-theme=dark] .btn-close{filter:invert()grayscale()brightness(200%)}:root[data-theme=dark] hr{border-color:var(--border-color);opacity:1}@media(prefers-color-scheme:dark){:root:not([data-theme=light]) .form-control,:root:not([data-theme=light]) .form-select{background-color:var(--bg-secondary);color:var(--text-primary);border-color:var(--border-color)}:root:not([data-theme=light]) .form-control:focus,:root:not([data-theme=light]) .form-select:focus{background-color:var(--bg-secondary);color:var(--text-primary);border-color:var(--primary);box-shadow:0 0 0 3px var(--primary)}@supports (color:color-mix(in lab,red,red)){:root:not([data-theme=light]) .form-control:focus,:root:not([data-theme=light]) .form-select:focus{box-shadow:0 0 0 3px color-mix(in srgb,var(--primary) 15%,transparent)}}:root:not([data-theme=light]) .card{background-color:var(--bg-elevated);border-color:var(--border-color);color:var(--text-primary)}:root:not([data-theme=light]) .table{color:var(--text-primary);border-color:var(--border-color)}:root:not([data-theme=light]) .modal-content{background-color:var(--bg-elevated);border-color:var(--border-color);color:var(--text-primary)}:root:not([data-theme=light]) .dropdown-menu{background-color:var(--bg-elevated);border-color:var(--border-color)}:root:not([data-theme=light]) .dropdown-item{color:var(--text-primary)}:root:not([data-theme=light]) .dropdown-item:hover{background-color:var(--bg-hover)}:root:not([data-theme=light]) .alert-info{background-color:var(--info)}@supports (color:color-mix(in lab,red,red)){:root:not([data-theme=light]) .alert-info{background-color:color-mix(in srgb,var(--info) 15%,var(--bg-primary))}}:root:not([data-theme=light]) .alert-info{color:var(--info)}:root:not([data-theme=light]) .alert-warning{background-color:var(--warning)}@supports (color:color-mix(in lab,red,red)){:root:not([data-theme=light]) .alert-warning{background-color:color-mix(in srgb,var(--warning) 15%,var(--bg-primary))}}:root:not([data-theme=light]) .alert-warning{color:var(--warning)}:root:not([data-theme=light]) .alert-danger{background-color:var(--danger)}@supports (color:color-mix(in lab,red,red)){:root:not([data-theme=light]) .alert-danger{background-color:color-mix(in srgb,var(--danger) 15%,var(--bg-primary))}}:root:not([data-theme=light]) .alert-danger{color:var(--danger)}:root:not([data-theme=light]) .alert-success{background-color:var(--success)}@supports (color:color-mix(in lab,red,red)){:root:not([data-theme=light]) .alert-success{background-color:color-mix(in srgb,var(--success) 15%,var(--bg-primary))}}:root:not([data-theme=light]) .alert-success{color:var(--success)}:root:not([data-theme=light]) .btn-close{filter:invert()grayscale()brightness(200%)}:root:not([data-theme=light]) hr{border-color:var(--border-color);opacity:1}}.skeleton{background:linear-gradient(90deg,var(--bg-tertiary) 25%,var(--bg-hover) 50%,var(--bg-tertiary) 75%);border-radius:var(--radius-sm);background-size:200% 100%;animation:1.5s ease-in-out infinite skeleton-pulse}@keyframes skeleton-pulse{0%{background-position:200% 0}to{background-position:-200% 0}}}@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.visible\!{visibility:visible!important}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.\!start{inset-inline-start:var(--spacing)!important}.start{inset-inline-start:var(--spacing)}.start\!{inset-inline-start:var(--spacing)!important}.start-100{inset-inline-start:calc(var(--spacing) * 100)}.end{inset-inline-end:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.top-1{top:calc(var(--spacing) * 1)}.top-3{top:calc(var(--spacing) * 3)}.top-5{top:calc(var(--spacing) * 5)}.top-6{top:calc(var(--spacing) * 6)}.top-10{top:calc(var(--spacing) * 10)}.top-20{top:calc(var(--spacing) * 20)}.top-25{top:calc(var(--spacing) * 25)}.top-1000{top:calc(var(--spacing) * 1000)}.isolate{isolation:isolate}.col-4{grid-column:4}.col-6{grid-column:6}.col-12{grid-column:12}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing) * 0)}.m-2{margin:calc(var(--spacing) * 2)}.m-3{margin:calc(var(--spacing) * 3)}.my-3{margin-block:calc(var(--spacing) * 3)}.ms-1{margin-inline-start:calc(var(--spacing) * 1)}.ms-2{margin-inline-start:calc(var(--spacing) * 2)}.ms-4{margin-inline-start:calc(var(--spacing) * 4)}.ms-auto{margin-inline-start:auto}.me-1{margin-inline-end:calc(var(--spacing) * 1)}.me-2{margin-inline-end:calc(var(--spacing) * 2)}.me-3{margin-inline-end:calc(var(--spacing) * 3)}.me-auto{margin-inline-end:auto}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-lg{margin-top:var(--spacing-lg)}.mt-md{margin-top:var(--spacing-md)}.mt-sm{margin-top:var(--spacing-sm)}.mt-xs{margin-top:var(--spacing-xs)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-0{margin-bottom:calc(var(--spacing) * 0)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-lg{margin-bottom:var(--spacing-lg)}.mb-md{margin-bottom:var(--spacing-md)}.mb-sm{margin-bottom:var(--spacing-sm)}.ml-2{margin-left:calc(var(--spacing) * 2)}.content-grid{grid-template-columns:minmax(0,960px);justify-content:center;padding-inline:1rem;display:grid}.\!hidden{display:none!important}.block{display:block}.contents{display:contents}.flex{display:flex}.flow-root{display:flow-root}.grid{display:grid}.grid\!{display:grid!important}.hidden{display:none}.hidden\!{display:none!important}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.inline-table{display:inline-table}.list-item{display:list-item}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-column{display:table-column}.table-column-group{display:table-column-group}.table-footer-group{display:table-footer-group}.table-header-group{display:table-header-group}.table-row{display:table-row}.table-row-group{display:table-row-group}.h-100{height:calc(var(--spacing) * 100)}.h-full{height:100%}.w-100{width:calc(var(--spacing) * 100)}.w-auto{width:auto}.flex-shrink,.shrink{flex-shrink:1}.flex-grow,.flex-grow-1,.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.resize{resize:both}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-dark{border-color:var(--color-dark)}.border-info{border-color:var(--color-info)}.border-primary{border-color:var(--color-primary)}.border-secondary{border-color:var(--color-secondary)}.border-success{border-color:var(--color-success)}.border-warning{border-color:var(--color-warning)}.bg-accent{background-color:var(--color-accent)}.bg-danger{background-color:var(--color-danger)}.bg-dark{background-color:var(--color-dark)}.bg-info{background-color:var(--color-info)}.bg-neutral-200{background-color:var(--color-neutral-200)}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.bg-success{background-color:var(--color-success)}.bg-warning{background-color:var(--color-warning)}.bg-white{background-color:var(--color-white)}.p-0{padding:calc(var(--spacing) * 0)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.ps-3{padding-inline-start:calc(var(--spacing) * 3)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-end{text-align:end}.text-justify{text-align:justify}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-nowrap{text-wrap:nowrap}.text-wrap{text-wrap:wrap}.break-all{word-break:break-all}.text-danger{color:var(--color-danger)}.text-dark{color:var(--color-dark)}.text-info{color:var(--color-info)}.text-primary{color:var(--color-primary)}.text-secondary{color:var(--color-secondary)}.text-success{color:var(--color-success)}.text-warning{color:var(--color-warning)}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.overline{text-decoration-line:overline}.overline\!{text-decoration-line:overline!important}.underline{text-decoration-line:underline}.underline\!{text-decoration-line:underline!important}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-50{opacity:.5}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow\!{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a)!important;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)!important}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.grayscale{--tw-grayscale:grayscale(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.sepia{--tw-sepia:sepia(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter\!{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)!important}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-all{-webkit-user-select:all;user-select:all}.\[h\:7\]{h:7}.\[notify\:error\]{notify:error}.\[notify\:info\]{notify:info}.\[notify\:success\]{notify:success}.\[p\:42\]{p:42}.\[program\:citydialogue\]{program:citydialogue}.\[timeout\:180\]{timeout:180}.block-full{block-size:100%}.before\:transform:before{content:var(--tw-content);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}@media(prefers-color-scheme:dark){.dark\:bg-neutral-800{background-color:var(--color-neutral-800)}}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}
/* ── css/variables.css ── */
/**
 * Shared Design Tokens (Global CSS Variables)
 * Single source of truth for palette, spacing, typography and stacking.
 *
 * IMPORTANT:
 * - Keep this file loaded BEFORE any other app styles.
 * - Do not redefine these variables in `main.css` / `dashboard-base.css`.
 * - Dashboard-specific files may override tokens (rare, intentional).
 */

:root {
  /* ======================================================================
     Primary palette (canonical)
     ====================================================================== */
  --primary: #4f46e5; /* §7.2: indigo-600 — WCAG AA; было #6366f1 (3.75:1, провал) */
  --primary-dark: #4338ca;
  --primary-light: #818cf8;
  --primary-lighter: #a5b4fc;
  --primary-lightest: #e0e7ff;

  /* Secondary / accent */
  --secondary: #10b981;
  --secondary-dark: #059669;
  --accent: #7c3aed; /* §7.2: violet-600 — WCAG AA; было #8b5cf6 (4.23:1, провал) */
  --accent-light: #f3e8ff;

  /* Status colors */
  --success: #10b981;
  --success-dark: #059669;
  --success-light: #34d399;
  --success-lighter: #6ee7b7;
  --success-lightest: #d1fae5;

  --warning: #f59e0b;
  --warning-dark: #d97706;
  --warning-light: #fbbf24;
  --warning-lighter: #fcd34d;
  --warning-lightest: #fef3c7;

  --danger: #ef4444;
  --danger-dark: #dc2626;
  --danger-light: #f87171;
  --danger-lighter: #fca5a5;
  --danger-lightest: #fee2e2;

  --info: #3b82f6;
  --info-dark: #2563eb;
  --info-light: #60a5fa;
  --info-lighter: #93c5fd;
  --info-lightest: #dbeafe;

  /* ======================================================================
     Grays
     ====================================================================== */
  --dark: #0f172a;
  --gray-900: #111827;
  --gray-800: #1f2937;
  --gray-700: #374151;
  --gray-600: #4b5563;
  --gray-500: #6b7280;
  --gray-400: #9ca3af;
  --gray-300: #d1d5db;
  --gray-200: #e5e7eb;
  --gray-100: #f3f4f6;
  --gray-50: #f9fafb;
  --white: #ffffff;

  /* ======================================================================
     Art type colors (used by cultural map)
     ====================================================================== */
  --art-street: #e74c3c;
  --art-music: #3498db;
  --art-visual: #9b59b6;
  --art-design: #2ecc71;
  --art-event: #f39c12;
  --art-other: #95a5a6;

  /* ======================================================================
     Backgrounds
     ====================================================================== */
  --bg-primary: #ffffff;
  --bg-secondary: #f9fafb;
  --bg-tertiary: #f3f4f6;
  --bg-hover: #f3f4f6;
  --bg-muted: #f3f4f6;
  --bg-base: #ffffff;
  --bg-card: #ffffff;
  --bg-overlay: rgba(0, 0, 0, 0.5);

  /* Text colors */
  --text-primary: #111827;
  --text-secondary: #4b5563;
  --text-muted: #5e6472; /* §7.2: WCAG AA даже на тонированных фонах (5.5:1 на #f6f6fe); было #6b7280 (4.49:1 на тинте, провал) */
  --text-light: #d1d5db;

  /* Borders */
  --border: #e5e7eb;
  --border-light: #f3f4f6;
  --border-dark: #d1d5db;

  /* ======================================================================
     Shadows
     ====================================================================== */
  --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
  --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px 0 rgb(0 0 0 / 0.06);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
  --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
  --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);

  /* ======================================================================
     Layout sizes
     ====================================================================== */
  /* ТЗ §6.2 (DS-03 2026-07-16): высота хедера — из ds-токена (56px desktop),
     было 64px. Единый источник: --ds-header-h/-mobile ниже. */
  --header-height: var(--ds-header-h, 56px);
  --footer-height: 50px;
  --sidebar-width: 320px;
  --sidebar-left-width: var(--sidebar-width);
  --sidebar-right-width: var(--sidebar-width);
  --sidebar-collapsed: 64px;

  /* Border radius */
  --radius-xs: 2px;
  --radius-sm: 4px;
  --radius: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-2xl: 24px;
  --radius-full: 9999px;

  /* Spacing scale */
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;
  --spacing-2xl: 3rem;
  --spacing-3xl: 4rem;

  /* Typography */
  --font-sans: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "SF Mono", Monaco, "Inconsolata", "Fira Code", "Fira Mono", "Roboto Mono", monospace;

  --font-weight-normal: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;

  --font-size-xs: 0.75rem;
  --font-size-sm: 0.8125rem;
  --font-size-base: 0.875rem;
  --font-size-md: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;

  /* Transitions */
  --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  --transition-fast: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
  --transition-slow: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

  /* ======================================================================
     Z-index scale (canonical)
     NOTE: values are intentionally spaced to avoid collisions across dashboards.
     Target ranges:
       - Base:            1-999
       - Widgets:         1000-1499
       - Stream panel:    1500-1599
       - Floating windows:1600-1699
       - Modals:          2000-2999
       - Tooltips/dropdowns: 3000-3999
     ====================================================================== */
  --z-sticky: 1100;
  --z-fixed: 1150;
  --z-widget: 1200;
  /* Sidebars (Citizen mobile overlay panels, etc.) */
  --z-sidebar-overlay: 1440;
  --z-sidebar: 1450;
  --z-stream-panel: 1550;
  --z-floating-window: 1650;

  --z-modal-backdrop: 2500;
  --z-modal: 2600;

  --z-dropdown: 3200;
  --z-popover: 3300;
  --z-tooltip: 3400;

  --z-toast: 3900;
  --z-loading: 3950;
  --z-debug-panel: 2800;

  /* Breakpoints (for consistency; JS may read these if needed) */
  --breakpoint-sm: 640px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 1024px;
  --breakpoint-xl: 1280px;
  --breakpoint-2xl: 1536px;

  /* ======================================================================
     Buttons (canonical sizing tokens)
     ====================================================================== */
  --btn-height-xs: 28px;
  --btn-height-sm: 36px;
  --btn-height-md: 44px;
  --btn-height-lg: 52px;

  --btn-padding-xs: 0.375rem 0.75rem;
  --btn-padding-sm: 0.5rem 1rem;
  --btn-padding-md: 0.75rem 1.25rem;
  --btn-padding-lg: 1rem 1.5rem;

  --btn-font-xs: 0.75rem;
  --btn-font-sm: 0.8125rem;
  --btn-font-md: 0.875rem;
  --btn-font-lg: 1rem;

  /* ======================================================================
     Unified Dashboard Shell tokens (--ds-*)
     Источник истины: docs/dashboard_ui_audit_and_tz_2026-07-16.md §6.2.
     Единственное место объявления. AppShell / 3 компоновки
     (overview | workspace | console) обязаны использовать ТОЛЬКО их;
     legacy-токены выше (--header-height, --sidebar-width) остаются для
     немигрированных экранов и удаляются по мере миграции MIG-01..03.
     ====================================================================== */

  /* Каркас */
  --ds-nav-rail-w: 56px;        /* постоянный рельс навигации */
  --ds-nav-overlay-w: 272px;    /* раскрытая навигация — всегда overlay */
  --ds-header-h: 56px;
  --ds-header-h-mobile: 48px;   /* <768px */
  --ds-panel-left-w: 288px;     /* левая панель workspace/console */
  --ds-panel-right-w: 360px;    /* правая контекстная панель / inspector */
  --ds-inspector-max-w: 420px;
  --ds-content-max: 1440px;     /* центр overview-компоновки */
  --ds-reading-max: 800px;      /* нарратив/чтение */

  /* Отступы (шкала 1..7) */
  --ds-space-1: 4px;
  --ds-space-2: 8px;
  --ds-space-3: 12px;
  --ds-space-4: 16px;
  --ds-space-5: 24px;
  --ds-space-6: 32px;
  --ds-space-7: 48px;

  /* Скругления */
  --ds-radius-control: 6px;
  --ds-radius-card: 10px;
  --ds-radius-modal: 12px;

  /* Высоты контролов: 36/40 desktop, ≥44 touch (ТЗ §6.4) */
  --ds-control-sm: 36px;
  --ds-control-md: 40px;
  --ds-control-touch: 44px;

  /* Типографика (ТЗ §6.5): size/line-height/weight.
     Функциональный текст ≥12px; 9–11px запрещён (кроме vendor-атрибуции карт). */
  --ds-font-page-title: 600 24px/32px var(--font-sans);
  --ds-font-section: 600 20px/28px var(--font-sans);
  --ds-font-card-title: 600 16px/24px var(--font-sans);
  --ds-font-ui: 400 14px/20px var(--font-sans);
  --ds-font-reading: 400 16px/25px var(--font-sans);
  --ds-font-caption: 500 12px/16px var(--font-sans);
  --ds-font-kpi: 600 28px/32px var(--font-sans);
  --ds-font-kpi-lg: 600 40px/44px var(--font-sans);

  /* ======================================================================
     Семантические цвета (UX/UI-ТЗ 2026-07-20, §7.2).
     Бренд разведён с семантикой: brand (тёмно-зелёный) — акцент роли;
     info/success/warning/danger — СТАТУС, не бренд. Смысл не кодируется
     ТОЛЬКО цветом (дублировать иконкой/текстом — §7.9 a11y).
     Единственное место объявления; роль может переопределить --ds-brand.
     ====================================================================== */
  --ds-brand:        #173F35;   /* primary тёмно-зелёный — акцент/бренд */
  --ds-brand-hover:  #0f2d26;
  --ds-brand-weak:   #dfeae6;   /* лёгкий зелёный тинт (hover/selected) */
  --ds-surface:      #FAF8F3;   /* тёплый нейтральный фон */
  --ds-surface-raised: #ffffff;
  --ds-surface-hover: #f2f4f7;  /* ховер строк/пунктов общих компонентов */
  --ds-text:         #172033;
  --ds-text-muted:   #667085;
  --ds-border:       #D0D5DD;
  --ds-info:         #2563EB;
  --ds-success:      #16835D;
  --ds-warning:      #B26A00;
  --ds-danger:       #C43D3D;
  /* Мягкие фоны статусов (для чипов/баннеров; текст — соответствующий цвет). */
  --ds-info-bg:      #eaf1fe;
  --ds-success-bg:   #e6f4ee;
  --ds-warning-bg:   #fbf1e3;
  --ds-danger-bg:    #fbeaea;
  --ds-brand-bg:     #e9f0ed;
  /* Контрастный foreground для сплошных семантических фонов. Не заменять
     на #fff: dark-тема осветляет brand/status и требует тёмный текст. */
  --ds-on-brand:      #ffffff;
  --ds-on-info:       #ffffff;
  --ds-on-success:    #ffffff;
  --ds-on-warning:    #172033;
  --ds-on-danger:     #ffffff;
}

/* Роль-тёмная тема ИИ-мэра и т.п. переопределяет --ds-brand локально;
   базовые статус-цвета остаются одинаковыми во всех ролях (§7.2). */



/* ======================================================================
   Тёмная тема для LEGACY-токенов (ТЗ Часть 7 §5).
   ----------------------------------------------------------------------
   ПРОБЛЕМА (доказано Chromium 2026-07-21): app.css переопределяет эти же
   поверхностные/текстовые/бордер-токены под :root[data-theme="dark"], НО внутри
   `@layer base`. Этот variables.css — БЕЗ слоя (unlayered). По правилам
   каскадных слоёв CSS unlayered ВСЕГДА побеждает layered, поэтому светлые
   значения отсюда перебивали тёмные из app.css → страницы на прямых
   legacy-токенах (login/register/admin) в тёмной теме оставались «половинчато
   тёмными» (тёмная карточка + тёмный текст = нечитабельно).
   РЕШЕНИЕ: даём unlayered dark-переопределения ЗДЕСЬ же. Значения зеркалят
   app.css :root[data-theme="dark"] (строки ~358-441) для консистентности.
   Дашборды используют Editorial-токены (--paper/--ink) и этим блоком не
   затрагиваются в поведении (легаси-токены на них и так должны быть тёмными).
   ====================================================================== */
:root[data-theme="dark"] {
  /* Поверхности */
  --bg-primary: #1A1A1A;
  --bg-secondary: #242422;
  --bg-tertiary: #2E2E2C;
  --bg-hover: #2E2E2C;
  --bg-muted: #2E2E2C;
  --bg-base: #1A1A1A;
  --bg-card: #242422;
  --bg-elevated: #333331;
  --bg-overlay: rgba(0, 0, 0, 0.6);

  /* Текст */
  --text-primary: #E8E6E1;
  --text-secondary: #A8A6A1;
  --text-muted: #9D9A93;  /* §7.2 dark: было #6B6A66 (2.3–3.2:1 на тёмных фонах — нечитаемо); стало ≥4.5:1 на #1a1a1a/#242422/#333331 */
  --text-light: #8A877F;  /* §7.2 dark: было #4A4A47 (невидимо); стало 4.85:1 на базовом фоне */

  /* Линии */
  --border: rgba(255, 255, 255, 0.12);
  --border-light: rgba(255, 255, 255, 0.06);
  --border-dark: rgba(255, 255, 255, 0.18);

  /* Тени — насыщеннее под тёмный фон */
  --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.4);
  --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.5);
  --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px 0 rgb(0 0 0 / 0.4);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.5);
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.55), 0 4px 6px -4px rgb(0 0 0 / 0.5);
  --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.6), 0 8px 10px -6px rgb(0 0 0 / 0.55);
  --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.7);
  --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.4);

  /* ====================================================================
     Тёмная тема для СЕМАНТИЧЕСКИХ --ds-* токенов (UX-аудит 2026-07-29).
     --------------------------------------------------------------------
     ПРОБЛЕМА (доказано чтением кода): --ds-* объявлялись ТОЛЬКО в светлом
     :root, тёмных переопределений не было нигде. На них построены ВСЕ 14
     общих компонентов (StatusChip, ContextBar, RoleSwitcher, JobCenter,
     ArtifactCard, RegistryTable, AsyncView, EvidenceDrawer, AIConsent,
     ConfirmDestructive, OverviewTemplate, MapFallback, LineageChain,
     RoleOverview) → в тёмной теме шапка тёмная, а переключатель роли,
     контекст-полоса и центр задач оставались белыми плашками.
     РЕШЕНИЕ: unlayered dark-значения здесь же (тот же приём и та же
     причина, что у legacy-блока выше — unlayered побеждает @layer base).
     Контраст проверен скриптом WCAG: текст ≥ 4.5:1, границы/акценты ≥ 3:1
     на соответствующих поверхностях.
     ==================================================================== */
  /* Бренд: тёмно-зелёный #173F35 на тёмном фоне нечитаем — осветляем. */
  --ds-brand:        #5FBF9B;
  --ds-brand-hover:  #7BD0B1;
  --ds-brand-weak:   #1E3B33;   /* тинт для hover/selected на тёмном */
  --ds-surface:      #1A1A1A;
  --ds-surface-raised: #242422;
  --ds-surface-hover: #2E2E2C;
  --ds-text:         #E8E6E1;
  --ds-text-muted:   #A8A6A1;
  --ds-border:       rgba(255, 255, 255, 0.14);
  /* Статусы: светлее светлотемных, чтобы держать 4.5:1 на #1A1A1A/#242422 */
  --ds-info:         #7FB0FF;
  --ds-success:      #57C79C;
  --ds-warning:      #E3A860;
  --ds-danger:       #F28B8B;
  /* Мягкие фоны статусов — тёмные тинты (текст берёт цвет статуса выше). */
  --ds-info-bg:      #16233D;
  --ds-success-bg:   #12332A;
  --ds-warning-bg:   #33270F;
  --ds-danger-bg:    #3A1E1E;
  --ds-brand-bg:     #16302A;
  /* Светлые dark-theme акценты требуют тёмный foreground (WCAG AA). */
  --ds-on-brand:      #172033;
  --ds-on-info:       #172033;
  --ds-on-success:    #172033;
  --ds-on-warning:    #172033;
  --ds-on-danger:     #172033;
}

/* ═══════════════════════════════════════════════════════════════════════
   ВИДИМЫЙ ФОКУС — unlayered (UX-аудит С-9)
   ═══════════════════════════════════════════════════════════════════════

   Замер на 2026-07-29: 71 правило `outline: none|0` против 43 `:focus-visible`.
   Глобальное кольцо объявлено в app.css внутри `@layer base`, а ролевые
   стили (moderator_dashboard.css, citizen_dashboard.css и др.) идут БЕЗ
   слоя — по правилам каскадных слоёв unlayered ВСЕГДА побеждает layered,
   независимо от специфичности. Итог: кольцо гасилось почти везде, и
   пользователь клавиатуры терял позицию на экране.

   Та же ловушка уже описана выше для тёмной темы — лечим тем же приёмом:
   объявляем кольцо ЗДЕСЬ (unlayered) и последним по порядку.

   `!important` здесь осознан: это a11y-восстановление поверх правил,
   которые гасят фокус целенаправленно. Форсируем ТОЛЬКО outline — если
   компонент рисует своё кольцо через box-shadow, оно остаётся на месте.
   Компоненту, у которого есть СОБСТВЕННЫЙ видимый индикатор фокуса и
   двойное кольцо мешает, — класс `.focus-ring-custom` (opt-out ниже).

   Мышь не затрагиваем: `:focus-visible` срабатывает на клавиатуре и
   ассистивных технологиях, при клике мышью кольца не будет. */

:focus-visible {
  outline: 2px solid var(--ds-brand, var(--primary, #173F35)) !important;
  outline-offset: 2px !important;
}

/* Тёмная тема: бренд-зелёный на тёмном фоне читается хуже — берём светлый
   вариант токена (он уже проверен на контраст в dark-блоке выше). */
:root[data-theme="dark"] :focus-visible {
  outline-color: var(--ds-brand-hover, #7BD0B1) !important;
}

/* Opt-out для компонентов с собственным видимым индикатором фокуса.
   ВАЖНО: применять, только если индикатор ДЕЙСТВИТЕЛЬНО виден (кольцо,
   заливка, сдвиг) — иначе это возврат к невидимому фокусу. */
.focus-ring-custom:focus-visible {
  outline: 2px solid var(--ds-brand, var(--primary)) !important;
  outline-offset: 2px;
}
/* ── css/_palette_refresh_2026.css ── */
/**
 * static/css/_palette_refresh_2026.css
 *
 * Обновление палитры — май 2026. Приглушённые оттенки поверх существующих
 * переменных в variables.css. Сохраняет ПОЛНУЮ совместимость:
 *   • Все классы и компоненты продолжают работать.
 *   • Только переопределяет hex-значения наиболее заметных переменных.
 *   • Цвета подобраны для меньшего "шума" в UI и лучшей читаемости.
 *
 * Подключается ПОСЛЕ variables.css в _base_dashboard.html — поэтому
 * любые компоненты, ссылающиеся на переменные ниже, получают новые
 * значения автоматически.
 *
 * Для отката — закомментировать `<link>` в _base_dashboard.html.
 *
 * Палитра вдохновлена Editorial Calm (см. design-system/tokens/colors.css):
 * приглушённая зелень "охранной грамоты" + тёплые neutral'ы.
 */

:root {
    /* ────────────────────── Primary ──────────────────────
     * Был: indigo #6366f1 (яркий, "сине-фиолетовый ибп")
     * Стал: deep-green #1F3D2B (Editorial Calm accent — "охранная грамота",
     *       чуть менее saturated, лучше для длительного просмотра)
     */
    --primary:         #1F3D2B;
    --primary-dark:    #163021;
    --primary-light:   #355A40;
    --primary-lighter: #6B8B72;
    --primary-lightest:#E8EEEA;

    /* ────────────────────── Accent ──────────────────────
     * Был: violet #8b5cf6
     * Стал: muted teal — спокойнее, лучше совместим с deep-green primary
     */
    --accent:       #4A7C7C;
    --accent-light: #DCE9E9;

    /* ────────────────────── Status colors — slightly muted ──────────────────────
     * Tailwind-яркие saturate'ы → editorial-приглушённые.
     * Tints стали теплее (бумажный оттенок).
     */
    --success:         #2E5E3E;
    --success-dark:    #1F4029;
    --success-light:   #5A8770;
    --success-lightest:#EAF1EC;

    --warning:         #8C5A1A;
    --warning-dark:    #6B4513;
    --warning-light:   #B68440;
    --warning-lightest:#F5EBDA;

    --danger:          #8B2A1F;
    --danger-dark:     #6B1F17;
    --danger-light:    #B65040;
    --danger-lightest: #F4E3DF;

    --info:            #2C5282;
    --info-dark:       #1E3A5F;
    --info-light:      #4A7DB0;
    --info-lightest:   #DCE6F0;

    /* ────────────────────── Surfaces (тёплые neutral'ы) ──────────────────────
     * Был: чистый #ffffff и #f9fafb (холодные).
     * Стал: лёгкий бумажный оттенок (#FBFAF7) — мягче для глаз при долгой работе.
     * НЕ влияет на контраст с текстом — AA сохранён.
     */
    --bg-primary:    #FBFAF7;   /* было: #ffffff (alias --white) */
    --bg-secondary:  #F4F1EA;   /* было: #f9fafb */
    --bg-tertiary:   #ECE7DA;   /* было: #f3f4f6 */
    --bg-elevated:   #FFFFFF;   /* для cards, поверх paper-фона */

    /* Border line: чуть тёплее серого */
    --border:        #D9D3C5;   /* было: #e5e7eb */
    --border-light:  #E6E1D4;   /* было: #f3f4f6 */
    --border-strong: #B8B0A0;

    /* ────────────────────── Text colors (Editorial ink) ──────────────────────
     * Чуть мягче чем чисто-чёрный — лучше для длительного чтения.
     */
    --text-primary:   #1A1A1A;  /* было: #0f172a (slate-900) */
    --text-secondary: #3D3D3D;  /* было: #374151 (gray-700) */
    --text-muted:     #5A5A55;  /* §7.2: WCAG AA на тонированных фонах (5.8:1); было #6B6B66 (4.46:1 на тинте, провал) */
    --text-disabled:  #9B978C;  /* было: #9ca3af (gray-400) */
    --text-inverse:   #FBFAF7;  /* для текста на тёмном фоне */

    /* ────────────────────── Shadows (мягче) ──────────────────────
     * Editorial Calm избегает резких теней. Используем тёплые tints.
     */
    --shadow-sm:  0 1px 2px rgba(26, 26, 26, 0.05);
    --shadow-md:  0 2px 8px rgba(26, 26, 26, 0.06), 0 1px 3px rgba(26, 26, 26, 0.04);
    --shadow-lg:  0 8px 24px rgba(26, 26, 26, 0.08), 0 2px 6px rgba(26, 26, 26, 0.04);
    --shadow-xl:  0 16px 40px rgba(26, 26, 26, 0.10);

    /* Focus ring — приглушённая зелень вместо ярко-синего */
    --focus-ring: 0 0 0 3px rgba(31, 61, 43, 0.20);
}

/* ============================================================
 * Dashboard themes — мягкие фоны для тематических ролей.
 * Не меняем кардинально, только приглушаем существующие.
 * (Каждый dashboard-themes.css добавляет свой акцент поверх.)
 * ============================================================ */

body[data-dashboard-theme] {
    background: var(--bg-primary);
    color: var(--text-secondary);
}

/* ============================================================
 * Smooth transitions для elements которые меняют цвет
 * (button hover, link hover, status changes).
 * Раньше резкие color-jumps делали интерфейс "дёрганным".
 * ============================================================ */

a, button, .btn, [role="button"], [data-toggleable] {
    transition:
        color 150ms ease,
        background-color 150ms ease,
        border-color 150ms ease,
        box-shadow 150ms ease,
        opacity 150ms ease;
}

/* ============================================================
 * Утилитарный класс для скрытия элементов (вместо style="display:none")
 * Используется в HTML где раньше был inline `style="display:none;"`.
 * ============================================================ */

.hidden,
[hidden] {
    display: none !important;
}

/* ============================================================
 * Reduced motion для пользователей с настройкой OS.
 * Editorial Calm избегает агрессивных анимаций.
 * ============================================================ */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}
/* ── css/design-system/tokens/colors.css ── */
/*
 * design-system/tokens/colors.css
 * Editorial Calm — палитра.
 * См. ТЗ редизайна v1.0 раздел 3.1.
 *
 * Активируется через: <body data-theme="editorial"> ... </body>
 * чтобы не конфликтовать с существующей Tailwind-палитрой проекта.
 */

[data-theme="editorial"] {
  /* Поверхности */
  --paper:       #FBFAF7;
  --paper-2:     #F4F1EA;
  --paper-3:     #ECE7DA;
  --paper-edge:  #FFFFFF;

  /* Чернила */
  --ink:    #1A1A1A;   /* заголовки, max-contrast */
  --ink-2:  #3D3D3D;   /* основной body-текст */
  --ink-3:  #5A5A55;   /* §7.2: подписи — WCAG AA даже на тонированных фонах (5.8:1); было #6B6B66 (4.46:1 на тинте, провал) */
  --ink-4:  #9B978C;   /* деактивированный, decorative-only — НЕ AA */

  /* Линейки */
  --rule:        #D9D3C5;
  --rule-2:      #E6E1D4;
  --rule-strong: #1A1A1A;

  /* Акцент — приглушённая зелень "охранной грамоты" */
  --accent:      #1F3D2B;
  --accent-ink:  #FBFAF7;
  --accent-tint: #E8EEEA;

  /* Семантика */
  --good:      #2E5E3E;
  --good-tint: #EAF1EC;
  --warn:      #8C5A1A;
  --warn-tint: #F5EBDA;
  --bad:       #8B2A1F;
  --bad-tint:  #F4E3DF;

  /* Подложки для слоёв карты */
  --map-base:   #EEE9DC;
  --map-water:  #DDE4DD;
  --map-stroke: #C9C2B0;

  /* Поверхность по умолчанию */
  background: var(--paper);
  color: var(--ink-2);
}

/*
 * Тёмный вариант Editorial-палитры (ТЗ Часть 7 §5).
 * Дашборды несут <body data-theme="editorial"> и используют --paper/--ink/--rule
 * НАПРЯМУЮ. app.css :root[data-theme="dark"] переопределяет только legacy
 * --bg-… и --text-… (НЕ писать здесь звёздочку сразу перед слэшем — это
 * закрывает CSS-комментарий и роняет следующее правило, Anti-pattern #2),
 * поэтому без этого блока тёмная тема не доходила до Editorial-компонентов
 * (визуальная проверка Chromium 2026-07-21: фон дашборда оставался
 * «бумажным» #FBFAF7 под data-theme="dark"). Селектор
 * :root[data-theme="dark"] [data-theme="editorial"] (специфичность 0,3,0)
 * перекрывает светлый [data-theme="editorial"] (0,1,0). Значения зеркалят
 * тёмные поверхности/текст из app.css для консистентности.
 */
:root[data-theme="dark"] [data-theme="editorial"] {
  /* Поверхности */
  --paper:       #1A1A1A;
  --paper-2:     #242422;
  --paper-3:     #2E2E2C;
  --paper-edge:  #333331;

  /* Чернила (инверсия) */
  --ink:    #E8E6E1;
  --ink-2:  #C9C6C0;
  --ink-3:  #A8A6A1;
  --ink-4:  #6B6A66;

  /* Линейки */
  --rule:        rgba(255, 255, 255, 0.12);
  --rule-2:      rgba(255, 255, 255, 0.06);
  --rule-strong: #E8E6E1;

  /* Акцент — осветлённая «охранная» зелень для контраста на тёмном */
  --accent:      #6FA382;
  --accent-ink:  #14211A;
  --accent-tint: #24352B;

  /* Семантика (осветлённая под тёмный фон) */
  --good:      #5FB37A;
  --good-tint: #1E2E23;
  --warn:      #D9A25A;
  --warn-tint: #33291A;
  --bad:       #E08A7D;
  --bad-tint:  #33211E;

  /* Подложки карты */
  --map-base:   #242422;
  --map-water:  #1E2A24;
  --map-stroke: #3A3A38;

  background: var(--paper);
  color: var(--ink-2);
}
/* ── css/design-system/tokens/legacy-aliases.css ── */
/*
 * design-system/tokens/legacy-aliases.css
 * Editorial Calm — алиасы LEGACY-переменных на палитру (ТЗ §6.1,
 * docs/dashboard_ui_audit_and_tz_2026-07-16.md; активация — решение
 * user 2026-07-16 по пакету до/после Аналитик/Продюсер/Горожанин).
 *
 * Весь существующий CSS продолжает ссылаться на --bg-… / --text-… / --border /
 * --primary — здесь они перенаправляются на Editorial-палитру из colors.css.
 * (Не писать в комментариях "--bg-*" со слэшем сразу после звёздочки —
 * это закрывает комментарий и убивает следующее правило целиком.)
 * Набор алиасов = РОВНО тот, что был в утверждённом эксперименте; расширение
 * (например --info-семейство) — отдельными шагами под VR.
 *
 * Гейт html:not([data-theme="dark"]) — на дашбордах html жёстко "light"
 * (тёмная тема отключена), но если dark когда-нибудь вернётся, Editorial
 * не должен перекрасить его в «бумагу».
 */

html:not([data-theme="dark"]) body[data-theme="editorial"] {
    /* Поверхности */
    --bg-primary: var(--paper-edge);
    --bg-secondary: var(--paper);
    --bg-tertiary: var(--paper-2);
    --bg-hover: var(--paper-2);
    --bg-elevated: var(--paper-edge);

    /* Линии */
    --border: var(--rule);
    --border-light: var(--rule-2);

    /* Текст */
    --text-primary: var(--ink);
    --text-secondary: var(--ink-2);
    --text-muted: var(--ink-3);

    /* Акцент и семантика */
    --primary: var(--accent);
    --primary-dark: #16301F;
    --primary-light: var(--accent-tint);
    --success: var(--good);
    --warning: var(--warn);
    --danger: var(--bad);
}
/* ── css/design-system/tokens/typography.css ── */
/*
 * design-system/tokens/typography.css
 * Editorial Calm — типографика. ТЗ раздел 3.2.
 *
 * 1 шкала, 7 ступеней, 2 шрифтовых семейства (sans + serif) + mono.
 * Шрифты подключаются через <link> в base-template (Google Fonts):
 *   Inter (400, 500, 600, 700) — sans
 *   Source Serif 4 (600) — serif
 *   JetBrains Mono (500) — mono
 */

[data-theme="editorial"] {
  --font-sans:  'Inter', 'Helvetica Neue', system-ui, sans-serif;
  --font-serif: 'Source Serif 4', 'Charter', 'Georgia', serif;
  --font-mono:  'JetBrains Mono', 'SF Mono', Menlo, monospace;

  /* Шкала размеров основных текстов: pair size/lh */
  --t-display-size: 56px; --t-display-lh: 1.05;  /* serif 600 */
  --t-title-size:   36px; --t-title-lh:   1.15;  /* serif 600 */
  --t-section-size: 24px; --t-section-lh: 1.30;  /* sans  600 */
  --t-body-lg-size: 18px; --t-body-lg-lh: 1.55;  /* sans  400 */
  --t-body-size:    16px; --t-body-lh:    1.55;  /* sans  400 */
  --t-caption-size: 13px; --t-caption-lh: 1.40;  /* sans  500 */
  --t-mono-size:    13px; --t-mono-lh:    1.40;  /* mono  500 */

  /* Шкала для KPI и микро-заголовков */
  --t-kpi-xl-size:  44px;
  --t-kpi-size:     28px;
  --t-eyebrow-size: 11px;

  /* Глобальные шрифтовые feature-settings: tnum для всех чисел,
     ss01 — стилистический сет (если есть в шрифте). */
  font-family: var(--font-sans);
  font-feature-settings: "tnum", "ss01";
  font-size: var(--t-body-size);
  line-height: var(--t-body-lh);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ─── Шкала текстовых утилит ───────────────────────────────────────── */

[data-theme="editorial"] .t-display {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-display-size);
  line-height: var(--t-display-lh);
  color: var(--ink);
  text-wrap: balance;
}

[data-theme="editorial"] .t-title {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-title-size);
  line-height: var(--t-title-lh);
  color: var(--ink);
  text-wrap: balance;
}

[data-theme="editorial"] .t-section {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--t-section-size);
  line-height: var(--t-section-lh);
  color: var(--ink);
  text-wrap: balance;
}

[data-theme="editorial"] .t-body-lg {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: var(--t-body-lg-size);
  line-height: var(--t-body-lg-lh);
  color: var(--ink-2);
  text-wrap: pretty;
}

[data-theme="editorial"] .t-body {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: var(--t-body-size);
  line-height: var(--t-body-lh);
  color: var(--ink-2);
}

[data-theme="editorial"] .t-caption {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--t-caption-size);
  line-height: var(--t-caption-lh);
  color: var(--ink-3);
}

[data-theme="editorial"] .t-mono {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--t-mono-size);
  line-height: var(--t-mono-lh);
  color: var(--ink-2);
}

/* KPI и eyebrow */
[data-theme="editorial"] .t-kpi-xl {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--t-kpi-xl-size);
  line-height: 1.0;
  color: var(--ink);
  font-feature-settings: "tnum";
}

[data-theme="editorial"] .t-kpi {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--t-kpi-size);
  line-height: 1.0;
  color: var(--ink);
  font-feature-settings: "tnum";
}

[data-theme="editorial"] .t-eyebrow {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--t-eyebrow-size);
  line-height: 1.4;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* ── css/design-system/tokens/spacing.css ── */
/*
 * design-system/tokens/spacing.css
 * Editorial Calm — 8-px ритм. ТЗ раздел 3.3.
 */

[data-theme="editorial"] {
  --s-1:  4px;
  --s-2:  8px;
  --s-3:  12px;
  --s-4:  16px;
  --s-5:  24px;
  --s-6:  32px;
  --s-7:  48px;
  --s-8:  64px;
  --s-9:  96px;
  --s-10: 128px;

  /* Семантические отступы — desktop-lg по умолчанию */
  --page-px:     96px;
  --page-py:     64px;
  --gutter:      32px;
  --stack:       24px;
  --stack-tight: 16px;
  --stack-loose: 48px;

  /* Радиусы — Editorial Calm не любит сильно скруглённое */
  --r-0:    0;
  --r-1:    2px;     /* пилюли */
  --r-2:    4px;     /* кнопки, инпуты */
  --r-3:    8px;     /* крупные карточки — редко */
  --r-pill: 999px;

  /* Тени — минимально */
  --shadow-0:     none;
  --shadow-1:     0 1px 0 rgba(26, 26, 26, 0.04);
  --shadow-2:     0 1px 3px rgba(26, 26, 26, 0.06), 0 1px 2px rgba(26, 26, 26, 0.04);
  --shadow-focus: 0 0 0 3px rgba(31, 61, 43, 0.25);

  /* Z-index слои.
     ВНИМАНИЕ: имена --z-sticky / --z-modal / --z-toast уже принадлежат общей
     шкале приложения (app.css, variables.css): 1100 / 2600 / 3900. Этот блок
     объявлен на [data-theme="editorial"], который висит на <body> ВСЕХ
     дашбордов и подключается ПОЗЖЕ — при равной специфичности он побеждал и
     подменял три чужих токена своими 10 / 1000 / 2000.
     Последствия были системные, а не косметические:
       • модалка получала 1000 при своей подложке --z-modal-backdrop 2500 —
         то есть уезжала ПОД собственный оверлей. Карточка отчёта Горожанина
         на весь экран выглядела мутной (её размывал backdrop-filter подложки)
         и не принимала клики: elementFromPoint в центре экрана возвращал
         оверлей (замерено на проде 18.08);
       • тост 2000 оказывался ниже выпадашки 3200 и попапа 3300;
       • sticky 10 вместо 1100.
     Свои имена дизайн-системы (--z-base, --z-overlay) не пересекаются с общей
     шкалой и остаются. Чужие — не переопределяем: шкала слоёв в проекте одна.
     Гейт: tests/test_design_token_layer_collisions.py */
  --z-base:    0;
  --z-overlay: 100;
}

/* Адаптивные отступы — десктоп / планшет / мобайл */
@media (max-width: 1439px) {
  [data-theme="editorial"] {
    --page-px: 64px;
    --page-py: 56px;
    --gutter:  24px;
  }
}
@media (max-width: 1023px) {
  [data-theme="editorial"] {
    --page-px: 32px;
    --page-py: 40px;
    --gutter:  24px;
  }
}
@media (max-width: 767px) {
  [data-theme="editorial"] {
    --page-px: 20px;
    --page-py: 32px;
    --gutter:  16px;
  }
}

/* ─── Сетка ─────────────────────────────────────────────────────────── */

[data-theme="editorial"] .ed-page {
  max-width: 1440px;
  margin: 0 auto;
  padding-inline: var(--page-px);
  padding-block:  var(--page-py);
}

[data-theme="editorial"] .ed-grid-12 {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gutter);
}

/* Семантические утилиты вертикального ритма */
[data-theme="editorial"] .ed-stack       { display: flex; flex-direction: column; gap: var(--stack); }
[data-theme="editorial"] .ed-stack-tight { display: flex; flex-direction: column; gap: var(--stack-tight); }
[data-theme="editorial"] .ed-stack-loose { display: flex; flex-direction: column; gap: var(--stack-loose); }
/* ── css/design-system/tokens/motion.css ── */
/*
 * design-system/tokens/motion.css
 * Editorial Calm — движение. ТЗ раздел 3.7.
 *
 * Любая анимация дольше 300ms запрещена.
 * Никаких easing-кривых вне --ease.
 */

[data-theme="editorial"] {
  --motion-fast: 120ms;
  --motion-base: 180ms;
  --motion-slow: 280ms;
  --ease:        cubic-bezier(0.2, 0.6, 0.2, 1);
}

/* Глобальный safety-net для пользователей с reduced-motion */
@media (prefers-reduced-motion: reduce) {
  [data-theme="editorial"],
  [data-theme="editorial"] *,
  [data-theme="editorial"] *::before,
  [data-theme="editorial"] *::after {
    animation-duration:   0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration:  0.01ms !important;
    scroll-behavior:      auto !important;
  }
}
/* ── css/design-system/components/button.css ── */
/*
 * design-system/components/button.css
 * Editorial Calm — Button. ТЗ раздел 4.1.
 *
 * Варианты:  primary, secondary, ghost, danger
 * Размеры:   sm (32px), md (40px по умолчанию), lg (48px)
 * Состояния: default, hover, active, focus, disabled, loading
 *
 * Использование:
 *   <button class="ed-btn ed-btn--primary">Открыть город</button>
 *   <button class="ed-btn ed-btn--secondary ed-btn--lg">Подробнее</button>
 *   <button class="ed-btn ed-btn--ghost ed-btn--sm">Отменить</button>
 *   <button class="ed-btn ed-btn--danger" disabled>Удалить</button>
 *   <button class="ed-btn ed-btn--primary" data-loading>Запускаю…</button>
 *
 * ЗАПРЕЩЕНО: эмодзи в кнопках. Только Lucide-иконки 16/20/24px,
 * stroke-width: 1.5 (см. ТЗ приложение A).
 */

[data-theme="editorial"] .ed-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  height: 40px;
  padding-inline: var(--s-4);
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  border-radius: var(--r-2);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  user-select: none;
  transition: background-color var(--motion-fast) var(--ease),
              border-color var(--motion-fast) var(--ease),
              color var(--motion-fast) var(--ease);
}

/* Размеры */
[data-theme="editorial"] .ed-btn--sm { height: 32px; padding-inline: var(--s-3); font-size: 13px; }
[data-theme="editorial"] .ed-btn--md { height: 40px; padding-inline: var(--s-4); font-size: 14px; }
[data-theme="editorial"] .ed-btn--lg { height: 48px; padding-inline: var(--s-5); font-size: 15px; }

/* Иконки внутри (Lucide, никаких эмодзи) */
[data-theme="editorial"] .ed-btn svg {
  width: 16px;
  height: 16px;
  stroke-width: 1.5;
  flex-shrink: 0;
}
[data-theme="editorial"] .ed-btn--lg svg { width: 20px; height: 20px; }

/* ─── PRIMARY ──────────────────────────────────────────────────────── */
[data-theme="editorial"] .ed-btn--primary {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
[data-theme="editorial"] .ed-btn--primary:hover:not(:disabled) {
  background: #000;        /* затемнение на ~4% */
  border-color: #000;
}
[data-theme="editorial"] .ed-btn--primary:active:not(:disabled) {
  background: #000;
  transform: translateY(0.5px);
}

/* ─── SECONDARY ────────────────────────────────────────────────────── */
[data-theme="editorial"] .ed-btn--secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
}
[data-theme="editorial"] .ed-btn--secondary:hover:not(:disabled) {
  background: var(--paper-2);
}
[data-theme="editorial"] .ed-btn--secondary:active:not(:disabled) {
  background: var(--paper-3);
}

/* ─── GHOST ────────────────────────────────────────────────────────── */
[data-theme="editorial"] .ed-btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: transparent;
}
[data-theme="editorial"] .ed-btn--ghost:hover:not(:disabled) {
  background: var(--paper-2);
}

/* ─── DANGER ───────────────────────────────────────────────────────── */
[data-theme="editorial"] .ed-btn--danger {
  background: var(--bad);
  color: var(--white);
  border-color: var(--bad);
}
[data-theme="editorial"] .ed-btn--danger:hover:not(:disabled) {
  background: #6E1F17;     /* ~4% темнее */
  border-color: #6E1F17;
}

/* ─── ОБЩИЕ СОСТОЯНИЯ ──────────────────────────────────────────────── */
[data-theme="editorial"] .ed-btn:focus-visible {
   box-shadow: var(--shadow-focus);
}
[data-theme="editorial"] .ed-btn:disabled,
[data-theme="editorial"] .ed-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;     /* ТЗ: именно прозрачность, не серый текст */
}

/* Loading: спиннер слева, текст остаётся.
   Применяется через атрибут [data-loading] для совместимости с
   реактивным state (без перерисовки HTML). */
[data-theme="editorial"] .ed-btn[data-loading]::before {
  content: "";
  width: 14px;
  height: 14px;
  border: 1.5px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: ed-btn-spin 700ms linear infinite;
}
[data-theme="editorial"] .ed-btn[data-loading] { pointer-events: none; }

@keyframes ed-btn-spin {
  to { transform: rotate(360deg); }
}
/* ── css/design-system/components/pill.css ── */
/*
 * design-system/components/pill.css
 * Editorial Calm — StatusPill. ТЗ раздел 4.3.
 *
 * Использование:
 *   <span class="ed-pill ed-pill--good">актив</span>
 *   <span class="ed-pill ed-pill--warn">пауза</span>
 *   <span class="ed-pill ed-pill--bad">сбой</span>
 *   <span class="ed-pill ed-pill--neutral">черновик</span>
 *
 * Спека: height 24, padding 0 10, font 12/500, letter-spacing 0.02em,
 * radius --r-1, фон *-tint, текст семантический, рамка цвет-25%.
 */

[data-theme="editorial"] .ed-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 24px;
  padding: 0 10px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
  line-height: 1;
  border-radius: var(--r-1);
  border-width: 1px;
  border-style: solid;
  white-space: nowrap;
  vertical-align: middle;
}

/* Семантические варианты */
[data-theme="editorial"] .ed-pill--good {
  background: var(--good-tint);
  color: var(--good);
  border-color: rgba(46, 94, 62, 0.25);   /* --good @ 25% */
}
[data-theme="editorial"] .ed-pill--warn {
  background: var(--warn-tint);
  color: var(--warn);
  border-color: rgba(140, 90, 26, 0.25);
}
[data-theme="editorial"] .ed-pill--bad {
  background: var(--bad-tint);
  color: var(--bad);
  border-color: rgba(139, 42, 31, 0.25);
}
[data-theme="editorial"] .ed-pill--neutral {
  background: var(--paper-2);
  color: var(--ink-3);
  border-color: var(--rule);
}

/* Точка-индикатор — отдельный примитив, можно использовать самостоятельно
   или внутри пилюли: <span class="ed-dot ed-dot--good"></span> */
[data-theme="editorial"] .ed-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  vertical-align: middle;
}
[data-theme="editorial"] .ed-dot--good { color: var(--good); }
[data-theme="editorial"] .ed-dot--warn { color: var(--warn); }
[data-theme="editorial"] .ed-dot--bad  { color: var(--bad); }
/* ── css/design-system/components/empty.css ── */
/*
 * design-system/components/empty.css
 * Editorial Calm — EmptyState. ТЗ раздел 4.6.
 *
 * 1 заголовок + 1 описание + опционально 1 действие.
 * НИКАКИХ иллюстраций. Тонкая пунктирная рамка.
 *
 * Использование:
 *   <div class="ed-empty">
 *     <div class="ed-empty__title">Исследования ещё не запущены</div>
 *     <div class="ed-empty__body">Когда появится первое — здесь будет
 *       список с прогрессом и логами.</div>
 *     <button class="ed-btn ed-btn--primary">Создать исследование</button>
 *   </div>
 */

[data-theme="editorial"] .ed-empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-6);
  border: 1px dashed var(--rule);
  border-radius: var(--r-2);
  background: var(--paper);
}

[data-theme="editorial"] .ed-empty__title {
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
  text-wrap: balance;
}

[data-theme="editorial"] .ed-empty__body {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-3);
  text-wrap: pretty;
  max-width: 60ch;
}

/* Центрированный вариант — для больших EmptyState на всю секцию. */
[data-theme="editorial"] .ed-empty--center {
  align-items: center;
  text-align: center;
}
[data-theme="editorial"] .ed-empty--center .ed-empty__body {
  max-width: 50ch;
}
/* ── css/design-system/components/skeleton.css ── */
/*
 * design-system/components/skeleton.css
 * Editorial Calm — Skeleton. ТЗ раздел 4.7.
 *
 * Skeleton РЕЗЕРВИРУЕТ место для финального компонента → нулевой CLS.
 * При prefers-reduced-motion — без shimmer-анимации, статичная плашка.
 *
 * Использование:
 *   <div class="ed-skel ed-skel--text" style="width: 70%"></div>
 *   <div class="ed-skel ed-skel--text" style="width: 90%"></div>
 *   <div class="ed-skel ed-skel--block" style="height: 200px"></div>
 *   <div class="ed-skel ed-skel--circle" style="width:40px;height:40px"></div>
 *
 * ВАЖНО (ТЗ 4.2 + 6.1): не используем "—" как индикатор "грузится".
 * "—" — это empty-state. Loading — это skeleton.
 */

[data-theme="editorial"] .ed-skel {
  display: block;
  background: var(--paper-3);
  border-radius: var(--r-2);
  background-image: linear-gradient(
    90deg,
    var(--paper-3) 0%,
    var(--paper-2) 50%,
    var(--paper-3) 100%
  );
  background-size: 200% 100%;
  animation: ed-skel-shimmer 1500ms ease-in-out infinite;
}

[data-theme="editorial"] .ed-skel--text {
  height: 14px;
  width: 100%;
  margin: 4px 0;
}

[data-theme="editorial"] .ed-skel--block {
  width: 100%;
  height: 100px;
}

[data-theme="editorial"] .ed-skel--circle {
  border-radius: 50%;
}

@keyframes ed-skel-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* Reduced-motion: статичная плашка без анимации */
@media (prefers-reduced-motion: reduce) {
  [data-theme="editorial"] .ed-skel {
    animation: none;
    background-image: none;
    background: var(--paper-3);
  }
}
/* ── css/design-system/components/stat.css ── */
/*
 * design-system/components/stat.css
 * Editorial Calm — Stat (KPI). ТЗ 4.2.
 *
 * 4 канонических состояния:
 *   data    — число + лейбл + опциональная дельта
 *   loading — skeleton-полоска вместо числа, лейбл остаётся
 *   empty   — "—" + микрокопия "Данные ожидаются"
 *   error   — иконка + кнопка "Попробовать снова"
 *
 * Использование:
 *   <div class="ed-stat">
 *     <div class="ed-stat__value">12 408</div>
 *     <div class="ed-stat__label">фактов в БЗ</div>
 *     <div class="ed-stat__delta ed-stat__delta--up">+ 12% за неделю</div>
 *   </div>
 *
 * Loading:
 *   <div class="ed-stat" data-state="loading">
 *     <div class="ed-skel ed-skel--text" style="width: 60%; height: 32px;"></div>
 *     <div class="ed-stat__label">фактов в БЗ</div>
 *   </div>
 *
 * Empty:
 *   <div class="ed-stat" data-state="empty">
 *     <div class="ed-stat__value">—</div>
 *     <div class="ed-stat__label">фактов в БЗ</div>
 *     <div class="ed-stat__hint">Данные ожидаются</div>
 *   </div>
 *
 * Error:
 *   <div class="ed-stat" data-state="error">
 *     <div class="ed-stat__error">
 *       <svg>...</svg>
 *       <span>Не удалось загрузить</span>
 *       <button class="ed-btn ed-btn--ghost ed-btn--sm">Попробовать снова</button>
 *     </div>
 *     <div class="ed-stat__label">фактов в БЗ</div>
 *   </div>
 *
 * ЗАПРЕЩЕНО (ТЗ 4.2):
 *  • Использовать "—" как loading-индикатор. Это empty-state.
 *  • Лейбл ПЕРВЫМ, число вторым. Лейбл ВСЕГДА снизу.
 *  • Дельта без знака. Всегда "+ 12%" или "− 4%".
 */

[data-theme="editorial"] .ed-stat {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-4) 0;
}

[data-theme="editorial"] .ed-stat__value {
  font-family: var(--font-sans);
  font-size: var(--t-kpi-size);
  font-weight: 600;
  line-height: 1;
  color: var(--ink);
  font-feature-settings: "tnum";
}

[data-theme="editorial"] .ed-stat__label {
  font-family: var(--font-sans);
  font-size: var(--t-caption-size);
  font-weight: 500;
  line-height: 1.4;
  color: var(--ink-3);
}

[data-theme="editorial"] .ed-stat__delta {
  font-family: var(--font-mono);
  font-size: var(--t-mono-size);
  font-weight: 500;
  margin-top: var(--s-1);
  color: var(--ink-3);
}
[data-theme="editorial"] .ed-stat__delta--up   { color: var(--good); }
[data-theme="editorial"] .ed-stat__delta--down { color: var(--bad);  }
[data-theme="editorial"] .ed-stat__delta--flat { color: var(--ink-3); }

/* Empty-state hint под лейблом */
[data-theme="editorial"] .ed-stat__hint {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--ink-4);  /* допустимо — это decorative-hint */
  margin-top: var(--s-1);
}

/* Error — компактный inline-блок с иконкой и кнопкой повтора */
[data-theme="editorial"] .ed-stat__error {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--font-sans);
  font-size: var(--t-caption-size);
  color: var(--bad);
}
[data-theme="editorial"] .ed-stat__error svg {
  width: 16px;
  height: 16px;
  stroke-width: 1.5;
  flex-shrink: 0;
}

/* XL-вариант — для главного KPI на странице (ТЗ 3.2 --t-kpi-xl) */
[data-theme="editorial"] .ed-stat--xl .ed-stat__value {
  font-size: var(--t-kpi-xl-size);
}

/* Inline-вариант: число и лейбл в одну строку, через тонкий разделитель */
[data-theme="editorial"] .ed-stat--inline {
  flex-direction: row;
  align-items: baseline;
  gap: var(--s-3);
  padding: 0;
}
[data-theme="editorial"] .ed-stat--inline .ed-stat__label::before {
  content: "";
  display: inline-block;
  width: 1px;
  height: 12px;
  background: var(--rule);
  margin-right: var(--s-3);
  vertical-align: middle;
}
/* ── css/design-system/components/city-card.css ── */
/*
 * design-system/components/city-card.css
 * Editorial Calm — CityCard. ТЗ 4.4.
 *
 * Кликабельна вся карточка (через <a>-обёртку).
 * НЕ используем тени — рамка + hover на ink.
 *
 * Использование:
 *   <a class="ed-city-card" href="/cities/kaluga">
 *     <div class="ed-city-card__name">Калуга</div>
 *     <div class="ed-city-card__meta">12 408 фактов · 9 исследований</div>
 *     <div class="ed-city-card__pulse">
 *       <span class="ed-dot ed-dot--good"></span>
 *       <span>обновлено сегодня</span>
 *     </div>
 *   </a>
 *
 * Loading:
 *   <div class="ed-city-card" data-state="loading">
 *     <div class="ed-skel ed-skel--text" style="width: 50%; height: 22px;"></div>
 *     <div class="ed-skel ed-skel--text" style="width: 70%;"></div>
 *     <div class="ed-skel ed-skel--text" style="width: 40%;"></div>
 *   </div>
 */

[data-theme="editorial"] .ed-city-card {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-5);
  background: var(--paper-edge);
  border: 1px solid var(--rule);
  border-radius: var(--r-2);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--motion-fast) var(--ease),
              background-color var(--motion-fast) var(--ease);
}

[data-theme="editorial"] a.ed-city-card:hover,
[data-theme="editorial"] a.ed-city-card:focus-visible {
  border-color: var(--ink);
}
[data-theme="editorial"] a.ed-city-card:focus-visible {
   box-shadow: var(--shadow-focus);
}

/* Имя города — title-like, sans 600 */
[data-theme="editorial"] .ed-city-card__name {
  font-family: var(--font-sans);
  font-size: 20px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
}

/* Мета — счётчики, через middle-dot */
[data-theme="editorial"] .ed-city-card__meta {
  font-family: var(--font-sans);
  font-size: var(--t-caption-size);
  font-weight: 500;
  color: var(--ink-3);
  font-feature-settings: "tnum";
}

/* Pulse-индикатор внизу */
[data-theme="editorial"] .ed-city-card__pulse {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--ink-3);
  margin-top: var(--s-2);
}

/* Empty-вариант — для городов без данных */
[data-theme="editorial"] .ed-city-card[data-state="empty"] .ed-city-card__name {
  color: var(--ink-3);
}
/* ── css/design-system/components/role-switcher.css ── */
/*
 * design-system/components/role-switcher.css
 * Editorial Calm — RoleSwitcher. ТЗ 4.5.
 *
 * Сегмент-контрол для переключения активной роли в контексте города.
 * До 6 ролей помещаются в одну линию, остальные — в overflow-меню «ещё».
 *
 * Использование:
 *   <div class="ed-role-switcher" role="tablist" aria-label="Роль">
 *     <button class="ed-role-switcher__item is-active" role="tab" aria-selected="true">Аналитик</button>
 *     <button class="ed-role-switcher__item" role="tab">Урбанист</button>
 *     <button class="ed-role-switcher__item is-locked" role="tab" disabled aria-disabled="true">
 *       Стратег
 *       <span class="ed-pill ed-pill--neutral">PRO</span>
 *     </button>
 *     <button class="ed-role-switcher__more">Ещё 5</button>
 *   </div>
 */

[data-theme="editorial"] .ed-role-switcher {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  padding: 0;
}

[data-theme="editorial"] .ed-role-switcher__item {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  height: 32px;
  padding-inline: var(--s-3);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  line-height: 1;
  color: var(--ink-3);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--r-2);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--motion-fast) var(--ease),
              color var(--motion-fast) var(--ease),
              background-color var(--motion-fast) var(--ease);
}

[data-theme="editorial"] .ed-role-switcher__item:hover:not(:disabled) {
  border-color: var(--ink);
  color: var(--ink);
}

[data-theme="editorial"] .ed-role-switcher__item:focus-visible {
   box-shadow: var(--shadow-focus);
}

/* Активная — рамка ink, текст ink */
[data-theme="editorial"] .ed-role-switcher__item.is-active,
[data-theme="editorial"] .ed-role-switcher__item[aria-selected="true"] {
  border-color: var(--ink);
  color: var(--ink);
  background: var(--paper-edge);
}

/* Locked (PRO без подписки) — disabled но кликабельна для показа PRO-флоу.
   Используем cursor: pointer и не блокируем клик через CSS. */
[data-theme="editorial"] .ed-role-switcher__item.is-locked,
[data-theme="editorial"] .ed-role-switcher__item:disabled,
[data-theme="editorial"] .ed-role-switcher__item[aria-disabled="true"] {
  opacity: 0.6;
  cursor: pointer;  /* Не not-allowed — мы хотим клик на PRO-флоу */
}

[data-theme="editorial"] .ed-role-switcher__more {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding-inline: var(--s-3);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-3);
  background: transparent;
  border: 1px dashed var(--rule);
  border-radius: var(--r-2);
  cursor: pointer;
}
[data-theme="editorial"] .ed-role-switcher__more:hover {
  border-color: var(--ink);
  color: var(--ink);
}
/* ── css/design-system/components/toast.css ── */
/*
 * design-system/components/toast.css
 * Editorial Calm — Toast. ТЗ 4.8.
 *
 * Появляется в правом нижнем углу, position: fixed, z-index --z-toast.
 * Auto-close 5s для good/warn. НЕ закрывается для bad — пользователь
 * должен прочитать ошибку.
 *
 * JS-помощник: static/js/design-system/toast.js
 *   ToastManager.show({ kind: 'good', title: '...', body: '...' });
 */

[data-theme="editorial"] .ed-toast-container {
  position: fixed;
  bottom: var(--s-5);
  right: var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  z-index: var(--z-toast);
  pointer-events: none;  /* контейнер не перехватывает клики */
}

[data-theme="editorial"] .ed-toast {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  min-width: 280px;
  max-width: 400px;
  padding: var(--s-3) var(--s-4);
  background: var(--paper-edge);
  border: 1px solid var(--rule);
  border-radius: var(--r-2);
  box-shadow: var(--shadow-2);
  pointer-events: auto;
  transform: translateY(8px);
  opacity: 0;
  animation: ed-toast-in var(--motion-base) var(--ease) forwards;
}

[data-theme="editorial"] .ed-toast.is-leaving {
  animation: ed-toast-out var(--motion-base) var(--ease) forwards;
}

[data-theme="editorial"] .ed-toast__icon {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  margin-top: 1px;  /* выравнивание с baseline заголовка */
}
[data-theme="editorial"] .ed-toast__icon svg {
  width: 20px;
  height: 20px;
  stroke-width: 1.5;
}

[data-theme="editorial"] .ed-toast--good .ed-toast__icon { color: var(--good); }
[data-theme="editorial"] .ed-toast--warn .ed-toast__icon { color: var(--warn); }
[data-theme="editorial"] .ed-toast--bad  .ed-toast__icon { color: var(--bad);  }

[data-theme="editorial"] .ed-toast__body {
  flex: 1;
  min-width: 0;
}

[data-theme="editorial"] .ed-toast__title {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
}

[data-theme="editorial"] .ed-toast__text {
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink-3);
  margin-top: 2px;
}

[data-theme="editorial"] .ed-toast__close {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--ink-3);
  border-radius: var(--r-2);
}
[data-theme="editorial"] .ed-toast__close:hover { color: var(--ink); background: var(--paper-2); }
[data-theme="editorial"] .ed-toast__close:focus-visible { outline: 0; box-shadow: var(--shadow-focus); }

@keyframes ed-toast-in {
  to { transform: translateY(0); opacity: 1; }
}
@keyframes ed-toast-out {
  to { transform: translateY(8px); opacity: 0; }
}
/* ── css/design-system/components/global-search.css ── */
/*
 * design-system/components/global-search.css
 * Editorial Calm — GlobalSearch (Cmd-K). ТЗ 4.9.
 *
 * Триггер — кнопка в AppHeader. Активация по клику / ⌘K / Ctrl+K.
 * Открывается модальный оверлей с инпутом и сгруппированными результатами.
 *
 * Группы (ТЗ 4.9):
 *   • Города
 *   • Активные исследования
 *   • Разделы (Пульс, Тарифы, Профиль)
 *   • История последних 5 запросов
 */

/* ─── Trigger в хедере ─────────────────────────────────────────────── */
[data-theme="editorial"] .ed-search-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  height: 36px;
  padding: 0 var(--s-3);
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: var(--r-2);
  color: var(--ink-3);
  font-family: var(--font-sans);
  font-size: 13px;
  cursor: pointer;
  min-width: 220px;
  transition: border-color var(--motion-fast) var(--ease);
}
[data-theme="editorial"] .ed-search-trigger:hover { border-color: var(--ink); }
[data-theme="editorial"] .ed-search-trigger:focus-visible { outline: 0; box-shadow: var(--shadow-focus); }
[data-theme="editorial"] .ed-search-trigger > span { flex: 1; text-align: left; }

[data-theme="editorial"] .ed-kbd {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-3);
  background: var(--paper-edge);
  border: 1px solid var(--rule);
  border-radius: var(--r-1);
}

/* ─── Overlay + dialog ─────────────────────────────────────────────── */
[data-theme="editorial"] .ed-search-overlay {
  position: fixed;
  inset: 0;
  background: rgba(26, 26, 26, 0.4);
  z-index: var(--z-modal);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 12vh;
}

[data-theme="editorial"] .ed-search-dialog {
  width: min(640px, 92%);
  max-height: 70vh;
  display: flex;
  flex-direction: column;
  background: var(--paper-edge);
  border: 1px solid var(--rule);
  border-radius: var(--r-3);
  box-shadow: var(--shadow-2);
  overflow: hidden;
}

[data-theme="editorial"] .ed-search-input-wrap {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4);
  border-bottom: 1px solid var(--rule);
}
[data-theme="editorial"] .ed-search-input-wrap svg {
  width: 20px; height: 20px; stroke-width: 1.5; color: var(--ink-3); flex-shrink: 0;
}
[data-theme="editorial"] .ed-search-input {
  flex: 1;
  border: 0;
   background: transparent;
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--ink);
}
[data-theme="editorial"] .ed-search-input::placeholder { color: var(--ink-3); }
[data-theme="editorial"] .ed-search-input:focus-visible { box-shadow: var(--shadow-focus); }

[data-theme="editorial"] .ed-search-results {
  overflow-y: auto;
  flex: 1;
}

/* Группа результатов */
[data-theme="editorial"] .ed-search-group {
  border-bottom: 1px solid var(--rule-2);
}
[data-theme="editorial"] .ed-search-group:last-child { border-bottom: 0; }

[data-theme="editorial"] .ed-search-group__title {
  padding: var(--s-3) var(--s-4) var(--s-1);
  font-family: var(--font-sans);
  font-size: var(--t-eyebrow-size);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

[data-theme="editorial"] .ed-search-item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  cursor: pointer;
  border: 0;
  width: 100%;
  background: transparent;
  text-align: left;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--ink-2);
  text-decoration: none;
  transition: background-color var(--motion-fast) var(--ease);
}
[data-theme="editorial"] .ed-search-item:hover,
[data-theme="editorial"] .ed-search-item.is-selected {
  background: var(--paper-2);
  color: var(--ink);
}
[data-theme="editorial"] .ed-search-item__title {
  flex: 1;
  font-weight: 500;
}
[data-theme="editorial"] .ed-search-item__meta {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink-3);
}

[data-theme="editorial"] .ed-search-empty {
  padding: var(--s-6) var(--s-4);
  text-align: center;
  color: var(--ink-3);
  font-size: 14px;
}

/* Footer-подсказка по горячим клавишам */
[data-theme="editorial"] .ed-search-footer {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-2) var(--s-4);
  border-top: 1px solid var(--rule-2);
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--ink-3);
}
[data-theme="editorial"] .ed-search-footer .ed-kbd {
  margin-right: var(--s-2);
}
/* ── css/design-system/components/app-header.css ── */
/*
 * design-system/components/app-header.css
 * Editorial Calm — AppHeader. ТЗ 4.10.
 *
 * Единый хедер на всех экранах кроме главной (на главной — упрощённый
 * вариант без хлебных крошек).
 *
 * Структура:
 *   [logo] · [breadcrumbs] · [search] · [pulse · profile]
 *   sticky top:0, z-index --z-sticky, height 64px,
 *   border-bottom 1px solid --rule
 *
 * Использование (через partial templates/components/_app_header.html):
 *   {% include "components/_app_header.html" with city=city, role=role %}
 */

[data-theme="editorial"] .ed-app-header {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--s-5);
  height: 64px;
  padding: 0 var(--s-6);
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}

/* Логотип-вордмарк слева */
[data-theme="editorial"] .ed-app-header__logo {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 18px;
  line-height: 1;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
[data-theme="editorial"] .ed-app-header__logo:focus-visible {
   box-shadow: var(--shadow-focus);
  border-radius: var(--r-2);
}

/* Хлебные крошки в центре-слева */
[data-theme="editorial"] .ed-app-header__crumbs {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--ink-3);
  flex: 0 1 auto;
  min-width: 0;
}
[data-theme="editorial"] .ed-app-header__crumbs a {
  color: var(--ink-3);
  text-decoration: none;
}
[data-theme="editorial"] .ed-app-header__crumbs a:hover {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
[data-theme="editorial"] .ed-app-header__crumbs strong {
  font-weight: 500;
  color: var(--ink);
}
[data-theme="editorial"] .ed-app-header__crumbs__sep {
  opacity: 0.4;
  user-select: none;
}

/* Spacer для распределения */
[data-theme="editorial"] .ed-app-header__spacer { flex: 1; }

/* Правая зона: поиск + меню */
[data-theme="editorial"] .ed-app-header__right {
  display: flex;
  align-items: center;
  gap: var(--s-3);
}

[data-theme="editorial"] .ed-app-header__menu-item {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  height: 36px;
  padding: 0 var(--s-3);
  background: transparent;
  border: 0;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--ink-3);
  cursor: pointer;
  text-decoration: none;
  border-radius: var(--r-2);
  transition: color var(--motion-fast) var(--ease),
              background-color var(--motion-fast) var(--ease);
}
[data-theme="editorial"] .ed-app-header__menu-item:hover { color: var(--ink); background: var(--paper-2); }
[data-theme="editorial"] .ed-app-header__menu-item:focus-visible { outline: 0; box-shadow: var(--shadow-focus); }
[data-theme="editorial"] .ed-app-header__menu-item svg {
  width: 16px; height: 16px; stroke-width: 1.5;
}

/* Адаптация — на узких экранах прячем breadcrumbs */
@media (max-width: 767px) {
  [data-theme="editorial"] .ed-app-header { padding: 0 var(--s-4); gap: var(--s-3); }
  [data-theme="editorial"] .ed-app-header__crumbs { display: none; }
  [data-theme="editorial"] .ed-search-trigger { min-width: 0; }
  [data-theme="editorial"] .ed-search-trigger > span { display: none; }
}
/* ── css/design-system/components/tabs.css ── */
/*
 * design-system/components/tabs.css
 * Editorial Calm — Tabs. ТЗ 4.11.
 *
 * Унифицируют структуру всех дашбордов. ≤ 5 вкладок верхнего уровня.
 * Активная вкладка — нижняя линия 2px ink с -1px margin (overlay рамки).
 *
 * Использование:
 *   <div class="ed-tabs" role="tablist" aria-label="Разделы города">
 *     <button class="ed-tab is-active" role="tab" aria-selected="true">Обзор</button>
 *     <button class="ed-tab" role="tab" aria-selected="false">Исследования</button>
 *     <button class="ed-tab" role="tab">Новости</button>
 *     <button class="ed-tab" role="tab">База знаний</button>
 *   </div>
 */

.ed-tabs {
  display: flex;
  gap: var(--s-5, 24px);
  height: 44px;
  border-bottom: 1px solid var(--rule, var(--border, #D9D3C5));
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* §12 «длинные названия»: внутри flex-контейнера (шапка/тулбар) группа
     вкладок сжимается и скроллится сама, а не распирает родителя. */
  min-width: 0;
  flex-shrink: 1;
}

.ed-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  height: 44px;
  padding: 0 var(--s-1, 4px);
  /* §12 стресс-сценарий «длинные названия»: вкладка с длинным лейблом не
     должна распирать тулбар за viewport — clamp + ellipsis. */
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: -1px;  /* перекрываем border-bottom родителя */
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-3, var(--text-muted, #6b7280));
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--motion-fast, .15s) var(--ease, ease),
              border-color var(--motion-fast, .15s) var(--ease, ease);
}

.ed-tab:hover { color: var(--ink, var(--text-primary, #1F2937)); }

.ed-tab:focus-visible {
   box-shadow: var(--shadow-focus, 0 0 0 2px var(--primary, #1F3D2B));
  border-radius: var(--r-2, 6px);
}

.ed-tab.is-active,
.ed-tab.active,
.ed-tab[aria-selected="true"] {
  color: var(--ink, var(--text-primary, #1F2937));
  border-bottom-color: var(--ink, var(--text-primary, #1F2937));
}

/* Бэйджик-счётчик внутри таба */
.ed-tab__count {
  margin-left: var(--s-2, 8px);
  padding: 0 6px;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-3, var(--text-muted, #6b7280));
  background: var(--paper-2, var(--bg-secondary, #F5F4F0));
  border-radius: var(--r-1, 4px);
  font-feature-settings: "tnum";
}
.ed-tab.is-active .ed-tab__count,
.ed-tab.active .ed-tab__count {
  color: var(--ink, var(--text-primary, #1F2937));
  background: var(--paper-3, var(--bg-tertiary, #ECEAE4));
}

/* Компактный вариант для под-табов и плотных тулбаров (§6.7):
   <div class="ed-tabs ed-tabs--sm" role="tablist"> */
.ed-tabs.ed-tabs--sm { height: 36px; gap: var(--s-4, 16px); }
.ed-tabs--sm .ed-tab { height: 36px; font-size: 13px; }
/* ── css/design-system/components/pulse.css ── */
/*
 * design-system/components/pulse.css
 * Editorial Calm — Pulse. ТЗ 4.12.
 *
 * Минималистичный индикатор статуса системы. Заменяет всё текущее
 * "💓 Пульс Системы" в одну строку.
 *
 * Состояния:
 *   good — все системы в норме
 *   warn — частичная деградация (с указанием подсистемы)
 *   bad  — сбой (со ссылкой на /system/pulse)
 *
 * Использование:
 *   <div class="ed-pulse ed-pulse--good">
 *     <span class="ed-dot ed-dot--good"></span>
 *     <span>Все системы в норме · обновлено 2 мин назад</span>
 *   </div>
 */

[data-theme="editorial"] .ed-pulse {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  transition: border-color var(--motion-fast) var(--ease),
              background-color var(--motion-fast) var(--ease);
}
[data-theme="editorial"] a.ed-pulse:hover { border-color: var(--ink); }
[data-theme="editorial"] a.ed-pulse:focus-visible { outline: 0; box-shadow: var(--shadow-focus); }

[data-theme="editorial"] .ed-pulse--good { background: transparent; }
[data-theme="editorial"] .ed-pulse--warn { background: var(--warn-tint); border-color: rgba(140, 90, 26, 0.25); color: var(--warn); }
[data-theme="editorial"] .ed-pulse--bad  { background: var(--bad-tint);  border-color: rgba(139, 42, 31, 0.25); color: var(--bad);  }

[data-theme="editorial"] .ed-pulse--warn .ed-dot,
[data-theme="editorial"] .ed-pulse--bad .ed-dot {
  /* анимация мягкого пульса для тревожных состояний */
  animation: ed-pulse-blink 2000ms ease-in-out infinite;
}

@keyframes ed-pulse-blink {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.5; }
}

@media (prefers-reduced-motion: reduce) {
  [data-theme="editorial"] .ed-pulse .ed-dot {
    animation: none;
  }
}
/* ── css/design-system/components/map.css ── */
/*
 * design-system/components/map.css
 * Editorial Calm — Map. ТЗ §6.5.
 *
 * Тайлы — монохромные на тёплой бумажной подложке (--map-base).
 * Точки городов — кружки 6-20px, диаметр по плотности данных.
 * Hover на точке — мини-превью CityCard.
 *
 * Зависимость: MapLibre GL JS подключается через CDN.
 */

[data-theme="editorial"] .ed-map {
  position: relative;
  width: 100%;
  min-height: 480px;
  background: var(--map-base);
  border: 1px solid var(--rule);
}

[data-theme="editorial"] .ed-map__container {
  width: 100%;
  height: 100%;
  min-height: 480px;
}

/* Mini-preview popup (CityCard adapt for hover) */
[data-theme="editorial"] .ed-map-popup {
  font-family: var(--font-sans);
  pointer-events: none;
}
[data-theme="editorial"] .ed-map-popup .maplibregl-popup-content {
  padding: var(--s-3) var(--s-4);
  background: var(--paper-edge);
  border: 1px solid var(--ink);
  border-radius: var(--r-2);
  box-shadow: var(--shadow-2);
  font-family: var(--font-sans);
  min-width: 200px;
}
[data-theme="editorial"] .ed-map-popup .maplibregl-popup-tip { display: none; }

[data-theme="editorial"] .ed-map-popup__name {
  font-weight: 600;
  font-size: 15px;
  color: var(--ink);
  margin-bottom: 4px;
}
[data-theme="editorial"] .ed-map-popup__meta {
  font-size: 12px;
  color: var(--ink-3);
  font-feature-settings: "tnum";
}

/* MapLibre overrides — приглушаем default UI под Editorial */
[data-theme="editorial"] .maplibregl-ctrl-group {
  background: var(--paper-edge);
  border: 1px solid var(--rule);
  border-radius: var(--r-2);
  box-shadow: none;
}
[data-theme="editorial"] .maplibregl-ctrl-group button {
  background: var(--paper-edge);
  border: 0;
}
[data-theme="editorial"] .maplibregl-ctrl-group button:hover {
  background: var(--paper-2);
}
[data-theme="editorial"] .maplibregl-ctrl-attrib {
  background: rgba(251, 250, 247, 0.85);
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--ink-3);
}

/* Loading-state */
[data-theme="editorial"] .ed-map[data-state="loading"]::after {
  content: "Загружаем карту…";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--map-base);
  color: var(--ink-3);
  font-family: var(--font-sans);
  font-size: 14px;
  z-index: 1;
}

/* Error-state */
[data-theme="editorial"] .ed-map[data-state="error"] .ed-map__container { display: none; }
[data-theme="editorial"] .ed-map[data-state="error"]::after {
  content: "Не удалось загрузить тайлы. Попробовать снова?";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--bad);
  font-family: var(--font-sans);
  font-size: 14px;
  padding: var(--s-5);
  text-align: center;
}
/* ── css/dashboard-base.css ── */
/**
 * Dashboard Base Styles - Unified Design System
 * Version 4.1 - Added Cultural Map Components
 * FIXED: Font imports, modal styles, backdrop-filter fallbacks
 */

/* ========================================================================
   Design tokens moved to static/css/variables.css
   ======================================================================== */

/* Stream Recorder styles were moved to static/css/stream-panel.css and are lazy-loaded
   by static/js/stream/streamToggle.js (to avoid global z-index collisions). */

/* ========================================================================
   RESET & BASE
   ======================================================================== */

*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    font-size: 16px;
    -webkit-text-size-adjust: 100%;
    -webkit-tap-highlight-color: transparent;
}

body {
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 400;
    line-height: 1.6;
    color: var(--text-primary);
    background: var(--bg-base);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow-x: hidden;
}

body[data-dashboard-theme] {
    /* One-screen dashboards: keep footer visible and make inner panes scroll */
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* body stays flex-column; nav-sidebar is position:fixed, shell uses margin-left */

body[data-dashboard-theme] .dashboard-header {
    flex-shrink: 0;
}

/* Desktop: lock body height so footer never "falls down" */
@media (min-width: 901px) {
    html,
    body[data-dashboard-theme] {
        height: 100%;
    }

    body[data-dashboard-theme] {
        height: 100vh;
        overflow: hidden;
    }
}

/* ========================================================================
   NAV SIDEBAR — persistent vertical navigation
   ======================================================================== */

:root {
    --nav-sidebar-width: 240px;
    --nav-sidebar-collapsed-width: 56px;
}

.nav-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: var(--nav-sidebar-width);
    background: var(--bg-primary);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    z-index: 200;
    transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    overflow: hidden;
}

.nav-sidebar.collapsed {
    width: var(--nav-sidebar-collapsed-width);
}

/* Brand / Logo area */
.nav-sidebar-brand {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px;
    height: 48px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.nav-sidebar-logo {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: var(--text-primary);
    min-width: 0;
    overflow: hidden;
}

.nav-sidebar-logo-icon {
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    color: var(--primary);
}

.nav-sidebar-brand-text {
    font-size: 0.9375rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    white-space: nowrap;
    opacity: 1;
    transition: opacity 0.2s;
}

.nav-sidebar.collapsed .nav-sidebar-brand-text {
    opacity: 0;
    width: 0;
    overflow: hidden;
}

/* Toggle button */
.nav-sidebar-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: none;
    background: var(--bg-tertiary);
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text-muted);
    flex-shrink: 0;
    transition: background 0.15s, color 0.15s;
}

.nav-sidebar-toggle:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.nav-sidebar .toggle-icon-expand { display: none; }
.nav-sidebar .toggle-icon-collapse { display: block; }
.nav-sidebar.collapsed .toggle-icon-expand { display: block; }
.nav-sidebar.collapsed .toggle-icon-collapse { display: none; }

/* Menu area */
.nav-sidebar-menu {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 8px 0;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}

.nav-sidebar-menu::-webkit-scrollbar {
    width: 4px;
}

.nav-sidebar-menu::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: var(--radius-xs);
}

/* Section labels */
.nav-sidebar-section {
    padding: 4px 0;
}

.nav-sidebar-section-label {
    display: block;
    padding: 6px 16px 4px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    transition: opacity 0.2s;
}

.nav-sidebar.collapsed .nav-sidebar-section-label {
    opacity: 0;
    height: 0;
    padding: 0;
    overflow: hidden;
}

/* Nav items */
.nav-sidebar-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    margin: 1px 8px;
    border-radius: var(--radius);
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 0.8125rem;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    cursor: pointer;
    border: none;
    background: none;
    width: calc(100% - 16px);
    text-align: left;
    transition: background 0.15s, color 0.15s;
}

.nav-sidebar-item:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}

.nav-sidebar-item.active {
    background: var(--primary);
    color: var(--white);
}

.nav-sidebar-item.active:hover {
    background: var(--primary-dark, var(--primary));
    color: var(--white);
}

.nav-sidebar-item.disabled {
    opacity: 0.4;
    pointer-events: none;
    cursor: not-allowed;
}

.nav-sidebar-item-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex-shrink: 0;
    font-size: 1rem;
    line-height: 1;
}

.nav-sidebar-item-icon svg {
    width: 20px;
    height: 20px;
}

.nav-sidebar-item-text {
    opacity: 1;
    transition: opacity 0.2s;
    overflow: hidden;
    text-overflow: ellipsis;
}

.nav-sidebar.collapsed .nav-sidebar-item-text {
    opacity: 0;
    width: 0;
    overflow: hidden;
}

.nav-sidebar.collapsed .nav-sidebar-item {
    justify-content: center;
    padding: 8px;
    margin: 1px 6px;
    gap: 0;
}

/* Bottom section */
.nav-sidebar-bottom {
    border-top: 1px solid var(--border);
    padding: 8px 0;
    flex-shrink: 0;
}

/* ========================================================================
   DASHBOARD SHELL — offset by nav sidebar
   ======================================================================== */

.dashboard-shell {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    min-width: 0;
    margin-left: var(--nav-sidebar-width);
    transition: margin-left 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    height: 100vh;
    overflow: hidden;
}

html.nav-sidebar-is-collapsed .dashboard-shell {
    margin-left: var(--nav-sidebar-collapsed-width);
}

/* ========================================================================
   NAV SIDEBAR BACKDROP & MOBILE TOGGLE
   ======================================================================== */

.nav-sidebar-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 199;
    opacity: 0;
    transition: opacity 0.25s;
}

.nav-sidebar-backdrop.visible {
    display: block;
    opacity: 1;
}

.nav-sidebar-mobile-toggle {
    display: none;
    position: fixed;
    top: 10px;
    left: 10px;
    z-index: 201;
    width: 40px;
    height: 40px;
    border: none;
    background: var(--bg-primary);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    cursor: pointer;
    align-items: center;
    justify-content: center;
    color: var(--text-primary);
}

@media (max-width: 768px) {
    .nav-sidebar {
        transform: translateX(-100%);
        transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        width: var(--nav-sidebar-width);
    }

    .nav-sidebar.mobile-open {
        transform: translateX(0);
    }

    .dashboard-shell {
        margin-left: 0;
    }

    html.nav-sidebar-is-collapsed .dashboard-shell {
        margin-left: 0;
    }

    .nav-sidebar-mobile-toggle {
        display: flex;
    }
}

@media print {
    .nav-sidebar,
    .nav-sidebar-backdrop,
    .nav-sidebar-mobile-toggle {
        display: none !important;
    }

    .dashboard-shell {
        margin-left: 0 !important;
    }
}

/* ========================================================================
   DASHBOARD HEADER (unified) — Phase 2: compact after nav-link cleanup
   ======================================================================== */

body[data-dashboard-theme] .dashboard-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-md);
    padding: 0 var(--spacing-lg);
    height: var(--header-height, 48px);
    background: var(--bg-primary);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 50;
    flex-shrink: 0;
}

body[data-dashboard-theme] .dashboard-header .header-left {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    min-width: 0;
}

body[data-dashboard-theme] .dashboard-header .header-left h1 {
    font-size: 0.9375rem;
    font-weight: 600;
    line-height: 1.25;
    letter-spacing: -0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* F-07 (аудит v2.2): без пола ширины flex ужимал заголовок до «Г…».
       Многоточие допустимо, односимвольный обрубок — нет. */
    min-width: 10ch;
}

body[data-dashboard-theme] .dashboard-header .city-name,
body[data-dashboard-theme] .dashboard-header .survey-name {
    font-size: 0.8125rem;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 40vw;
}

/* Пилот ContextBar (UX-P0-01): слот полосы контекста выносится на СВОЮ строку
   под заголовком (flex-wrap + order + flex-basis), чтобы не сжимать h1 — иначе
   даже короткий role-title («Аналитика») сам обрезается в одном ряду. Сам бар
   несёт свою ellipsis-логику для длинных значений. */
body[data-dashboard-theme] .dashboard-header .header-left:has(.dashboard-contextbar) {
    flex-wrap: wrap;
    row-gap: 4px;
}
body[data-dashboard-theme] .dashboard-header .dashboard-contextbar {
    order: 99;
    flex-basis: 100%;
    min-width: 0;
}

body[data-dashboard-theme] .dashboard-header .header-right {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.375rem;
    flex-wrap: nowrap;
}

/* Hide header-right when empty (all nav links removed) */
body[data-dashboard-theme] .dashboard-header .header-right:empty {
    display: none;
}

/* Header center stats (moderator, etc.) — compact */
body[data-dashboard-theme] .dashboard-header .header-center {
    display: flex;
    align-items: center;
    gap: var(--spacing-md);
}

body[data-dashboard-theme] .dashboard-header .stats-container {
    display: flex;
    gap: var(--spacing-md);
}

body[data-dashboard-theme] .dashboard-header .stat-item {
    text-align: center;
    line-height: 1.2;
}

body[data-dashboard-theme] .dashboard-header .stat-value {
    font-size: 1rem;
    font-weight: 700;
    color: var(--text-primary);
}

body[data-dashboard-theme] .dashboard-header .stat-label {
    font-size: 0.75rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

body[data-dashboard-theme] .llm-balance-widget {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    background: var(--bg-tertiary);
    border-radius: var(--radius-full);
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--text-secondary);
    /* Тесный топбар ломал чип в 3 строки — держим одной строкой */
    white-space: nowrap;
    flex-shrink: 0;
}

body[data-dashboard-theme] .llm-balance-widget .details-link {
    color: var(--primary);
    text-decoration: none;
}

@media (max-width: 640px) {
    body[data-dashboard-theme] .dashboard-header {
        padding: 0 var(--spacing-md);
        /* ТЗ §6.2/§12: mobile-хедер не ниже 48px (было fallback 40px) */
        height: var(--ds-header-h-mobile, 48px);
    }
    body[data-dashboard-theme] .dashboard-header .header-left h1 {
        font-size: 0.875rem;
    }
    body[data-dashboard-theme] .dashboard-header .city-name,
    body[data-dashboard-theme] .dashboard-header .survey-name {
        display: none;
    }
    body[data-dashboard-theme] .dashboard-header .header-center {
        display: none;
    }
    body[data-dashboard-theme] .user-info-panel .user-details {
        display: none;
    }
}

body[data-dashboard-theme] .dashboard-main {
    /* Allow main layout to shrink so footer stays visible and inner panes can scroll */
    flex: 1 1 auto;
    width: 100%;
    display: flex;
    flex-direction: row;
    min-height: 0;
    overflow: hidden;
    background: var(--bg-secondary);
    /* Subtle inner shadow to separate from header */
    box-shadow: inset 0 1px 0 var(--border);
}

/* Layout wrappers used by role dashboards inside shell.
   Each needs flex:1 + min-height:0 so the 3-column content fills the shell. */
.dashboard-shell .dashboard-layout,
.dashboard-shell .developer-dashboard-root,
.dashboard-shell .econ-wrap,
.dashboard-shell > .page-container {
    flex: 1 1 auto;
    display: flex;
    flex-direction: row;
    min-height: 0;
    overflow: hidden;
    width: 100%;
}

/* KPI/status bars between header and main — don't let them shrink */
.dashboard-shell .strategy-kpi-grid,
.dashboard-shell .status-bar,
.dashboard-shell .view-mode-bar {
    flex-shrink: 0;
}

/* Main container (methodologist etc.) — fill shell like dashboard-main */
.dashboard-shell .main-container {
    flex: 1 1 auto;
    display: flex;
    flex-direction: row;
    min-height: 0;
    overflow: hidden;
    width: 100%;
}

/* Header actions — compact button group */
body[data-dashboard-theme] .header-actions {
    display: flex;
    align-items: center;
    gap: 0.375rem;
}

/* User info panel in header — keep compact.
   Header is 60px: the chip must stay a single meta-line and never overflow
   vertically (floating-windows.css gives it fat paddings + 36px avatar —
   override here; these rules win by specificity). */
body[data-dashboard-theme] .user-info-panel {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 4px 10px;
    flex-shrink: 0;
}

body[data-dashboard-theme] .user-info-panel .user-avatar {
    font-size: 1rem;
    line-height: 1;
    width: 28px;
    height: 28px;
    flex-shrink: 0;
}

body[data-dashboard-theme] .user-info-panel .user-details {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

body[data-dashboard-theme] .user-info-panel .user-name {
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

body[data-dashboard-theme] .user-info-panel .user-meta {
    display: flex;
    gap: 0.5rem;
    font-size: 0.75rem;
    color: var(--text-muted);
    white-space: nowrap;
}

body[data-dashboard-theme] .dashboard-content {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    /* Critical for nested flex layouts: enables .content-wrapper/.view-pane scroll instead of clipping */
    min-height: 0;
    overflow: auto;
    padding: var(--spacing-md);
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}

body[data-dashboard-theme] .dashboard-content::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

body[data-dashboard-theme] .dashboard-content::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 3px;
}

body[data-dashboard-theme] .dashboard-content::-webkit-scrollbar-track {
    background: transparent;
}

body[data-dashboard-theme] .dashboard-sidebar-left,
body[data-dashboard-theme] .dashboard-sidebar-right {
    background: var(--bg-primary);
    border: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    flex-shrink: 0;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}

body[data-dashboard-theme] .dashboard-sidebar-left::-webkit-scrollbar,
body[data-dashboard-theme] .dashboard-sidebar-right::-webkit-scrollbar {
    width: 4px;
}

body[data-dashboard-theme] .dashboard-sidebar-left::-webkit-scrollbar-thumb,
body[data-dashboard-theme] .dashboard-sidebar-right::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: var(--radius-xs);
}

body[data-dashboard-theme] .dashboard-sidebar-left {
    width: var(--sidebar-left-width);
    border-left: none;
    border-right: 1px solid var(--border);
}

body[data-dashboard-theme] .dashboard-sidebar-right {
    width: var(--sidebar-right-width);
    border-right: none;
    border-left: 1px solid var(--border);
}

/* ── Шторки боковых панелей (UX 2026-07-03 «больше места для работы»).
   Классы ставит static/js/shared/sidebar-collapse.js; аддитивно для всех
   дашбордов на общем каркасе. Плавное схлопывание в 0 + ручки ‹ › на краях. ── */
body[data-dashboard-theme] .dashboard-sidebar-left,
body[data-dashboard-theme] .dashboard-sidebar-right {
    transition: width 0.25s ease;
}
body[data-dashboard-theme].sb-left-collapsed .dashboard-sidebar-left,
body[data-dashboard-theme].sb-right-collapsed .dashboard-sidebar-right {
    width: 0 !important;
    min-width: 0 !important;
    border: none !important;
    overflow: hidden !important;
    padding: 0 !important;
}
body[data-dashboard-theme] .dashboard-main.sb-collapse-host {
    position: relative;
}
body[data-dashboard-theme] .sb-collapse-handle {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 60;
    width: 16px;
    height: 64px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-primary);
    color: var(--text-muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    opacity: 0.55;
    transition: opacity 0.15s, background 0.15s;
    box-shadow: 0 1px 4px color-mix(in srgb, var(--dark) 15%, transparent);
}
body[data-dashboard-theme] .sb-collapse-handle:hover {
    opacity: 1;
    background: var(--bg-secondary);
    color: var(--text-primary);
}
body[data-dashboard-theme] .sb-handle-left  { left: 0;  border-top-left-radius: 0; border-bottom-left-radius: 0; }
body[data-dashboard-theme] .sb-handle-right { right: 0; border-top-right-radius: 0; border-bottom-right-radius: 0; }
body[data-dashboard-theme] .sb-handle-chev { font-size: 14px; font-weight: 700; line-height: 1; transition: transform 0.25s; }
body[data-dashboard-theme] .sb-handle-collapsed .sb-handle-chev { transform: rotate(180deg); }

body[data-dashboard-theme] .sidebar-section {
    padding: var(--spacing-md);
    border-bottom: 1px solid var(--border);
}

body[data-dashboard-theme] .sidebar-section:last-child {
    border-bottom: none;
}

body[data-dashboard-theme] .sidebar-title {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-bottom: var(--spacing-sm);
}

/* Central panel containers — fill available space, scroll when needed */
body[data-dashboard-theme] .dashboard-main > main,
body[data-dashboard-theme] .dashboard-main .dashboard-center,
body[data-dashboard-theme] .dashboard-main .center-panel {
    flex: 1;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
    position: relative;
}

@media (max-width: 1200px) {
    body[data-dashboard-theme] .dashboard-main {
        flex-direction: column;
    }
    body[data-dashboard-theme] .dashboard-sidebar-left,
    body[data-dashboard-theme] .dashboard-sidebar-right {
        width: 100%;
        max-height: 30vh;
        border: none;
    }
    body[data-dashboard-theme] .dashboard-sidebar-left { border-bottom: 1px solid var(--border); }
    body[data-dashboard-theme] .dashboard-sidebar-right { border-top: 1px solid var(--border); }
}

@media (max-width: 768px) {
    body[data-dashboard-theme] .dashboard-sidebar-left,
    body[data-dashboard-theme] .dashboard-sidebar-right {
        position: fixed;
        left: -100%;
        height: 100vh;
        z-index: var(--z-fixed);
        transition: left 0.3s;
        box-shadow: var(--shadow-lg);
        max-height: 100vh;
    }
    body[data-dashboard-theme] .dashboard-sidebar-right {
        left: auto;
        right: -100%;
        transition: right 0.3s;
    }
    body[data-dashboard-theme] .dashboard-sidebar-left.open { left: 0; }
    body[data-dashboard-theme] .dashboard-sidebar-right.open { right: 0; }
}

/* ========================================================================
   COLLAPSIBLE SIDE PANELS — системный стандарт (shared/collapsible-panels.js)
   Кнопки-переключатели в .dashboard-header сворачивают левую/правую
   .dashboard-sidebar-* (или [data-collapsible-panel]) колонку — центр
   растёт через flex:1. Стандарт для всех 3-колоночных дашбордов.
   ======================================================================== */
.panel-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-primary, var(--white));
    color: var(--text-secondary, var(--gray-500));
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.panel-toggle:hover {
    background: var(--bg-secondary);
    color: var(--text-primary);
}
.panel-toggle.is-collapsed {
    /* Свёрнуто — подсветить, чтобы было видно «панель спрятана». */
    background: var(--accent-light, var(--bg-secondary));
    border-color: var(--accent, var(--border));
    color: var(--accent, var(--text-primary));
}
.panel-toggle svg { display: block; transition: transform 0.2s ease; }
.panel-toggle-left { margin-right: 0.5rem; }
.panel-toggle-right { margin-left: 0.5rem; }
/* Направление шеврона: ‹ = «свернуть», › = «развернуть».
   Базовый SVG указывает ‹ (влево). */
.panel-toggle-right svg            { transform: rotate(180deg); } /* › свернуть вправо */
.panel-toggle-left.is-collapsed svg  { transform: rotate(180deg); } /* › развернуть */
.panel-toggle-right.is-collapsed svg { transform: rotate(0deg); }  /* ‹ развернуть */

/* Опциональный якорь для центральной колонки: если центр не имеет
   flex-grow в своём CSS, пометь его data-collapsible-center — тогда при
   сворачивании боковой панели освободившееся место заберёт именно он. */
body[data-dashboard-theme] [data-collapsible-center] {
    flex: 1 1 auto;
    min-width: 0;
}

/* Страховка последней инстанции (P0-DEV-01, аудит 23.08). Если после
   «показать панель» она всё равно оказалась ЗА пределами окна — раскладка
   решила иначе, чем мы думали, — panel-visibility.js вешает этот класс.
   Панель становится шторкой у правого края ВНУТРИ окна: пользователь
   добирается до вкладок и легенд, даже когда чужое правило увело колонку
   за viewport. Лучше шторка, чем недостижимая панель. */
body[data-dashboard-theme] .dashboard-sidebar-right.panel-forced-drawer,
body[data-dashboard-theme] [data-collapsible-panel="right"].panel-forced-drawer {
    position: fixed !important;
    top: var(--ds-header-h, 56px);
    right: 0 !important;
    left: auto !important;
    height: calc(100vh - var(--ds-header-h, 56px));
    width: min(var(--sidebar-right-width, 360px), 92vw) !important;
    transform: none !important;
    z-index: var(--z-fixed, 1030);
    box-shadow: var(--shadow-lg);
    overflow-y: auto;
}
body[data-dashboard-theme] .dashboard-sidebar-left.panel-forced-drawer,
body[data-dashboard-theme] [data-collapsible-panel="left"].panel-forced-drawer {
    position: fixed !important;
    top: var(--ds-header-h, 56px);
    left: 0 !important;
    right: auto !important;
    height: calc(100vh - var(--ds-header-h, 56px));
    width: min(var(--sidebar-left-width, 320px), 92vw) !important;
    transform: none !important;
    z-index: var(--z-fixed, 1030);
    box-shadow: var(--shadow-lg);
    overflow-y: auto;
}

/* Свёртка применяется только на широких экранах — ниже работает
   адаптивная раскладка (стек / drawer), там переключатели не нужны. */
@media (min-width: 1201px) {
    body[data-dashboard-theme] .dashboard-sidebar-left.is-collapsed,
    body[data-dashboard-theme] .dashboard-sidebar-right.is-collapsed,
    body[data-dashboard-theme] [data-collapsible-panel].is-collapsed {
        display: none !important;
    }
}
@media (max-width: 1200px) {
    .panel-toggle { display: none; }
}

/* ========================================================================
   CONTEXT PANEL — slide-over panel (V2 Design System)
   Replaces permanent right sidebar for dashboards that need on-demand info.
   Usage: add .context-panel to a right-side element, toggle .is-open via JS.
   ======================================================================== */

.context-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: 400px;
    max-width: 90vw;
    background: var(--bg-elevated, var(--white));
    border-left: 1px solid var(--border);
    box-shadow: -4px 0 24px rgba(0, 0, 0, 0.08);
    z-index: var(--z-fixed, 100);
    transform: translateX(100%);
    transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.context-panel.is-open {
    transform: translateX(0);
}

.context-panel__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.context-panel__title {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--text-primary);
}

.context-panel__close {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: var(--radius-md, 8px);
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.context-panel__close:hover {
    background: color-mix(in srgb, var(--danger) 12%, var(--bg-primary));
    color: var(--danger);
}

.context-panel__body {
    flex: 1 1 0;
    overflow-y: auto;
    padding: 16px;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}

/* Backdrop overlay when panel is open */
.context-panel-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.2);
    z-index: calc(var(--z-fixed, 100) - 1);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
}

.context-panel-backdrop.is-visible {
    opacity: 1;
    pointer-events: auto;
}

[data-theme="dark"] .context-panel {
    box-shadow: -4px 0 24px rgba(0, 0, 0, 0.35);
}

/* Ensure font inheritance for all elements */
button, input, select, textarea, optgroup {
    font-family: inherit;
    font-size: inherit;
    line-height: inherit;
}

/* ========================================================================
   TYPOGRAPHY
   ======================================================================== */

/* §6.5: пол 12px для относительных вложений. small/code внутри уже мелкого
   родителя (0.75rem-хинты) композитно уезжали до 10.5–11.2px — max() держит
   относительный размер, но не даёт упасть ниже читаемого минимума.
   Vendor-атрибуция карт — единственное допущенное исключение (ТЗ §6.5). */
small { font-size: max(12px, 0.83em); }
code, kbd, samp { font-size: max(12px, 0.875em); }

h1, h2, h3, h4, h5, h6 {
    margin: 0;
    font-weight: 600;
    line-height: 1.2;
    color: var(--text-primary);
}

h1 { font-size: 2.25rem; }
h2 { font-size: 1.875rem; }
h3 { font-size: 1.5rem; }
h4 { font-size: 1.25rem; }
h5 { font-size: 1.125rem; }
h6 { font-size: 1rem; }

p {
    margin: 0 0 1rem;
    line-height: 1.6;
}

a {
    color: var(--primary);
    text-decoration: none;
    transition: var(--transition-fast);
}

a:hover {
    color: var(--primary-dark);
}

/* ========================================================================
   LAYOUT COMPONENTS
   ======================================================================== */

.page-container {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* Header */
.app-header {
    position: sticky;
    top: 0;
    height: var(--header-height);
    background: color-mix(in srgb, var(--bg-primary) 92%, transparent);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
    z-index: var(--z-sticky);
    display: flex;
    align-items: center;
    padding: 0 var(--spacing-lg);
}

/* Fallback for browsers without backdrop-filter support */
@supports not (backdrop-filter: blur(20px)) {
    .app-header {
        background: color-mix(in srgb, var(--bg-primary) 92%, transparent);
    }
}

.app-logo {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--gray-900);
    letter-spacing: -0.02em;
}

.header-nav {
    display: flex;
    align-items: center;
    gap: var(--spacing-md);
    margin-left: auto;
    margin-right: auto;
}

/* ========================================================================
   KNOWLEDGE OVERLAY COMPONENT
   ======================================================================== */

.kb-overlay {
    position: absolute;
    top: clamp(12px, 2vw, 32px);
    right: clamp(12px, 3vw, 40px);
    width: clamp(240px, 24vw, 340px);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 1rem;
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    pointer-events: auto;
}

.kb-overlay.is-hidden {
    display: none;
}

.kb-overlay__close {
    align-self: flex-end;
    border: none;
    background: none;
    font-size: 1.25rem;
    line-height: 1;
    color: var(--text-muted);
    cursor: pointer;
    padding: 2px 6px;
    border-radius: var(--radius-sm);
    margin: -0.5rem -0.5rem -0.5rem 0;
}

.kb-overlay__close:hover {
    color: var(--text-primary);
    background: var(--bg-tertiary);
}

.kb-overlay__header {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    align-items: flex-start;
}

.kb-overlay__eyebrow {
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin: 0 0 0.2rem;
}

.kb-overlay__title {
    font-size: 0.95rem;
    margin: 0;
    color: var(--text-primary);
}

.kb-overlay__btn {
    border: 1px solid var(--border);
    background: var(--bg-tertiary);
    color: var(--text-primary);
    border-radius: var(--radius-sm);
    padding: 0.35rem 0.6rem;
    font-size: 0.78rem;
    cursor: pointer;
    transition: all 0.2s ease;
}

.kb-overlay__btn:hover {
    border-color: var(--primary);
    color: var(--primary);
}

.kb-overlay__stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.4rem;
}

.kb-overlay__stat {
    padding: 0.5rem;
    border-radius: var(--radius);
    background: var(--bg-tertiary);
}

.kb-overlay__stat-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    color: var(--text-muted);
    letter-spacing: 0.05em;
}

.kb-overlay__stat-value {
    display: block;
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--text-primary);
}

.kb-overlay__section {
    border-top: 1px solid var(--border-light);
    padding-top: 0.6rem;
}

.kb-overlay__section h4 {
    margin: 0 0 0.4rem;
    font-size: 0.82rem;
    color: var(--text-secondary);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.kb-overlay__fact {
    padding: 0.35rem 0;
    border-bottom: 1px dashed var(--border-light);
}

.kb-overlay__fact:last-child {
    border-bottom: none;
}

.kb-overlay__fact-title {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 0.2rem;
}

.kb-overlay__fact-meta {
    font-size: 0.75rem;
    color: var(--text-muted);
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
}

.kb-overlay__fact button {
    border: none;
    background: none;
    color: var(--primary);
    font-size: 0.78rem;
    padding: 0;
    cursor: pointer;
}

.kb-overlay__principle {
    font-size: 0.8rem;
    color: var(--text-secondary);
    margin-bottom: 0.45rem;
    display: flex;
    gap: 0.4rem;
}

.kb-overlay__principle span {
    color: var(--primary);
    font-weight: 600;
}

.kb-overlay__footnote {
    font-size: 0.75rem;
    color: var(--text-muted);
    display: flex;
    justify-content: space-between;
    gap: 0.4rem;
    align-items: center;
}

.kb-overlay__loading {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    align-items: center;
    justify-content: center;
    min-height: 140px;
    color: var(--text-secondary);
    text-align: center;
}

@media (max-width: 1200px) {
    .kb-overlay {
        display: none;
    }
}

.header-user-section {
    display: flex;
    align-items: center;
    gap: var(--spacing-md);
}

.user-info {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
}

#current-user-name {
    font-weight: 500;
    color: var(--text-primary);
}

/* Footer */
.app-footer {
    background: color-mix(in srgb, var(--bg-primary) 92%, transparent);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-top: 1px solid var(--border);
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
    padding: var(--spacing-md) var(--spacing-lg);
    margin-top: auto;
}

@supports not (backdrop-filter: blur(20px)) {
    .app-footer {
        background: var(--bg-elevated);
    }
}

.footer-actions {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-md);
}

/* Buttons moved to static/css/components/buttons.css */

/* ========================================================================
   FORMS
   ======================================================================== */

.form-group {
    margin-bottom: var(--spacing-md);
}

.form-label {
    display: block;
    margin-bottom: var(--spacing-xs);
    font-size: 14px;
    font-weight: 500;
    color: var(--text-primary);
}

.input,
.form-input,
.search-input {
    width: 100%;
    padding: 10px 14px;
    font-family: var(--font-sans);
    font-size: 14px;
    font-weight: 400;
    line-height: 1.5;
    color: var(--gray-900);
    background: var(--bg-elevated);
    border: 1px solid var(--gray-300);
    border-radius: var(--radius-md);
    transition: var(--transition-fast);
 }

.input:focus,
.form-input:focus,
.search-input:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-success) 10%, transparent);
}

.input:disabled {
    background: var(--gray-50);
    color: var(--text-muted);
    cursor: not-allowed;
}

textarea.input,
textarea.form-input {
    resize: vertical;
    min-height: 100px;
}

select.input {
    cursor: pointer;
}

/* Form helper / error / actions / row — shared by templates/components/_macros.html
   form macros (form_field, text_input, textarea, select_field, …). Используют
   только design tokens, без hardcoded цветов.

   .form-hint — legacy-alias .form-helper'а; canonical-source консолидирован
   из task_setter_layout.css + grant_wizard.css в эту общую точку (P13).
   Scoped-overrides (.dashboard-sidebar-right .form-hint в developer,
   #mission-constructor-modal .form-hint в data_engineer) сохраняются как
   контекстные. */
.form-helper,
.form-hint {
    margin-top: var(--spacing-xs);
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.45;
}
.form-error {
    margin-top: var(--spacing-xs);
    font-size: 12px;
    color: var(--danger);
    line-height: 1.45;
}
.form-group.has-error .form-input,
.form-group.has-error .input,
.form-group.has-error select,
.form-group.has-error textarea {
    border-color: var(--danger);
}
.form-group.has-error .form-input:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-danger) 10%, transparent);
}
.form-required {
    color: var(--danger);
    margin-left: 2px;
}

/* Inline row layout for fields side-by-side. Wraps gracefully on narrow viewports. */
.form-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--spacing-md);
}

/* Actions row — submit + cancel button alignment.
   Default: right-aligned (form convention). Pass align='space-between' / 'left'
   for alternative layouts. */
.form-actions {
    display: flex;
    gap: var(--spacing-sm);
    justify-content: flex-end;
    margin-top: var(--spacing-lg);
    flex-wrap: wrap;
}
.form-actions--left { justify-content: flex-start; }
.form-actions--space-between { justify-content: space-between; }
.form-actions--center { justify-content: center; }

/* Checkbox / radio — inline label + native input. */
.form-checkbox,
.form-radio {
    display: flex;
    align-items: flex-start;
    gap: var(--spacing-sm);
    cursor: pointer;
    font-size: 14px;
    line-height: 1.45;
    color: var(--text-primary);
}
.form-checkbox input,
.form-radio input {
    margin-top: 2px;
    flex-shrink: 0;
    cursor: pointer;
}
.form-checkbox-text,
.form-radio-text {
    flex: 1;
}

/* ========================================================================
   CARDS
   ======================================================================== */

.card {
    background: var(--bg-elevated);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    overflow: hidden;
    transition: var(--transition);
}

.card:hover {
    box-shadow: var(--shadow-lg);
}

.card-header {
    padding: var(--spacing-md);
    background: var(--gray-50);
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    color: var(--gray-900);
}

.card-body {
    padding: var(--spacing-md);
}

.card-footer {
    padding: var(--spacing-md);
    background: var(--gray-50);
    border-top: 1px solid var(--border);
}

/* ========================================================================
   CULTURAL MAP COMPONENTS
   ======================================================================== */

/* Masonry Grid Layout */
.masonry-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 20px;
    margin-top: 20px;
}

/* Art Card */
.art-card {
    background: var(--bg-elevated);
    border-radius: var(--radius-lg);
    padding: 20px;
    box-shadow: var(--shadow);
    transition: var(--transition);
    break-inside: avoid;
    position: relative;
    overflow: hidden;
}

.art-card::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: linear-gradient(90deg, var(--primary) 0%, var(--primary-light) 100%);
    opacity: 0;
    transition: opacity var(--transition-fast);
}

.art-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-lg);
}

.art-card:hover::before {
    opacity: 1;
}

/* Art Card Type Badge */
.art-card-type {
    display: inline-block;
    padding: 4px 12px;
    border-radius: var(--radius-full);
    font-size: 12px;
    font-weight: 600;
    margin-bottom: 12px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* Art Type Specific Colors */
.art-card[data-type="street-art"] .art-card-type {
    background: color-mix(in srgb, var(--art-street) 15%, transparent);
    color: var(--art-street);
}

.art-card[data-type="music"] .art-card-type {
    background: color-mix(in srgb, var(--art-music) 15%, transparent);
    color: var(--art-music);
}

.art-card[data-type="visual-art"] .art-card-type {
    background: color-mix(in srgb, var(--art-visual) 15%, transparent);
    color: var(--art-visual);
}

.art-card[data-type="design"] .art-card-type {
    background: color-mix(in srgb, var(--art-design) 15%, transparent);
    color: var(--art-design);
}

.art-card[data-type="event"] .art-card-type {
    background: color-mix(in srgb, var(--art-event) 15%, transparent);
    color: var(--art-event);
}

.art-card[data-type="other"] .art-card-type {
    background: color-mix(in srgb, var(--art-other) 15%, transparent);
    color: var(--art-other);
}

/* Art Card Content */
.art-card-title {
    margin: 12px 0;
    font-size: 18px;
    font-weight: 600;
    color: var(--gray-900);
    line-height: 1.4;
}

.art-card-author {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--gray-600);
    font-size: 14px;
    margin-bottom: 12px;
}

.art-card-author svg {
    width: 14px;
    height: 14px;
    opacity: 0.6;
}

.art-card-description {
    color: var(--text-secondary);
    font-size: 14px;
    line-height: 1.6;
    margin: 12px 0;
}

.art-card-location,
.art-card-date {
    font-size: 13px;
    color: var(--gray-500);
    margin: 8px 0;
    display: flex;
    align-items: center;
    gap: 6px;
}

.art-card-location::before {
    content: '📍';
}

.art-card-date::before {
    content: '📅';
}

.art-card-actions {
    display: flex;
    gap: 8px;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--border-light);
}

.art-card-actions .btn {
    flex: 1;
    font-size: 13px;
    padding: 8px 12px;
}

/* Filter Buttons */
.filter-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin: 20px 0;
    padding: 16px;
    background: var(--bg-secondary);
    border-radius: var(--radius-md);
}

.filter-btn {
    padding: 8px 16px;
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    border-radius: var(--radius-full);
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: var(--transition-fast);
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.filter-btn:hover {
    border-color: var(--primary);
    background: var(--primary-lightest);
    transform: translateY(-1px);
}

.filter-btn.active {
    background: var(--primary);
    color: var(--text-inverse);
    border-color: var(--primary);
    box-shadow: var(--shadow-sm);
}

.filter-btn .filter-count {
    background: color-mix(in srgb, var(--bg-primary) 20%, transparent);
    padding: 2px 6px;
    border-radius: var(--radius-full);
    font-size: 12px;
    font-weight: 600;
    margin-left: 4px;
}

.filter-btn.active .filter-count {
    background: color-mix(in srgb, var(--bg-primary) 30%, transparent);
}

/* Art Statistics */
#art-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
    margin: 20px 0;
}

#art-stats .stat-card {
    background: var(--bg-elevated);
    padding: 20px;
    border-radius: var(--radius-lg);
    text-align: center;
    box-shadow: var(--shadow);
    border: 1px solid var(--border-light);
    transition: var(--transition);
}

#art-stats .stat-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

#art-stats .stat-value {
    font-size: 32px;
    font-weight: 700;
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    margin-bottom: 4px;
}

#art-stats .stat-label {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 600;
}

/* Cultural Map Tab */
#tab-cultural-map {
    padding: var(--spacing-lg);
}

#tab-cultural-map .content-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--spacing-lg);
}

#tab-cultural-map .content-header h2 {
    font-size: 24px;
    font-weight: 700;
    color: var(--gray-900);
}

/* ========================================================================
   TABLES
   ======================================================================== */

.table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
}

.table thead {
    background: var(--gray-50);
}

.table th {
    padding: var(--spacing-sm) var(--spacing-md);
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--gray-600);
    text-align: left;
    border-bottom: 2px solid var(--border);
}

.table td {
    padding: var(--spacing-md);
    border-bottom: 1px solid var(--border-light);
    color: var(--text-primary);
}

.table tbody tr {
    transition: var(--transition-fast);
}

.table tbody tr:hover {
    background: var(--gray-50);
}

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

/* Modals moved to static/css/components/modals.css */

/* ========================================================================
   DASHBOARD LAYOUT - FLEX CONTAINERS
   ======================================================================== */

body.dashboard-layout {
    min-height: 100vh;
    margin: 0;
    display: flex;
    flex-direction: column;
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-family: var(--font-sans);
}

body.dashboard-layout > .dashboard-header {
    flex-shrink: 0;
}

body.dashboard-layout > .main-container {
    flex: 1;
    display: flex;
    min-height: 0;
    min-width: 0;
}

body.dashboard-layout .sidebar {
    flex-shrink: 0;
    min-height: 0;
    overflow-y: auto;
}

body.dashboard-layout .main-content {
    flex: 1;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

body.dashboard-layout .view-container,
body.dashboard-layout .view-panel {
    flex: 1;
    min-height: 0;
}

body.dashboard-layout .view-panel {
    display: none;
    overflow: auto;
}

body.dashboard-layout .view-panel.active {
    display: flex;
    flex-direction: column;
}

body.dashboard-layout footer,
body.dashboard-layout .dashboard-footer {
    flex-shrink: 0;
    margin-top: auto;
}

/* ========================================================================
   LOADING & STATES
   NOTE: loading overlay/spinner styles moved to static/css/components/loading.css
   ======================================================================== */

/* Empty State */
.empty-state {
    text-align: center;
    padding: var(--spacing-2xl);
    color: var(--text-muted);
}

.empty-state-icon {
    font-size: 48px;
    margin-bottom: var(--spacing-md);
    opacity: 0.3;
}

.empty-state-title {
    font-size: 18px;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: var(--spacing-sm);
    font-family: var(--font-sans);
}

.empty-state-text {
    color: var(--text-secondary);
    font-size: 14px;
    font-family: var(--font-sans);
}

/* ========================================================================
   NOTIFICATIONS - FIXED
   ======================================================================== */

.notification {
    box-shadow: var(--shadow-md);
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--spacing-md);
    font-family: var(--font-sans);
}

.notification * {
    font-family: var(--font-sans);
}

.notification.success {
    border-left: 3px solid var(--success);
}

.notification.error {
    border-left: 3px solid var(--danger);
}

.notification.warning {
    border-left: 3px solid var(--warning);
}

.notification.info {
    border-left: 3px solid var(--info);
}

/* ========================================================================
   BADGES
   ======================================================================== */

.badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    font-size: 12px;
    font-weight: 600;
    border-radius: var(--radius-full);
    line-height: 1.5;
    font-family: var(--font-sans);
}

.badge-primary {
    background: var(--primary);
    color: var(--text-inverse);
}

.badge-success {
    background: var(--success);
    color: var(--text-inverse);
}

.badge-warning {
    background: var(--warning);
    color: var(--text-inverse);
}

.badge-danger {
    background: var(--danger);
    color: var(--text-inverse);
}

.badge-info {
    background: var(--info);
    color: var(--text-inverse);
}

/* ========================================================================
   SIDEBAR
   ======================================================================== */

.sidebar {
    background: color-mix(in srgb, var(--bg-primary) 92%, transparent);
    border-right: 1px solid var(--border);
    overflow-y: auto;
}

@supports (backdrop-filter: blur(20px)) {
    .sidebar {
        backdrop-filter: blur(20px);
        -webkit-backdrop-filter: blur(20px);
    }
}

.sidebar-section {
    padding: var(--spacing-md);
    border-bottom: 1px solid var(--border);
}

.sidebar-title {
    /* ЕДИНЫЙ канон (ТЗ §6.5 caption / §12, DS-03 2026-07-16): маленький
       капс-ярлык секции — стиль большинства ролей (patron/visionary/
       historian/analyst/citizen). Ролевые дубли удалены; допустимая
       ролевая дельта — ОДНА строка с комментарием (напр. justify-content
       у Аналитика для кнопки-тогглера в заголовке). */
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    margin: 0 0 var(--spacing-sm) 0;
    display: flex;
    align-items: center;
    gap: var(--spacing-xs);
    font-family: var(--font-sans);
}

/* ========================================================================
   KPI COMPONENTS
   ======================================================================== */

.kpi-container {
    display: flex;
    gap: var(--spacing-md);
    align-items: center;
}

.kpi-card {
    background: var(--bg-elevated);
    border-radius: var(--radius-lg);
    padding: var(--spacing-md);
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    box-shadow: var(--shadow);
    transition: var(--transition);
    cursor: pointer;
}

.kpi-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
}

.kpi-value {
    font-size: 24px;
    font-weight: 700;
    color: var(--primary);
    line-height: 1;
    font-family: var(--font-sans);
}

.kpi-label {
    font-size: 12px;
    font-weight: 500;
    color: var(--gray-500);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-family: var(--font-sans);
}

/* Stats Row for Cultural Map */
.stats-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--spacing-md);
    margin-bottom: var(--spacing-lg);
}

.stat-card {
    background: var(--bg-elevated);
    padding: var(--spacing-md);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    border: 1px solid var(--border-light);
}

.stat-value {
    font-size: 28px;
    font-weight: 700;
    color: var(--primary);
    margin-bottom: 4px;
}

.stat-label {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* ========================================================================
   PROGRESS
   ======================================================================== */

.progress {
    height: 8px;
    background: var(--gray-200);
    border-radius: var(--radius-full);
    overflow: hidden;
}

.progress-bar {
    height: 100%;
    background: linear-gradient(90deg, var(--primary) 0%, var(--primary-dark) 100%);
    transition: width 0.3s ease;
}

/* ========================================================================
   UTILITIES
   ======================================================================== */

/* Text */
.text-primary { color: var(--text-primary); }
.text-secondary { color: var(--text-secondary); }
.text-muted { color: var(--text-muted); }
.text-danger { color: var(--danger); }
.text-success { color: var(--success); }
.text-warning { color: var(--warning); }
.text-info { color: var(--info); }

.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }

.text-xs { font-size: 12px; }
.text-sm { font-size: 13px; }
.text-md { font-size: 14px; }
.text-lg { font-size: 16px; }
.text-xl { font-size: 18px; }
.text-2xl { font-size: 24px; }

.font-normal { font-weight: 400; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }

/* Spacing */
.m-0 { margin: 0; }
.mt-sm { margin-top: var(--spacing-sm); }
.mt-md { margin-top: var(--spacing-md); }
.mt-lg { margin-top: var(--spacing-lg); }
.mb-sm { margin-bottom: var(--spacing-sm); }
.mb-md { margin-bottom: var(--spacing-md); }
.mb-lg { margin-bottom: var(--spacing-lg); }

.p-0 { padding: 0; }
.p-sm { padding: var(--spacing-sm); }
.p-md { padding: var(--spacing-md); }
.p-lg { padding: var(--spacing-lg); }

/* Display */
.hidden { display: none; }
.block { display: block; }
.flex { display: flex; }
.inline-flex { display: inline-flex; }

.flex-col { flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }

.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }

.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.justify-end { justify-content: flex-end; }

.flex-1 { flex: 1; }
.flex-grow { flex-grow: 1; }
.flex-shrink-0 { flex-shrink: 0; }

.gap-xs { gap: var(--spacing-xs); }
.gap-sm { gap: var(--spacing-sm); }
.gap-md { gap: var(--spacing-md); }
.gap-lg { gap: var(--spacing-lg); }

/* Width */
.w-full { width: 100%; }
.w-auto { width: auto; }

/* Borders */
.border { border: 1px solid var(--border); }
.border-t { border-top: 1px solid var(--border); }
.border-b { border-bottom: 1px solid var(--border); }
.border-l { border-left: 1px solid var(--border); }
.border-r { border-right: 1px solid var(--border); }

.rounded { border-radius: var(--radius); }
.rounded-sm { border-radius: var(--radius-sm); }
.rounded-md { border-radius: var(--radius-md); }
.rounded-lg { border-radius: var(--radius-lg); }
.rounded-xl { border-radius: var(--radius-xl); }
.rounded-full { border-radius: var(--radius-full); }

/* Shadows */
.shadow-xs { box-shadow: var(--shadow-xs); }
.shadow-sm { box-shadow: var(--shadow-sm); }
.shadow { box-shadow: var(--shadow); }
.shadow-md { box-shadow: var(--shadow-md); }
.shadow-lg { box-shadow: var(--shadow-lg); }
.shadow-xl { box-shadow: var(--shadow-xl); }
.shadow-2xl { box-shadow: var(--shadow-2xl); }

/* ========================================================================
   RESPONSIVE
   ======================================================================== */

@media (max-width: 1024px) {
    .sidebar {
        width: 280px;
    }

    .masonry-grid {
        grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    }
}

@media (max-width: 768px) {
    :root {
        /* ТЗ §6.2: mobile-хедер 48px (--ds-header-h-mobile) */
        --header-height: var(--ds-header-h-mobile, 48px);
    }

    .app-header {
        padding: 0 var(--spacing-md);
    }

    .kpi-container {
        flex-direction: column;
        align-items: stretch;
    }

    .sidebar {
        display: none;
    }

    /* Cultural Map Mobile */
    .masonry-grid {
        grid-template-columns: 1fr;
        gap: 16px;
    }

    .filter-buttons {
        padding: 12px;
        gap: 8px;
    }

    .filter-btn {
        font-size: 13px;
        padding: 6px 12px;
    }

    #art-stats {
        grid-template-columns: 1fr 1fr;
    }

    .art-card {
        padding: 16px;
    }
}

@media (max-width: 480px) {
    .header-title {
        font-size: 16px;
    }

    /* DS-03 (§12): мобильный компакт-твик .btn удалён — канон components/
       buttons.css (на дашбордах он и так выигрывал каскадом; на standalone-
       страницах без buttons.css твик ужимал кнопки до 13px/8px — ПРОТИВ
       touch-нормы ТЗ §6.4 ≥44px; VR: plans 1.72%, research_templates 0.34% —
       кнопки стали крупнее, эталоны обновлены). */

    .art-card-actions {
        flex-direction: column;
    }

    .art-card-actions .btn {
        width: 100%;
    }

    #art-stats {
        grid-template-columns: 1fr;
    }
}

/* ========================================================================
   UNIVERSAL ACTIVE TAB STYLES (P1.5)
   ======================================================================== */

/* Улучшенные стили для активных табов во всех дашбордах */
.view-tab.active,
.view-switcher-btn.active,
.tab.active,
.central-view-tab.active,
.view-tabs .view-tab.active,
.list-view-tab.active {
    position: relative;
    font-weight: 600;
    box-shadow: 0 2px 4px color-mix(in srgb, var(--status-info) 20%, transparent);
    transform: translateY(-1px);
    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Индикатор активного таба (нижняя линия) */
.view-tab.active::before,
.view-switcher-btn.active::before,
.tab.active::after,
.central-view-tab.active::before {
    content: '';
    position: absolute;
    bottom: -1px;
    left: 0;
    right: 0;
    height: 2px;
    background: var(--primary, var(--info));
    transform: scaleX(1);
    transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Для табов с вертикальной ориентацией */
.tab.active::after {
    bottom: auto;
    top: 0;
    left: -1px;
    right: auto;
    width: 2px;
    height: 100%;
}

/* Улучшенные hover эффекты для неактивных табов */
.view-tab:not(.active):hover,
.view-switcher-btn:not(.active):hover,
.tab:not(.active):hover,
.central-view-tab:not(.active):hover {
    transform: translateY(-1px);
    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

/* ========================================================================
   PRINT
   ======================================================================== */

@media print {
    .app-header,
    .app-footer,
    .sidebar,
    .btn,
    .modal, .modal-overlay {
        display: none !important;
    }
}
/* TEP Wizard Styles */
.tep-wizard {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.tep-tabs {
    display: flex;
    border-bottom: 2px solid var(--border);
    background: var(--bg-secondary);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    overflow: hidden;
}

.tep-tab {
    flex: 1;
    padding: 0.75rem 1rem;
    border: none;
    background: transparent;
    cursor: pointer;
    color: var(--text-muted);
    font-weight: 500;
    border-bottom: 3px solid transparent;
    margin-bottom: -2px;
    transition: all 0.3s ease;
    position: relative;
}

.tep-tab:hover {
    background: var(--bg-hover);
    color: var(--text-secondary);
}

.tep-tab.active {
    color: var(--primary);
    background: var(--bg-elevated);
    border-bottom-color: var(--primary);
}

.tep-tab.active::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: var(--color-primary);
}

.tep-tab-content {
    display: none;
    padding: 1.5rem 0;
    animation: tepFadeIn 0.3s ease-out;
}

.tep-tab-content.active {
    display: block;
}

.tep-tab-content h4 {
    margin: 0 0 1.5rem 0;
    color: var(--text-primary);
    font-size: 1.125rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--border);
}

.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 1.25rem;
}

.tep-modal .form-group {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.tep-modal .form-group label {
    font-weight: 500;
    color: var(--text-secondary);
    font-size: 0.875rem;
}

.tep-modal .form-input {
    padding: 0.625rem 0.875rem;
    border: 1px solid var(--border-strong, var(--border));
    border-radius: var(--radius);
    font-size: 0.95rem;
    transition: all 0.2s ease;
    background: var(--bg-elevated);
    color: var(--text-primary);
}

.tep-modal .form-input:focus {
     border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-success) 15%, transparent);
}

.tep-modal .form-input::placeholder {
    color: var(--text-muted);
    font-size: 0.875rem;
}

.tep-modal .form-group small {
    color: var(--text-muted);
    font-size: 0.8rem;
    margin-top: -0.25rem;
}

/* fadeIn keyframes moved to static/css/components/animations.css */

@keyframes tepFadeOut {
    from {
        opacity: 1;
        transform: translateY(0);
    }
    to {
        opacity: 0;
        transform: translateY(-10px);
    }
}
/* Стили для информации о генерации */
.generation-info {
    margin-top: 1rem;
    padding: 0.75rem;
    background: var(--bg-secondary);
    border-radius: var(--radius-sm);
    border-left: 3px solid var(--primary, var(--info));
}

.generation-info ul {
    margin: 0.5rem 0;
    padding-left: 1.5rem;
}

/* Стили для контейнера статистики мастер-плана */
.masterplan-stats-container {
    margin-top: 1.5rem;
    padding: 1rem;
    background: var(--bg-elevated);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

.masterplan-stats-container h4 {
    margin-bottom: 1rem;
    color: var(--text-primary);
    border-bottom: 2px solid var(--primary);
    padding-bottom: 0.5rem;
}

.stats-section {
    margin-bottom: 1rem;
    padding: 0.75rem;
    background: var(--bg-light, var(--bg-secondary));
    border-radius: var(--radius-sm);
}

.stats-section h5 {
    margin-bottom: 0.5rem;
    color: var(--text-secondary);
}

.stats-list {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0;
}

.stats-list li {
    padding: 0.25rem 0;
    border-bottom: 1px solid var(--border-color, var(--border));
}

.stats-list li:last-child {
    border-bottom: none;
}

/* Анимация для master-кнопки автопилота мастерпланирования */
#btn-autopilot {
    position: relative;
    overflow: hidden;
    transition: all 0.3s ease;
}

#btn-autopilot:not(:disabled):hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px color-mix(in srgb, var(--status-info) 40%, transparent);
}

#btn-autopilot:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* === НОВЫЙ БЛОК: Стили для карты и мудборда v2 === */

/* Улучшенный маркер для арт-объектов */
.art-marker {
    width: 36px;
    height: 36px;
    border-radius: 50% 50% 50% 0;
    transform: rotate(-45deg);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    border: 2px solid white;
}
.art-marker svg {
    transform: rotate(45deg);
    width: 20px;
    height: 20px;
    color: var(--text-inverse);
}

/* Стандартный маркер для других объектов */
.default-marker {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.3);
    border: 2px solid white;
    box-sizing: border-box; /* ✅ ИСПРАВЛЕНО: учитываем border */
    overflow: hidden; /* ✅ ДОБАВЛЕНО: обрезаем содержимое */
    flex-shrink: 0; /* ✅ ДОБАВЛЕНО: маркер не сжимается */
    text-align: center; /* ✅ ДОБАВЛЕНО: центрирование текста */
    line-height: 1; /* ✅ ДОБАВЛЕНО: выравнивание по вертикали */
}

/* Подсветка карточки при переходе с карты */
.art-card.highlight {
    transition: all 0.5s ease-in-out;
    box-shadow: 0 0 0 3px var(--primary), 0 8px 25px rgba(0,0,0,0.4);
    transform: scale(1.02);
}

/* ========================================================================
   TEXT OVERFLOW & BUTTON UTILITIES (shared across all dashboards)
   ======================================================================== */

/* Clamp text to N lines (use with --line-clamp: N) */
.text-clamp {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: var(--line-clamp, 2);
    overflow: hidden;
    word-break: break-word;
}

/* Single-line truncation */
.text-truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Safe word-wrapping for long content */
.text-wrap-break {
    overflow-wrap: break-word;
    word-break: break-word;
}

/* Map / chart error fallback */
.map-error,
.chart-error {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    min-height: 120px;
    color: var(--text-secondary);
    background: var(--bg-secondary);
    border: 1px dashed var(--border);
    border-radius: var(--radius, 8px);
    font-size: 0.875rem;
    padding: 1rem;
    text-align: center;
}

/* Empty state hint (reusable) */
.empty-state-hint {
    padding: 1rem;
    color: var(--text-secondary);
    background: var(--bg-secondary);
    border: 1px dashed var(--border);
    border-radius: var(--radius, 8px);
    text-align: center;
    font-size: 0.875rem;
    line-height: 1.5;
}

/* Filter / tag pill (reusable) */
.filter-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 0.8125rem;
    max-width: 100%;
}

.filter-pill-label {
    color: var(--text-muted);
    white-space: nowrap;
}

.filter-pill-value {
    color: var(--text-primary);
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.filter-pill-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    padding: 0;
    border: none;
    background: var(--bg-tertiary);
    color: var(--text-muted);
    border-radius: 50%;
    cursor: pointer;
    font-size: 0.75rem;
    line-height: 1;
    flex-shrink: 0;
    transition: background 0.15s, color 0.15s;
}

.filter-pill-remove:hover {
    background: var(--danger);
    color: var(--text-inverse);
}

/* Clue basket (analyst) */
.clue-item {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px 10px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm, 6px);
    margin-bottom: 6px;
}

.clue-text {
    flex: 1;
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--text-primary);
    overflow-wrap: break-word;
    word-break: break-word;
}

.clue-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: none;
    background: var(--bg-tertiary);
    color: var(--text-muted);
    border-radius: 50%;
    cursor: pointer;
    font-size: 0.875rem;
    line-height: 1;
    flex-shrink: 0;
    transition: background 0.15s, color 0.15s;
}

.clue-remove:hover {
    background: var(--danger);
    color: var(--text-inverse);
}

/* Grant card (producer/patron JS-rendered) */
.grant-card {
    padding: 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-elevated);
    margin-bottom: 12px;
}

/* Scenario save controls (visionary JS-rendered) */
.scenario-save-controls {
    margin: 10px 0 15px 0;
    padding: 10px;
    background: var(--bg-secondary);
    border-radius: var(--radius-sm);
    border-left: 3px solid var(--primary);
    color: var(--text-primary);
}

/* ============================================================
   Survey wizard (_survey_wizard_steps.html) — form controls и
   геокодер-dropdown.
   Разметка мастера использует Bootstrap-классы (form-control,
   list-group, form-row, col, form-control-sm). Мастер встраивается
   в ДВА места: дашборд урбаниста (bootstrap НЕ грузится) и отдельную
   страницу /admin survey_wizard (bootstrap грузится). dashboard-base.css
   грузится в обоих, поэтому правила здесь — единый источник: поля во всю
   ширину + результаты геокодера как нормальный плавающий dropdown с фоном
   (а не прозрачное наложение на поля «Название/Описание» ниже).
   var(--urbanist-*) с хардкод-fallback — на /admin urbanist.css не загружен.
   ============================================================ */
.survey-wizard label {
    display: block;
    margin-bottom: 6px;
    font-weight: 600;
    color: var(--text-primary);
}

.survey-wizard .form-control {
    display: block;
    width: 100%;
    padding: 10px 14px;
    font-size: 0.95rem;
    line-height: 1.5;
    color: var(--text-primary);
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    transition: var(--transition-fast);
    box-sizing: border-box;
}

.survey-wizard .form-control:focus {
     border-color: var(--urbanist-primary, var(--color-primary));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-success) 15%, transparent);
}

.survey-wizard .form-control::placeholder {
    color: var(--text-tertiary, var(--gray-400));
}

.survey-wizard textarea.form-control {
    min-height: 96px;
    resize: vertical;
}

.survey-wizard textarea#description {
    min-height: 120px;
}

.survey-wizard .form-control-sm {
    padding: 7px 10px;
    font-size: 0.85rem;
    border-radius: 10px;
}

.survey-wizard small.form-text {
    display: block;
    margin-top: 5px;
    font-size: 0.8rem;
    line-height: 1.4;
    color: var(--text-secondary);
}

/* Bootstrap-хелперы, которых нет без bootstrap.min.css */
.survey-wizard .small,
.survey-wizard small {
    font-size: 0.8rem;
    line-height: 1.4;
}

.survey-wizard .text-muted {
    color: var(--text-secondary);
}

.survey-wizard .card-title {
    font-size: 0.95rem;
    font-weight: 600;
    margin: 0 0 8px;
}

.survey-wizard .card-text {
    margin: 0 0 10px;
}

/* Подформа «новый город/район»: ряды инпутов в строку (flex, чтобы
   3 поля координат помещались в ряд; перебивает глобальный .form-row grid). */
.survey-wizard .form-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.survey-wizard .form-row > .col {
    flex: 1 1 0;
    min-width: 130px;
}

/* Результаты геокодера — плавающий dropdown, не перекрывает поля ниже.
   Якорится к .form-group (у неё position: relative). */
.survey-wizard #citySearchResults {
    position: absolute;
    top: 100%;
    left: 0;
    width: 100%;
    margin-top: 4px;
    z-index: 1200;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: 0 14px 32px color-mix(in srgb, var(--dark) 18%, transparent);
    max-height: 300px;
    overflow-y: auto;
    padding: 4px;
}

.survey-wizard #citySearchResults .list-group-item {
    display: block;
    width: 100%;
    padding: 10px 12px;
    background: transparent;
    border: none;
    border-radius: var(--radius-md);
    color: var(--text-primary);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.survey-wizard #citySearchResults a.list-group-item-action:hover {
    background: var(--urbanist-primary-light, var(--primary-lightest));
    color: var(--urbanist-primary, var(--color-primary));
}

.survey-wizard #citySearchResults .list-group-item + .list-group-item {
    border-top: 1px solid var(--border);
}

.survey-wizard #citySearchResults .list-group-item-light {
    background: transparent;
    color: var(--text-secondary);
}

/* Подформа нового города поверх соседних полей */
.survey-wizard #newCityForm {
    position: relative;
    z-index: 1100;
}
/* ── A11y (ТЗ §12): видимый фокус для полного keyboard-flow ─────────────────
   :focus-visible срабатывает только при клавиатурной навигации (мышь кольцо
   не рисует). Инпуты с собственным focus-ring (box-shadow) не затрагиваются —
   их правила специфичнее. */
:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}
[role="tab"]:focus-visible {
    outline-offset: -2px; /* кольцо внутрь — вкладки часто в плотных рядах с overflow */
}

/* §6.9 declutter: нативный accordion сайдбара (канон — перенос inline-стилей
   Аналитика 2026-05-25; у Аналитика остаётся идентичная inline-копия, каскад
   не меняет вид). Используется для «убрать с первого уровня» без потери
   функциональности: id и обработчики внутри details не меняются. */
.sv-accordion { margin-bottom: 8px; }
.sv-accordion > summary {
    cursor: pointer; list-style: none; font-weight: 600; font-size: .95rem;
    padding: 4px 0; display: flex; align-items: center; gap: 6px;
    color: var(--text-primary);
}
.sv-accordion > summary::-webkit-details-marker { display: none; }
.sv-accordion > summary::before {
    content: '▸'; font-size: .8rem; color: var(--text-secondary, var(--gray-500));
    transition: transform .15s ease;
}
.sv-accordion[open] > summary::before { transform: rotate(90deg); }

/* ── Глобальные исследования: полоса шильдиков над картой ─────────────────
   Глобальное исследование живёт сразу в нескольких городах, поэтому у него
   нет одной точки на карте. Отдельная полоса вместо маркера. */
/* Шильдики ПЛАВАЮТ НАД картой, а не занимают полосу над ней. Прежняя
   раскладка (block + border-bottom) сдвигала карту вниз на всю свою высоту —
   на широком экране это была пустая лента во всю ширину ради одного чипа.
   Родитель .map-wrapper уже position: relative (map_overview.css:490).
   Левый верх свободен: NavigationControl в проекте всегда 'top-right'. */
.global-studies-strip {
    position: absolute;
    top: 12px;
    left: 12px;
    z-index: 1150;                 /* выше карты, ниже модалок (1500+) */
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    max-width: min(320px, calc(100% - 24px));
    max-height: calc(100% - 24px);
    overflow-y: auto;
    padding: 0;
    border: 0;
    background: none;
    /* Контейнер прозрачен для мыши — карта тянется в промежутках между
       чипами; сами чипы события ловят. */
    pointer-events: none;
}
.global-studies-strip[hidden] { display: none; }
.global-study-chip {
    pointer-events: auto;
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    max-width: 100%;
    padding: 7px 13px;
    border: 1px solid var(--border-color, var(--border));
    border-radius: 10px;
    background: var(--bg-primary, var(--white));
    box-shadow: 0 2px 10px color-mix(in srgb, var(--dark) 12%, transparent);
    cursor: pointer;
    text-align: left;
    text-decoration: none;
    /* Только цвет и тень. Подъём `transform: translateY(-1px)` на :hover убран:
       шильдик приподнимался под курсором и возвращался обратно, и это читалось
       как «плавает, но возвращается на место». Отклик на наведение нужен,
       смещение самого элемента — нет. */
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.global-study-chip:hover {
    border-color: var(--accent-color, var(--accent));
    box-shadow: 0 4px 16px color-mix(in srgb, var(--dark) 18%, transparent);
}
.global-study-chip:focus-visible {
    outline: 2px solid var(--accent-color, var(--accent));
    outline-offset: 2px;
}
.global-study-chip__title {
    font-size: 0.82rem;
    font-weight: 600;
    line-height: 1.25;
    color: var(--text-primary, var(--dark));
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}
.global-study-chip__meta {
    font-size: 0.7rem;
    line-height: 1.3;
    color: var(--text-secondary);
}
.global-studies-strip__error {
    pointer-events: auto;
    font-size: 0.8rem;
    color: var(--danger, var(--danger-dark));
    background: var(--bg-primary, var(--white));
    border: 1px solid var(--danger, var(--danger-dark));
    border-radius: 10px;
    padding: 7px 13px;
    box-shadow: 0 2px 10px color-mix(in srgb, var(--dark) 12%, transparent);
}
/* На узком экране карта и так тесная — чип не должен её закрывать. */
@media (max-width: 640px) {
    .global-studies-strip {
        top: 8px;
        left: 8px;
        max-width: calc(100% - 16px);
        max-height: 40%;
    }
    .global-study-chip { padding: 6px 10px; }
}
/* Удаление — единственное необратимое действие в списке, поэтому оно
   отличается цветом, а не только текстом. */
.gs-danger { color: var(--danger, var(--danger-dark)); }
.gs-danger:hover { background: var(--danger-bg, var(--status-danger-bg)); }

/* ── Глобальные исследования: экран управления («Шаблоны анкет») ─────────
   Цвета берутся из токенов, а не зашиваются в JS: гейт scan_ux_contract
   считает hex в скриптах, и он же ловит нечитаемость в тёмной теме. */
.gs-row-box { padding: 10px 0; border-bottom: 1px solid var(--border-light, var(--bg-tertiary)); }
.gs-panel {
    margin-top: 8px;
    padding: 10px;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}
.gs-input {
    flex: 1;
    min-width: 180px;
    width: 100%;
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.gs-error { color: var(--danger, var(--danger-dark)); }

/* По-секционный коллапс сайдбара (макрос _macros.html sidebar_section).
   QA 2026-08-12: правила жили только в citizen_dashboard.css — на дашбордах
   без него (Горинженер, ЖКХ) кнопка ▼ ничего видимого не делала даже с
   обработчиком. JS: shared/collapsible-panels.js (делегированный, один на всех). */
.sidebar-section.collapsible.collapsed .sidebar-section-content {
    max-height: 0;
    opacity: 0;
    overflow: hidden;
}
.sidebar-section.collapsible.collapsed .sidebar-section-toggle {
    transform: rotate(-90deg);
}
/* ── css/shared/inline-utilities.css ── */
/*
 * ВАЖНО (анти-дуп): классы u-hidden / u-flex-1 / u-m-0 / u-w-full /
 * u-text-sm|xs|lg|muted|danger определены в КАНОНЕ components/utilities.css
 * (грузится в _base_dashboard ПОСЛЕ этого файла) — здесь их НЕ переопределять.
 * Урок 2026-07-16: локальный u-text-sm (0.85rem) молча проигрывал канону
 * (0.8125rem) — tests/test_utility_collisions.py держит инвариант.
 *
 * shared/inline-utilities.css — утилиты для миграции статических inline-styles
 * (ТЗ F-07/§12: в мигрированных шаблонах статических style="..." быть не должно).
 *
 * Подключается в _base_dashboard.html ПОСЛЕ dashboard-base.css.
 * Классы намеренно узкие и соответствуют самым частым inline-паттернам
 * (аудит 2026-07-16: топ-12 паттернов Урбаниста покрывают ~70 вхождений).
 *
 * Видимость: .u-hidden вместо style="display:none". JS-переключение —
 * el.classList.toggle('u-hidden'); НЕ el.style.display (см. uVisible()
 * в shared/ui-utils.js — устойчив к обоим механизмам).
 */


/* Вертикальные стеки (flex-column + gap) */
.u-stack-3 { display: flex; flex-direction: column; gap: 3px; }
.u-stack-4 { display: flex; flex-direction: column; gap: 4px; }
.u-stack-6 { display: flex; flex-direction: column; gap: 6px; }

/* Горизонтальные ряды */
.u-row-between { display: flex; justify-content: space-between; align-items: center; }
.u-row-center { display: inline-flex; align-items: center; gap: 4px; }

/* Типографика (в связке со шрифтовым полом ≥12px) */
.u-title-sm { font-weight: 700; font-size: 0.95rem; margin-bottom: 4px; }
.u-mb-8 { margin-bottom: 8px; }
.u-mt-8 { margin-top: 8px; }

/* Частые паттерны Девелопера / Инженера данных (F-07 часть 3) */
.u-mt-2 { margin-top: 0.5rem; }
.u-mt-3 { margin-top: 0.75rem; }
.u-mt-neg-1 { margin-top: -0.25rem; }
.u-row-icon-sm { font-size: 0.8rem; display: flex; align-items: center; gap: 0.3rem; }
.u-h4-compact { margin: 0 0 0.5rem 0; font-size: 1rem; }

/* Частые паттерны Соц-архитектора (F-07 часть 4) */
.u-icon-lg { font-size: 1.2rem; }
.u-caption-muted { font-size: 0.82rem; color: var(--text-muted); }
.u-label-block { display: block; margin-bottom: 3px; color: var(--text-secondary, var(--gray-500)); }

/* Частые паттерны Горожанина / Куратора (F-07 часть 5) */
.u-mt-0 { margin-top: 0; }
.u-mb-06 { margin-bottom: 0.6rem; }
.u-mb-07 { margin-bottom: 0.7rem; }
.u-mr-auto { margin-right: auto; }
.u-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.u-chip-input { padding: 6px 10px; border: 1px solid var(--border-color, var(--border)); border-radius: var(--radius-md); font-size: 0.85rem; }
.u-card-plain { border: 1px solid var(--border-color, var(--border)); border-radius: 10px; padding: 14px; }
/* .78rem/.72rem подняты до пола 12px (F-04) — визуально идентичны post-sweep */
.u-caption { font-size: 0.78rem; }
.u-caption-mb { font-size: 0.78rem; margin-bottom: 0.4rem; }
.u-overline { font-size: 0.75rem; text-transform: uppercase; color: var(--text-muted); }

/* Кликабельная карточка-кнопка (частый паттерн списков Урбаниста) */
.u-card-btn {
    text-align: left;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 14px 16px;
    background: var(--bg-elevated, var(--white));
    cursor: pointer;
}

/* Частые паттерны ОКЦ-менеджера / Исследователя (F-07 часть 6) */
.u-form-label { display: block; margin-bottom: 4px; font-size: 13px; font-weight: 600; }
.u-form-label-sm { display: block; margin-bottom: 4px; font-size: 12px; font-weight: 600; }
.u-input-full { width: 100%; padding: 8px; border: 1px solid var(--border); border-radius: 5px; }
.u-mb-10 { margin-bottom: 10px; }
.u-mb-14 { margin-bottom: 14px; }
.u-my-6 { margin: 6px 0; }
.u-ml-auto { margin-left: auto; }
.u-grid-2-form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
/* Оверлей модалки: display:none остаётся INLINE (контракт style.display в JS),
   класс даёт остальное; JS ставит display:flex — flex-выравнивание из класса. */
.u-modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, .5); z-index: 9999; justify-content: center; align-items: center; }
.u-modal-panel { background: var(--bg-elevated, var(--white)); padding: 24px; border-radius: var(--radius-md); width: 94%; max-height: 90vh; overflow-y: auto; }
.u-mw-420 { max-width: 420px; }
.u-mw-500 { max-width: 500px; }
.u-mw-520 { max-width: 520px; }
.u-mw-560 { max-width: 560px; }
.u-actions-end { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; }

/* F-07 часть 7 — хвост частотных паттернов (Урбанист/Девелопер/ИД/Архитектор/Куратор) */
.u-mb-3 { margin-bottom: 0.75rem; }
.u-mb-4 { margin-bottom: 1rem; }
.u-mt-4 { margin-top: 1rem; }
.u-p-3 { padding: 0.75rem; }
.u-row-3 { display: flex; align-items: center; gap: 0.75rem; }
.u-card-soft { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 10px; padding: 1rem; margin-bottom: 1rem; }
.u-progress-track { background: var(--border); border-radius: var(--radius-sm); height: 6px; overflow: hidden; }
.u-check-row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; cursor: pointer; }
/* .72rem поднят до пола 12px (F-04) */
.u-half-btn { flex: 1; min-width: 46%; font-size: 0.75rem; padding: 4px; border: 1px solid var(--border); border-radius: var(--radius); background: transparent; cursor: pointer; }
/* ── css/components/animations.css ── */
/**
 * Shared keyframe animations — единый источник правды.
 *
 * Все дашборды должны использовать эти анимации вместо
 * дублирования @keyframes в своих CSS файлах.
 *
 * Подключается через dashboard-base.css → @import.
 */

/* ========================================================================
   FADE ANIMATIONS
   ======================================================================== */

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes fadeInDown {
  from {
    opacity: 0;
    transform: translateY(-20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ========================================================================
   SLIDE ANIMATIONS
   ======================================================================== */

@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateX(-20px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes slideInRight {
  /* §7.2/WCAG: без opacity-fade — на промежуточном кадре 0<opacity<1 текст
     композитится с фоном и проваливает контраст (напр. .notification-message
     тоста). Вход из-за правого края уже сообщает появление; fade избыточен. */
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(100%);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-100%);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ========================================================================
   PULSE / LOADING ANIMATIONS
   ======================================================================== */

/* Универсальный pulse (opacity) — для loading-индикаторов, skeleton, placeholders */
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* Pulse с масштабированием — для привлечения внимания (кнопки, badges) */
@keyframes pulse-scale {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}

/* Pulse с масштабированием и opacity — для маркеров на карте, иконок */
@keyframes pulse-full {
  0%, 100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.05);
    opacity: 0.7;
  }
}

/* ========================================================================
   SPINNER
   ======================================================================== */

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

/* ========================================================================
   SKELETON / SHIMMER
   ======================================================================== */

@keyframes skeleton-shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

/* ========================================================================
   MODAL ANIMATIONS
   ======================================================================== */

@keyframes modalSlideIn {
  from {
    opacity: 0;
    transform: translateY(-30px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}
/* ── css/components/loading.css ── */
/**
 * Loading Overlay + Spinner (shared component)
 * Canonical implementation to avoid conflicts between main.css and dashboard-base.css.
 */

.loading-overlay {
  position: fixed;
  inset: 0;
  background: var(--bg-overlay, color-mix(in srgb, var(--bg-primary) 95%, transparent));
  z-index: var(--z-loading);
  display: none;
  align-items: center;
  justify-content: center;
}

@supports (backdrop-filter: blur(10px)) {
  .loading-overlay {
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
  }
}

.loading-overlay,
.loading-overlay * {
  font-family: var(--font-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif);
}

.loading-overlay.active {
  display: flex;
}

.loading-overlay.show {
  display: flex;
}

.loading-overlay.is-visible {
  display: flex;
}

.loading-content {
  text-align: center;
}

.loading-spinner {
  width: 48px;
  height: 48px;
  border: 4px solid var(--gray-200, var(--border));
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  margin: 0 auto var(--spacing-md, 16px);
}

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

/* Back-compat aliases used across templates */
.loading-message {
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 500;
}

.loading-spinner-small {
  width: 20px;
  height: 20px;
  border: 3px solid var(--gray-200, var(--border));
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

/* @keyframes spin — moved to components/animations.css */
/* appSpin is an alias for spin — use spin instead */
/* ── css/components/empty-states.css ── */
/* ========================================================================
   EMPTY STATES - универсальные стили для всех дашбордов
   ======================================================================== */

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--spacing-2xl, 3rem) var(--spacing-lg, 1.5rem);
    text-align: center;
    min-height: 300px;
}

.empty-icon {
    font-size: 4rem;
    margin-bottom: var(--spacing-lg, 1.5rem);
    opacity: 0.6;
}

.empty-title {
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: var(--spacing-sm, 0.5rem);
}

.empty-text {
    font-size: 0.875rem;
    color: var(--text-secondary, var(--gray-500));
    margin-bottom: var(--spacing-lg, 1.5rem);
    max-width: 400px;
    line-height: 1.6;
}

.empty-state .btn {
    margin-top: var(--spacing-md, 1rem);
}

/* Loading state */
.loading-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--spacing-2xl, 3rem) var(--spacing-lg, 1.5rem);
    text-align: center;
    min-height: 300px;
}

.loading-state .loading-spinner {
    margin-bottom: var(--spacing-md, 1rem);
}

.loading-state p {
    font-size: 0.875rem;
    color: var(--text-secondary, var(--gray-500));
    margin: 0;
}

/* Compact variants */
.empty-state--compact {
    padding: var(--spacing-lg, 1.5rem);
    min-height: 200px;
}

.empty-state--compact .empty-icon {
    font-size: 2.5rem;
    margin-bottom: var(--spacing-md, 1rem);
}

.empty-state--compact .empty-title {
    font-size: 1rem;
}

.empty-state--compact .empty-text {
    font-size: 0.8125rem;
}

.loading-state--compact {
    padding: var(--spacing-lg, 1.5rem);
    min-height: 200px;
}

.loading-state--compact .loading-spinner {
    width: 32px;
    height: 32px;
    border-width: 3px;
}
/* ── css/components/drawer.css ── */
/* ========================================================================
   DRAWER COMPONENT - универсальный для всех дашбордов
   ======================================================================== */

.drawer {
    position: fixed;
    top: 0;
    bottom: 0;
    width: 320px;
    max-width: 90vw;
    background: var(--bg-base, var(--white));
    z-index: var(--z-modal, 1000);
    transform: translateX(-100%);
    transition: transform 0.3s ease;
    overflow-y: auto;
    box-shadow: 4px 0 20px rgba(0,0,0,0.15);
}

.drawer.drawer-right {
    left: auto;
    right: 0;
    transform: translateX(100%);
    box-shadow: -4px 0 20px rgba(0,0,0,0.15);
}

.drawer.active {
    transform: translateX(0);
}

.drawer-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: calc(var(--z-modal, 1000) - 1);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
}

.drawer-backdrop.active {
    opacity: 1;
    visibility: visible;
}

.drawer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--spacing-md, 1rem);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    background: var(--bg-base, var(--white));
    z-index: 1;
}

.drawer-close {
    padding: var(--spacing-xs, 0.5rem);
    border: none;
    background: transparent;
    cursor: pointer;
    border-radius: var(--radius-sm, 4px);
    color: var(--text-secondary, var(--gray-500));
    font-size: 1.5rem;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
}

.drawer-close:hover {
    background: var(--bg-secondary);
    color: var(--text-primary);
}

.drawer-content {
    padding: var(--spacing-md, 1rem);
}

/* Toggle buttons для мобильных */
.sidebar-toggle {
    display: none;
    position: fixed;
    bottom: var(--spacing-lg, 1.5rem);
    padding: var(--spacing-sm, 0.75rem);
    border: none;
    border-radius: 50%;
    background: var(--primary, var(--info));
    color: var(--text-inverse);
    box-shadow: 0 4px 12px rgba(0,0,0,0.2);
    cursor: pointer;
    z-index: var(--z-sticky, 999);
    width: 48px;
    height: 48px;
    align-items: center;
    justify-content: center;
    transition: transform 0.2s ease, background 0.2s ease;
}

.sidebar-toggle:hover {
    background: var(--primary-hover, var(--info-dark));
    transform: scale(1.1);
}

.sidebar-toggle:active {
    transform: scale(0.95);
}

.sidebar-toggle-left {
    left: var(--spacing-md, 1rem);
}

.sidebar-toggle-right {
    right: var(--spacing-md, 1rem);
}

@media (max-width: 1024px) {
    .sidebar-toggle {
        display: flex;
    }
}
/* ── css/components/buttons.css ── */
/**
 * Buttons (shared component)
 * Extracted from dashboard-base.css to avoid conflicts and to be reusable on non-dashboard pages.
 */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-xs);
  min-height: var(--btn-height-md);
  padding: var(--btn-padding-md);
  font-family: var(--font-sans);
  font-size: var(--btn-font-md);
  font-weight: 500;
  line-height: 1;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: var(--transition);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  text-decoration: none;
  outline: 2px solid transparent;
  outline-offset: 2px;
  position: relative;
  user-select: none;
}

.btn:focus-visible {
  outline-color: var(--primary);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary) 20%, transparent);
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

/* Variants */
.btn-primary {
  background: var(--primary);
  color: var(--text-inverse, var(--white));
  border-color: var(--primary);
}

.btn-primary:hover:not(:disabled) {
  background: var(--primary-dark);
  border-color: var(--primary-dark);
}

.btn-secondary {
  background: var(--bg-elevated, var(--white));
  color: var(--text-secondary);
  border-color: var(--border-strong, var(--gray-300));
}

.btn-secondary:hover:not(:disabled) {
  background: var(--gray-50);
  border-color: var(--gray-400);
}

.btn-ghost {
  background: transparent;
  color: var(--text-secondary);
  border-color: transparent;
}

.btn-ghost:hover:not(:disabled) {
  background: var(--bg-hover);
  color: var(--text-primary);
  border-color: var(--border-color, rgba(0,0,0,0.08));
}

.btn-danger {
  background: var(--danger);
  color: var(--text-inverse);
  border-color: var(--danger);
}

.btn-danger:hover:not(:disabled) {
  background: var(--danger-dark);
  border-color: var(--danger-dark);
}

.btn-success {
  background: var(--success);
  color: var(--text-inverse);
  border-color: var(--success);
}

.btn-warning {
  background: var(--warning);
  color: var(--text-inverse);
  border-color: var(--warning);
}

.btn-info {
  background: var(--info);
  color: var(--text-inverse);
  border-color: var(--info);
}

.btn-info:hover:not(:disabled) {
  background: var(--info-dark);
  border-color: var(--info-dark);
}

/* Outline variants */
.btn-outline-primary {
  background: transparent;
  color: var(--primary);
  border-color: var(--primary);
}

.btn-outline-primary:hover:not(:disabled) {
  background: var(--primary);
  color: var(--text-inverse);
}

.btn-outline-danger {
  background: transparent;
  color: var(--danger);
  border-color: var(--danger);
}

.btn-outline-danger:hover:not(:disabled) {
  background: var(--danger);
  color: var(--text-inverse);
}

.btn-outline-secondary {
  background: transparent;
  color: var(--gray-600);
  border-color: var(--gray-300);
}

.btn-outline-secondary:hover:not(:disabled) {
  background: var(--gray-50);
  border-color: var(--gray-400);
  color: var(--gray-700);
}

/* Sizes */
.btn-xs {
  min-height: var(--btn-height-xs);
  padding: var(--btn-padding-xs);
  font-size: var(--btn-font-xs);
}

.btn-sm {
  min-height: var(--btn-height-sm);
  padding: var(--btn-padding-sm);
  font-size: var(--btn-font-sm);
}

.btn-lg {
  min-height: var(--btn-height-lg);
  padding: var(--btn-padding-lg);
  font-size: var(--btn-font-lg);
}

.btn-block {
  width: 100%;
}

/* Prevent SVG icons from shrinking inside flex buttons */
.btn > svg {
  flex-shrink: 0;
}

/* Ensure text fits inside constrained-width buttons (sidebars, narrow containers) */
.sidebar .btn,
.sidebar-section .btn,
.btn-block {
  min-width: 0;
}
.sidebar .btn > span,
.sidebar .btn > svg + span,
.sidebar-section .btn > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* F-07 (аудит v2.2): btn-block занимает всю ширину колонки — длинная
   русская подпись обязана ПЕРЕНОСИТЬСЯ, а не резаться посреди слова
   («ЗАГРУЗИТЬ ИЗОБРАЖЕНИЕ ИЛИ Р»). Резерв под EN-строки ×1.3 — тем более
   перенос, не обрезка. */
.btn-block,
.btn-block > span {
  white-space: normal;
  overflow-wrap: break-word;
  line-height: 1.3;
  min-width: 0;
}
/* For buttons with inline text (no inner span), use inner flex trick */
.sidebar .btn,
.sidebar-section .btn {
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--btn-font-sm);
}
/* Reduce sidebar button padding to fit more text */
.sidebar .btn-block,
.sidebar-section .btn-block {
  padding-left: 0.75rem;
  padding-right: 0.75rem;
}

.btn-icon {
  width: 40px;
  height: 40px;
  padding: 0;
}

@media (max-width: 480px) {
  .btn {
    min-height: var(--btn-height-sm);
    padding: var(--btn-padding-sm);
    font-size: var(--btn-font-sm);
  }
}

@media (prefers-contrast: high) {
  .btn:focus-visible {
    outline-width: 3px;
    outline-color: currentColor;
    box-shadow: none;
  }
}



/* ========================================================================
 * .btn-success-tone — приглушённая зелёная вариация (Editorial Calm).
 * Раньше было inline-style="background:#22c55e; border-color:#22c55e"
 * для кнопок типа «Экспедиция» в researcher header. Использует
 * --success / --success-dark из _palette_refresh_2026.css.
 * ======================================================================== */

.btn.btn-success-tone {
    background: var(--success);
    border-color: var(--success);
    color: var(--text-inverse, var(--white));
}
.btn.btn-success-tone:hover:not(:disabled) {
    background: var(--success-dark);
    border-color: var(--success-dark);
}
.btn.btn-success-tone:focus-visible {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--success) 25%, transparent);
}
/* ── css/components/modals.css ── */
/**
 * Modals (shared component)
 * Centralized modal styles for modalManager and dashboard modals.
 * Theme-aware: works with light/dark via CSS variables.
 */

/* ========================================================================
   OVERLAY / BACKDROP
   ======================================================================== */

.modal,
.modal-overlay {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.6);
  z-index: var(--z-modal-backdrop);
  align-items: center;
  justify-content: center;
}

@supports (backdrop-filter: blur(4px)) {
  .modal,
  .modal-overlay {
    background: rgba(0, 0, 0, 0.4);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
  }
}

.modal.active,
.modal-overlay.active {
  display: flex;
  animation: fadeIn 0.2s ease;
}

body.modal-open {
  overflow: hidden;
}

.modal.modal-fallback-open {
  display: flex;
}

/* ========================================================================
   FONT INHERITANCE
   ======================================================================== */

.modal,
.modal *,
.modal-overlay,
.modal-overlay * {
  font-family: var(--font-sans);
}

/* ========================================================================
   MODAL CONTENT SHELL
   ======================================================================== */

.modal-content {
  background: var(--bg-elevated, var(--white));
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-2xl);
  max-width: 500px;
  width: 90%;
  max-height: 90vh;
  overflow-y: auto;
  position: relative;
  animation: appSlideUp 0.3s ease;
  font-family: var(--font-sans);
  color: var(--text-primary);
}

@keyframes appSlideUp {
  from {
    transform: translateY(20px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

/* ========================================================================
   MODAL HEADER
   ======================================================================== */

.modal-header {
  padding: var(--spacing-lg);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--bg-primary);
}

.modal-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-primary);
  font-family: var(--font-sans);
  letter-spacing: -0.01em;
}

/* ========================================================================
   CLOSE BUTTON
   ======================================================================== */

.modal-close {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-tertiary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  font-family: var(--font-sans);
}

.modal-close:hover {
  background: color-mix(in srgb, var(--danger) 12%, var(--bg-primary));
  border-color: color-mix(in srgb, var(--danger) 25%, transparent);
  color: var(--danger);
}

/* ========================================================================
   MODAL BODY
   ======================================================================== */

.modal-body {
  padding: var(--spacing-lg);
  font-family: var(--font-sans);
  color: var(--text-secondary);
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

.modal-body::-webkit-scrollbar {
  width: 8px;
}

.modal-body::-webkit-scrollbar-track {
  background: var(--bg-secondary);
  border-radius: var(--radius-md);
}

.modal-body::-webkit-scrollbar-thumb {
  background: var(--border-strong, var(--border));
  border-radius: var(--radius-md);
  border: 2px solid var(--bg-secondary);
}

.modal-body::-webkit-scrollbar-thumb:hover {
  background: var(--text-muted);
}

/* ========================================================================
   MODAL FOOTER
   ======================================================================== */

.modal-footer {
  padding: var(--spacing-lg);
  border-top: 1px solid var(--border);
  display: flex;
  justify-content: flex-end;
  gap: var(--spacing-sm);
  background: var(--bg-secondary);
}

/* ========================================================================
   MODAL BUTTONS — unified theme-aware styles
   ======================================================================== */

.modal .btn,
.modal-content .btn {
  padding: 0.75rem 1.5rem;
  border-radius: 10px;
  font-weight: 600;
  font-size: 0.9375rem;
  cursor: pointer;
  transition: all 0.2s;
  border: none;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 100px;
  justify-content: center;
}

.modal .btn-primary,
.modal-content .btn-primary {
  background: var(--accent-color, var(--primary));
  color: var(--white);
}

.modal .btn-primary:hover,
.modal-content .btn-primary:hover {
  background: var(--accent-hover, var(--primary-dark, var(--info-dark)));
}

.modal .btn-secondary,
.modal-content .btn-secondary {
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border: 2px solid var(--border);
}

.modal .btn-secondary:hover,
.modal-content .btn-secondary:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong, var(--border));
}

.modal .btn-danger,
.modal-content .btn-danger {
  background: var(--danger);
  color: var(--white);
}

.modal .btn-danger:hover,
.modal-content .btn-danger:hover {
  background: var(--danger-dark);
}

.modal .btn-success,
.modal-content .btn-success {
  background: var(--success);
  color: var(--white);
}

.modal .btn-success:hover,
.modal-content .btn-success:hover {
  background: var(--success-dark);
}

.modal .btn-warning,
.modal-content .btn-warning {
  background: var(--warning);
  color: var(--white);
}

.modal .btn-warning:hover,
.modal-content .btn-warning:hover {
  background: var(--warning-dark);
}

/* ========================================================================
   FORM ELEMENTS inside modals — theme-aware
   ======================================================================== */

.modal .form-input,
.modal textarea,
.modal select,
.modal input[type="text"],
.modal input[type="number"],
.modal input[type="email"],
.modal input[type="url"],
.modal input[type="file"] {
  color: var(--text-primary);
  background: var(--bg-elevated);
  border-color: var(--border);
}

.modal .form-input:focus,
.modal textarea:focus,
.modal select:focus {
  border-color: var(--primary);
  background: var(--bg-elevated);
}

.modal .form-input::placeholder,
.modal textarea::placeholder {
  color: var(--text-muted);
}

/* ========================================================================
   BOOTSTRAP MODAL COMPAT (class="modal fade")
   ======================================================================== */

.modal.fade {
  background: transparent;
  z-index: var(--z-modal);
  align-items: flex-start;
}

.modal.fade.show {
  display: block;
}

.modal.fade .modal-dialog {
  margin: 1.75rem auto;
  max-width: none;
  width: auto;
}

.modal.fade .modal-content {
  max-width: none;
  width: auto;
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-2xl);
  border: 1px solid var(--border);
}

.modal-backdrop {
  background: var(--bg-overlay);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  z-index: var(--z-modal-backdrop);
}

/* ========================================================================
   DARK THEME OVERRIDES
   ======================================================================== */

[data-theme="dark"] .modal,
[data-theme="dark"] .modal-overlay {
  background: rgba(0, 0, 0, 0.7);
}

@supports (backdrop-filter: blur(4px)) {
  [data-theme="dark"] .modal,
  [data-theme="dark"] .modal-overlay {
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(8px);
  }
}

[data-theme="dark"] .modal-content {
  box-shadow:
    0 25px 50px -12px rgba(0, 0, 0, 0.5),
    0 0 0 1px rgba(255, 255, 255, 0.06);
}
/* ── css/copilot.css ── */
/* AI-Автопилот: header-controls + модал журнала.
 * Кладутся ВНУТРЬ <div class="header-right"> каждого дашборда.
 * Никаких FAB, никаких overlay'ев на карту — обычные inline-controls.
 */

/* Контейнер: inline-flex, чтобы лежал в header-right между другими кнопками */
.copilot-inline {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-right: 8px;
    font-family: inherit;
}

/* Checkbox-обёртка — выглядит как пилюля с галкой */
.copilot-checkbox {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 32px;
    padding: 0 12px;
    background: var(--bg-primary);
    color: var(--status-success);
    border: 1px solid var(--gray-300);
    border-radius: var(--radius-xl);
    cursor: pointer;
    font-size: 13px;
    font-weight: 500;
    user-select: none;
    transition: background 0.15s, border-color 0.15s;
}
.copilot-checkbox:hover {
    background: var(--bg-tertiary);
    border-color: var(--gray-400);
}
.copilot-checkbox input[type="checkbox"] {
    width: 14px;
    height: 14px;
    margin: 0;
    cursor: pointer;
    accent-color: var(--status-success);
}
.copilot-checkbox-icon {
    font-size: 14px;
    line-height: 1;
}
.copilot-checkbox-label {
    line-height: 1;
}
/* Активное состояние — checkbox checked */
.copilot-checkbox:has(input:checked) {
    background: var(--status-success-bg);
    border-color: var(--status-success);
    color: var(--status-success);
}

.copilot-checkbox-badge {
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    background: var(--status-danger);
    color: var(--bg-primary);
    border-radius: 9px;
    font-size: 12px;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}
.copilot-checkbox-badge[hidden] { display: none; }

/* Лог-кнопка — обычная btn-secondary, унаследует стиль дашборда.
 * Дополнительный нормализатор размера на случай если у дашборда нет
 * btn-sm в дизайн-системе. */
.copilot-log-btn {
    height: 32px;
    padding: 0 12px;
    font-size: 13px;
    line-height: 1;
}

/* ────────── Drawer-style лог панели ──────────
 * Раньше было fullscreen-модалка с translucent-backdrop'ом — на дашбордах
 * с большой картой (Урбанист, Историк, Аналитик) backdrop тонировал всю
 * карту, выглядело прозрачным «облаком» поверх карты.
 *
 * Теперь — side-drawer справа, без backdrop'а: карта/контент остаётся
 * полностью видимым и кликабельным. Закрытие — по кнопке ✕ или Esc. */
.copilot-modal {
    position: fixed !important;
    top: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    left: auto !important;
    z-index: 9999 !important;
    display: flex;
    align-items: stretch;
    justify-content: flex-end;
    pointer-events: none;          /* drawer сам ловит клики через card */
}
.copilot-modal[hidden] { display: none !important; }
/* TRIPLE-LOCK против регрессий: модал виден ТОЛЬКО когда JS добавит .is-open.
 * История: модал «протекал» вниз страницы из-за CSS-кеша / inline-стилей.
 * Сейчас тройная защита:
 *   1. [hidden] attr → display:none !important
 *   2. style="display:none" inline (в HTML template)
 *   3. :not(.is-open) → display:none !important (этот rule)
 * Если хотя бы один lock сработает — модал не появится. */
body > .copilot-modal:not(.is-open),
.copilot-modal:not(.is-open) {
    display: none !important;
    visibility: hidden !important;
    pointer-events: none !important;
}
.copilot-modal-backdrop {
    /* В drawer-режиме backdrop невидим И не ловит клики — карта/контент
     * слева от drawer'а полностью кликабельны. Backdrop оставлен в DOM
     * только для обратной совместимости (старые шаблоны могли иметь
     * inline-style на нём). */
    position: absolute;
    top: 0;
    right: 480px;
    bottom: 0;
    left: 0;
    background: transparent;
    pointer-events: none;
}
.copilot-modal-card {
    position: relative;
    background: var(--bg-primary);
    border-left: 1px solid var(--gray-300);
    box-shadow: -8px 0 24px rgba(0, 0, 0, 0.12);
    width: 480px;
    max-width: 100vw;
    height: 100vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    pointer-events: auto;
    animation: copilot-drawer-in 0.18s ease-out;
}
@keyframes copilot-drawer-in {
    from { transform: translateX(16px); opacity: 0.6; }
    to   { transform: translateX(0);    opacity: 1; }
}
.copilot-modal-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 18px;
    border-bottom: 1px solid var(--border);
}
.copilot-modal-head h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary);
}
.copilot-modal-tabs {
    display: flex;
    gap: 0;
    border-bottom: 1px solid var(--border);
    background: var(--bg-tertiary);
}
.copilot-tab {
    flex: 1;
    padding: 10px 14px;
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-muted);
    transition: color 0.15s, border-color 0.15s;
}
.copilot-tab:hover { color: var(--status-success); }
.copilot-tab.is-active {
    color: var(--status-success);
    border-bottom-color: var(--status-success);
    background: var(--bg-primary);
}
.copilot-modal-body {
    flex: 1;
    overflow-y: auto;
    padding: 12px 18px;
    min-height: 200px;
}
.copilot-tab-pane[hidden] { display: none; }
.copilot-modal-foot {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 18px;
    border-top: 1px solid var(--border);
    background: var(--bg-primary);
}
.copilot-modal-status {
    font-size: 12px;
    color: var(--text-light);
}

/* Список предложений и активности */
.copilot-list { display: flex; flex-direction: column; gap: 8px; }
.copilot-empty {
    color: var(--text-light);
    font-size: 13px;
    text-align: center;
    padding: 24px 12px;
}

/* Карточка предложения */
.copilot-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.copilot-card.is-priority-1 { border-left: 3px solid var(--status-danger); }
.copilot-card.is-priority-2,
.copilot-card.is-priority-3 { border-left: 3px solid var(--status-warning); }
.copilot-card.is-priority-4,
.copilot-card.is-priority-5 { border-left: 3px solid var(--status-success); }
.copilot-card-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    padding: 10px 12px;
    gap: 8px;
    border-bottom: 1px solid var(--bg-tertiary);
}
.copilot-card-action-type {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 2px;
}
.copilot-card-title {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.4;
}
.copilot-card-confidence {
    background: var(--status-success-bg);
    color: var(--status-success);
    border-radius: var(--radius-lg);
    padding: 2px 8px;
    font-size: 12px;
    font-weight: 500;
    white-space: nowrap;
}
.copilot-card-summary {
    padding: 8px 12px;
    color: var(--gray-700);
    font-size: 13px;
    line-height: 1.5;
}
.copilot-card-actions {
    display: flex;
    gap: 6px;
    padding: 8px 12px;
    background: var(--bg-primary);
    border-top: 1px solid var(--bg-tertiary);
}
.copilot-card-meta {
    padding: 0 12px 8px;
    font-size: 12px;
    color: var(--text-light);
    display: flex;
    gap: 12px;
}

/* Activity-row — строка журнала */
.copilot-activity {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-left: 3px solid var(--gray-400);
    border-radius: var(--radius-sm);
    padding: 8px 12px;
    font-size: 13px;
}
.copilot-activity-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 4px;
    color: var(--text-muted);
    font-size: 12px;
}
.copilot-activity-type {
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 600;
    color: var(--status-success);
}
.copilot-activity-content {
    color: var(--gray-700);
    line-height: 1.5;
    white-space: pre-wrap;
}

/* Mobile: drawer на весь экран */
@media (max-width: 520px) {
    .copilot-checkbox-label { display: none; }
    .copilot-modal-card { width: 100vw; }
    .copilot-modal-backdrop { right: 0; }   /* нет просвета — drawer покрывает всё */
}

/* Очень узкие экраны (≤420px): прячем текст Лог, оставляем только эмодзи.
   Это даёт ~50px обратно в header-right для других кнопок. */
@media (max-width: 420px) {
    .copilot-log-btn {
        font-size: 0;          /* скрывает текст-узел */
        padding: 0 10px;
        gap: 0;
    }
    .copilot-log-btn::before {
        content: "📋";
        font-size: 14px;
    }
    .copilot-inline { margin-right: 4px; gap: 4px; }
}
/* ── css/components/map-popups.css ── */
/**
 * Map Popups & Drawing Controls (shared component)
 * Unified styles for MapLibre GL + Leaflet popups, drawing toolbars.
 * Theme-aware: works with light/dark via CSS variables.
 *
 * Popup structure (managed by mapPopupManager.js):
 *   .map-popup-enhanced
 *     .map-popup-header  (drag handle + title + minimize/close)
 *     .map-popup-body    (scrollable content)
 *     .map-popup-footer  (optional action buttons)
 */

/* ========================================================================
   CSS CUSTOM PROPERTIES (scoped)
   ======================================================================== */
:root {
  --map-popup-bg: var(--bg-elevated, var(--white));
  --map-popup-border: var(--border);
  --map-popup-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.12), 0 2px 8px -2px rgba(0, 0, 0, 0.08);
  --map-popup-radius: var(--radius-lg, 12px);
  --map-popup-header-bg: var(--bg-secondary);
  --map-popup-max-w: 400px;
  --map-popup-min-w: 220px;
  --map-popup-max-h: 50vh;
}

/* ========================================================================
   MAPLIBRE POPUP OVERRIDES
   ======================================================================== */

.maplibregl-popup {
  z-index: 20;
  font-family: var(--font-sans);
}

.maplibregl-popup-content {
  background: var(--map-popup-bg);
  border-radius: var(--map-popup-radius);
  box-shadow: var(--map-popup-shadow);
  border: 1px solid var(--map-popup-border);
  padding: 0;
  min-width: var(--map-popup-min-w);
  max-width: var(--map-popup-max-w);
  overflow: hidden;
  font-family: var(--font-sans);
  color: var(--text-primary);
}

.maplibregl-popup-close-button {
  display: none; /* replaced by our custom header buttons */
}

/* Popup tip / anchor */
.maplibregl-popup-tip {
  border-top-color: var(--map-popup-bg);
}

.maplibregl-popup-anchor-bottom .maplibregl-popup-tip {
  border-top-color: var(--map-popup-bg);
}

.maplibregl-popup-anchor-top .maplibregl-popup-tip {
  border-bottom-color: var(--map-popup-bg);
}

.maplibregl-popup-anchor-left .maplibregl-popup-tip {
  border-right-color: var(--map-popup-bg);
}

.maplibregl-popup-anchor-right .maplibregl-popup-tip {
  border-left-color: var(--map-popup-bg);
}

/* ========================================================================
   LEAFLET POPUP OVERRIDES
   ======================================================================== */

.leaflet-popup-content-wrapper {
  background: var(--map-popup-bg);
  border-radius: var(--map-popup-radius);
  box-shadow: var(--map-popup-shadow);
  border: 1px solid var(--map-popup-border);
  padding: 0;
  color: var(--text-primary);
  font-family: var(--font-sans);
}

.leaflet-popup-content {
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.5;
  min-width: 200px;
  max-width: var(--map-popup-max-w);
  color: var(--text-primary);
}

.leaflet-popup-tip-container .leaflet-popup-tip {
  background: var(--map-popup-bg);
  border: 1px solid var(--map-popup-border);
  border-top: none;
  border-left: none;
}

.leaflet-popup-close-button {
  display: none; /* replaced by header close */
}

/* ========================================================================
   ENHANCED POPUP STRUCTURE (injected by mapPopupManager.js)
   ======================================================================== */

/* Header: drag handle + title + controls */
.map-popup-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  background: var(--map-popup-header-bg);
  border-bottom: 1px solid var(--map-popup-border);
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  border-radius: var(--map-popup-radius) var(--map-popup-radius) 0 0;
  min-height: 36px;
}

.map-popup-header:active {
  cursor: grabbing;
}

.map-popup-drag-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: var(--text-muted);
  flex-shrink: 0;
  opacity: 0.5;
}

.map-popup-drag-handle svg {
  width: 14px;
  height: 14px;
}

.map-popup-title {
  flex: 1;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.3;
}

.map-popup-controls {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.map-popup-btn {
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm, 4px);
  color: var(--text-muted);
  cursor: pointer;
  transition: all 0.15s ease;
  padding: 0;
  line-height: 1;
}

.map-popup-btn:hover {
  background: var(--bg-hover, var(--bg-tertiary));
  border-color: var(--border);
  color: var(--text-secondary);
}

.map-popup-btn-close:hover {
  background: color-mix(in srgb, var(--danger) 12%, var(--bg-primary));
  border-color: color-mix(in srgb, var(--danger) 25%, transparent);
  color: var(--danger);
}

.map-popup-btn svg {
  width: 14px;
  height: 14px;
}

/* Body: scrollable content area */
.map-popup-body {
  padding: 10px 12px;
  max-height: var(--map-popup-max-h);
  overflow-y: auto;
  overflow-x: hidden;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--text-secondary);
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

.map-popup-body::-webkit-scrollbar {
  width: 5px;
}

.map-popup-body::-webkit-scrollbar-track {
  background: transparent;
}

.map-popup-body::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 3px;
}

.map-popup-body::-webkit-scrollbar-thumb:hover {
  background: var(--text-muted);
}

/* Content typography inside popups */
.map-popup-body h3,
.map-popup-body h4 {
  margin: 0 0 6px 0;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.3;
}

.map-popup-body h3 { font-size: 0.9375rem; }
.map-popup-body h4 { font-size: 0.8125rem; }

.map-popup-body p {
  margin: 4px 0;
  color: var(--text-secondary);
}

.map-popup-body table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.8125rem;
}

.map-popup-body table tr {
  border-bottom: 1px solid var(--border);
}

.map-popup-body table tr:last-child {
  border-bottom: none;
}

.map-popup-body table td {
  padding: 4px 0;
}

.map-popup-body table td:first-child {
  padding-right: 12px;
  color: var(--text-muted);
}

.map-popup-body table td:last-child {
  font-weight: 500;
  text-align: right;
}

/* Footer: optional action buttons */
.map-popup-footer {
  padding: 8px 10px;
  border-top: 1px solid var(--map-popup-border);
  background: var(--map-popup-header-bg);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  border-radius: 0 0 var(--map-popup-radius) var(--map-popup-radius);
}

.map-popup-footer .btn-sm {
  padding: 4px 10px;
  font-size: 0.75rem;
  border-radius: var(--radius-sm, 4px);
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  transition: all 0.15s ease;
}

.map-popup-footer .btn-sm:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong, var(--border));
}

.map-popup-footer .btn-sm.btn-primary {
  background: var(--primary);
  color: var(--white);
  border-color: var(--primary);
}

.map-popup-footer .btn-sm.btn-primary:hover {
  filter: brightness(1.1);
}

/* ========================================================================
   MINIMIZED STATE
   ======================================================================== */

.map-popup-minimized .map-popup-body,
.map-popup-minimized .map-popup-footer {
  display: none;
}

.map-popup-minimized .map-popup-header {
  border-bottom: none;
  border-radius: var(--map-popup-radius);
}

.map-popup-minimized .maplibregl-popup-content,
.map-popup-minimized .leaflet-popup-content-wrapper {
  min-width: auto;
}

/* Hide tip when minimized */
.map-popup-minimized .maplibregl-popup-tip,
.map-popup-minimized .leaflet-popup-tip-container {
  display: none;
}

/* ========================================================================
   DRAGGING STATE
   ======================================================================== */

.map-popup-dragging {
  opacity: 0.92;
  z-index: 100 !important;
}

.map-popup-dragging .maplibregl-popup-content,
.map-popup-dragging .leaflet-popup-content-wrapper {
  box-shadow: 0 16px 48px -8px rgba(0, 0, 0, 0.2), 0 4px 12px -4px rgba(0, 0, 0, 0.12);
}

/* ========================================================================
   POPUP ANIMATIONS
   ======================================================================== */

@keyframes mapPopupIn {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.96);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.maplibregl-popup-content,
.leaflet-popup-content-wrapper {
  animation: mapPopupIn 0.2s ease-out;
}

/* ========================================================================
   COMMON POPUP CONTENT PATTERNS
   ======================================================================== */

/* Stat row (label: value) */
.popup-stat-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 3px 0;
  font-size: 0.8125rem;
}

.popup-stat-label {
  color: var(--text-muted);
}

.popup-stat-value {
  font-weight: 600;
  color: var(--text-primary);
}

/* Coordinate display */
.popup-coords {
  font-size: 0.75rem;
  color: var(--text-muted);
  font-family: var(--font-mono);
  padding: 2px 0;
}

/* Tags / badges in popups */
.popup-tag {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  font-size: 0.75rem;
  font-weight: 500;
  border-radius: var(--radius-full, 9999px);
  background: color-mix(in srgb, var(--primary) 10%, var(--bg-secondary));
  color: var(--primary);
  gap: 3px;
}

/* Divider */
.popup-divider {
  height: 1px;
  background: var(--border);
  margin: 8px 0;
}

/* ========================================================================
   DRAWING / EDITING TOOLBAR — UNIFIED STYLES
   ======================================================================== */

/* Leaflet Draw toolbar */
.leaflet-draw-toolbar a,
.leaflet-bar a {
  background-color: var(--bg-elevated, var(--white)) !important;
  color: var(--text-secondary) !important;
  border-color: var(--border) !important;
  transition: all 0.15s ease;
}

.leaflet-draw-toolbar a:hover,
.leaflet-bar a:hover {
  background-color: var(--bg-hover, var(--bg-tertiary)) !important;
  color: var(--text-primary) !important;
}

.leaflet-bar {
  border: 1px solid var(--border) !important;
  border-radius: var(--radius-md, 8px) !important;
  box-shadow: var(--shadow-md) !important;
  overflow: hidden;
}

.leaflet-bar a:first-child {
  border-top-left-radius: var(--radius-md, 8px) !important;
  border-top-right-radius: var(--radius-md, 8px) !important;
}

.leaflet-bar a:last-child {
  border-bottom-left-radius: var(--radius-md, 8px) !important;
  border-bottom-right-radius: var(--radius-md, 8px) !important;
  border-bottom: none !important;
}

/* Leaflet.PM / Geoman toolbar */
.leaflet-pm-toolbar .leaflet-pm-icon-delete,
.leaflet-pm-toolbar .leaflet-pm-icon-edit,
.leaflet-pm-toolbar .leaflet-pm-icon-draw {
  filter: var(--icon-filter, none);
}

.leaflet-pm-actions-container {
  background: var(--bg-elevated, var(--white)) !important;
  border-radius: var(--radius, 6px) !important;
  box-shadow: var(--shadow-md) !important;
  border: 1px solid var(--border) !important;
  padding: 2px !important;
}

.leaflet-pm-action {
  color: var(--text-primary) !important;
  padding: 6px 12px !important;
  font-size: 0.8125rem !important;
  font-family: var(--font-sans) !important;
  border-radius: var(--radius-sm, 4px) !important;
  transition: background 0.15s ease !important;
}

.leaflet-pm-action:hover {
  background: var(--bg-hover, var(--bg-tertiary)) !important;
}

/* Active drawing/editing state indicator */
.leaflet-pm-toolbar .active a,
.leaflet-draw-toolbar a.leaflet-draw-toolbar-button-enabled {
  background-color: color-mix(in srgb, var(--primary) 15%, var(--bg-elevated)) !important;
  border-color: var(--primary) !important;
  color: var(--primary) !important;
}

/* MapboxDraw controls (MapLibre) */
.mapboxgl-ctrl-group,
.maplibregl-ctrl-group {
  background: var(--bg-elevated, var(--white)) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius-md, 8px) !important;
  box-shadow: var(--shadow-md) !important;
  overflow: hidden;
}

.mapboxgl-ctrl-group button,
.maplibregl-ctrl-group button {
  background-color: var(--bg-elevated, var(--white)) !important;
  border-color: var(--border) !important;
  color: var(--text-secondary) !important;
  transition: all 0.15s ease;
}

.mapboxgl-ctrl-group button:hover,
.maplibregl-ctrl-group button:hover {
  background-color: var(--bg-hover, var(--bg-tertiary)) !important;
  color: var(--text-primary) !important;
}

.mapboxgl-ctrl-group button.active,
.maplibregl-ctrl-group button.active {
  background-color: color-mix(in srgb, var(--primary) 15%, var(--bg-elevated)) !important;
  color: var(--primary) !important;
}

/* MapLibre navigation controls */
.maplibregl-ctrl-zoom-in,
.maplibregl-ctrl-zoom-out,
.maplibregl-ctrl-compass {
  background-color: var(--bg-elevated, var(--white)) !important;
  color: var(--text-secondary) !important;
}

.maplibregl-ctrl-zoom-in:hover,
.maplibregl-ctrl-zoom-out:hover,
.maplibregl-ctrl-compass:hover {
  background-color: var(--bg-hover, var(--bg-tertiary)) !important;
  color: var(--text-primary) !important;
}

/* ========================================================================
   EDIT MODE INDICATOR (shared across dashboards)
   ======================================================================== */

.edit-mode-indicator {
  position: fixed;
  top: 52px;
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 16px;
  border-radius: var(--radius-full, 9999px);
  font-size: 0.8125rem;
  font-weight: 600;
  font-family: var(--font-sans);
  z-index: 1000;
  display: none;
  box-shadow: var(--shadow-lg);
  pointer-events: none;
  white-space: nowrap;
}

.edit-mode-indicator.active {
  display: block;
  animation: mapPopupIn 0.2s ease-out;
}

.edit-mode-indicator--drawing {
  background: color-mix(in srgb, var(--primary) 90%, white);
  color: var(--white);
  border: 1px solid var(--primary);
}

.edit-mode-indicator--editing {
  background: color-mix(in srgb, var(--warning) 90%, white);
  color: var(--white);
  border: 1px solid var(--warning);
}

.edit-mode-indicator--deleting {
  background: color-mix(in srgb, var(--danger) 90%, white);
  color: var(--white);
  border: 1px solid var(--danger);
}

/* ========================================================================
   DARK THEME OVERRIDES
   ======================================================================== */

[data-theme="dark"] {
  --map-popup-bg: var(--bg-elevated, var(--gray-800));
  --map-popup-border: var(--border, var(--gray-700));
  --map-popup-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.4), 0 2px 8px -2px rgba(0, 0, 0, 0.3);
  --map-popup-header-bg: var(--bg-secondary, var(--text-primary));
  --icon-filter: invert(0.85);
}

[data-theme="dark"] .maplibregl-popup-content,
[data-theme="dark"] .leaflet-popup-content-wrapper {
  box-shadow:
    0 8px 24px -4px rgba(0, 0, 0, 0.5),
    0 0 0 1px rgba(255, 255, 255, 0.06);
}

[data-theme="dark"] .maplibregl-popup-tip {
  border-top-color: var(--map-popup-bg);
}

[data-theme="dark"] .maplibregl-popup-anchor-top .maplibregl-popup-tip {
  border-bottom-color: var(--map-popup-bg);
}

[data-theme="dark"] .maplibregl-popup-anchor-left .maplibregl-popup-tip {
  border-right-color: var(--map-popup-bg);
}

[data-theme="dark"] .maplibregl-popup-anchor-right .maplibregl-popup-tip {
  border-left-color: var(--map-popup-bg);
}

[data-theme="dark"] .leaflet-popup-tip-container .leaflet-popup-tip {
  background: var(--map-popup-bg);
  border-color: var(--map-popup-border);
}

[data-theme="dark"] .leaflet-bar a,
[data-theme="dark"] .leaflet-draw-toolbar a,
[data-theme="dark"] .leaflet-pm-action,
[data-theme="dark"] .mapboxgl-ctrl-group button,
[data-theme="dark"] .maplibregl-ctrl-group button {
  background-color: var(--bg-elevated) !important;
  color: var(--text-secondary) !important;
  border-color: var(--border) !important;
}

[data-theme="dark"] .leaflet-bar,
[data-theme="dark"] .mapboxgl-ctrl-group,
[data-theme="dark"] .maplibregl-ctrl-group {
  box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.4) !important;
}

/* ========================================================================
   RESPONSIVE
   ======================================================================== */

@media (max-width: 768px) {
  :root {
    --map-popup-max-w: 320px;
    --map-popup-max-h: 40vh;
  }

  .map-popup-header {
    padding: 6px 8px;
    min-height: 32px;
  }

  .map-popup-body {
    padding: 8px 10px;
    font-size: 0.75rem;
  }

  .map-popup-title {
    font-size: 0.75rem;
  }
}
/* ── css/components/cards.css ── */
/**
 * Cards (shared component) — Unified Design System
 *
 * V3 (2026-05-14): после audit'а 600+ card-классов в системе — добавлены
 * design-tokens + расширенные modifier'ы. Цель: 80%+ карточек могут
 * стать `class="card"` или `class="card card--<modifier>"` без потери
 * визуального стиля. Outliers (kanban, mission-floating) остаются
 * со своими специфическими стилями.
 *
 * Доминирующие в системе значения (из audit'а):
 *  - radius: 12px (тренд: 8/10/12/14/16 — 12 побеждает)
 *  - padding: 16-20px (1rem-1.25rem)
 *  - bg: var(--bg-primary)
 *  - border: 1px solid var(--border-color)
 *  - hover: box-shadow + translateY(-1..-2px)
 */

/* ═══════════════════════════════════════════════════════════════
   DESIGN TOKENS — single source of truth для card visual style.
   После audit'а 2026-05-14 — добавлены отсутствовавшие card-vars.
   ═══════════════════════════════════════════════════════════════ */
:root {
  --card-bg: var(--bg-primary, var(--white));
  --card-bg-elevated: var(--bg-elevated, var(--bg-primary, var(--white)));
  --card-border: 1px solid var(--border-color, var(--border));
  --card-radius: 12px;
  --card-radius-sm: 8px;
  --card-radius-lg: 16px;
  --card-padding: 16px;
  --card-padding-sm: 12px;
  --card-padding-lg: 20px;
  --card-shadow: none;
  --card-shadow-hover: 0 2px 8px color-mix(in srgb, var(--dark) 6%, transparent);
  --card-shadow-elevated: 0 8px 24px color-mix(in srgb, var(--dark) 8%, transparent);
  --card-shadow-elevated-hover: 0 12px 32px color-mix(in srgb, var(--dark) 12%, transparent);
  --card-transition: box-shadow 0.15s ease, transform 0.15s ease,
                     border-color 0.15s ease;
}

[data-theme="dark"] {
  --card-shadow-hover: 0 2px 8px rgba(0, 0, 0, 0.25);
  --card-shadow-elevated: 0 8px 24px rgba(0, 0, 0, 0.35);
  --card-shadow-elevated-hover: 0 12px 32px rgba(0, 0, 0, 0.45);
}

/* ═══════════════════════════════════════════════════════════════
   BASE CARD
   ═══════════════════════════════════════════════════════════════ */
.card {
  position: relative;            /* Для status badge top-right */
  background: var(--card-bg);
  border: var(--card-border);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
  transition: var(--card-transition);
}

.card:hover {
  box-shadow: var(--card-shadow-hover);
}

/* Если card используется как контейнер с header/body/footer */
.card-header {
  padding: var(--card-padding) var(--card-padding-lg);
  border-bottom: 1px solid var(--border-light, var(--border-color, var(--bg-tertiary)));
}

.card-header h3,
.card-header .card-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}

.card-body {
  padding: var(--card-padding-lg);
}

.card-footer {
  padding: var(--card-padding) var(--card-padding-lg);
  border-top: 1px solid var(--border-light, var(--border-color, var(--bg-tertiary)));
}

/* ═══════════════════════════════════════════════════════════════
   MODIFIERS — наследование стиля без переопределения всего
   ═══════════════════════════════════════════════════════════════ */

/* COMPACT — для chip-подобных / mini-карточек (resident, kb-mini) */
.card--compact {
  padding: var(--card-padding-sm);
  border-radius: var(--card-radius-sm);
  font-size: 0.875rem;
}
.card--compact .card-body {
  padding: var(--card-padding-sm);
}

/* ELEVATED — modal-like / spotlight (KB studio, OKC big-card) */
.card--elevated {
  box-shadow: var(--card-shadow-elevated);
  padding: var(--card-padding-lg);
  border-radius: var(--card-radius-lg);
}
.card--elevated:hover {
  box-shadow: var(--card-shadow-elevated-hover);
}

/* INTERACTIVE — clickable cards с subtle lift на hover */
.card--interactive {
  cursor: pointer;
}
.card--interactive:hover {
  transform: translateY(-1px);
  box-shadow: var(--card-shadow-hover);
}
.card--interactive:active {
  transform: translateY(0);
  box-shadow: var(--card-shadow);
}

/* PADDED — добавляет .card-padding если структура card-header/body
   не используется (просто содержимое внутри .card). */
.card--padded {
  padding: var(--card-padding);
}
.card--padded-lg {
  padding: var(--card-padding-lg);
}

/* BORDERED-ACCENT — accent-border для подсветки выбранного / важного.
   Использует --primary; для booking-confirmed, selected items. */
.card--accent {
  border-color: var(--primary, var(--color-primary));
}
.card--accent-success { border-color: var(--success); }
.card--accent-warning { border-color: var(--warning); }
.card--accent-danger  { border-color: var(--danger); }

/* BORDERED-LEFT — толстая полоса слева (admin pulse-style, status indicator) */
.card--border-left {
  border-left: 4px solid var(--primary, var(--color-primary));
}
.card--border-left.card--accent-success { border-left-color: var(--success); }
.card--border-left.card--accent-warning { border-left-color: var(--warning); }
.card--border-left.card--accent-danger  { border-left-color: var(--danger); }

/* MUTED — приглушённая карточка (rejected / archived) */
.card--muted {
  opacity: 0.7;
  background: var(--bg-secondary);
}

/* ═══════════════════════════════════════════════════════════════
   STATUS BADGE — top-right corner (member / pending / rejected)
   ═══════════════════════════════════════════════════════════════ */
.card-status {
  position: absolute;
  top: 10px;
  right: 10px;
  font-size: 0.75rem;
  padding: 3px 8px;
  border-radius: 999px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.card-status--member, .card-status--success {
  background: var(--success-lightest);
  color: var(--status-success);
}
.card-status--pending, .card-status--warning {
  background: var(--warning-lightest);
  color: var(--status-warning);
}
.card-status--rejected, .card-status--danger {
  background: var(--danger-lightest);
  color: var(--status-danger);
}
.card-status--neutral, .card-status--virtual {
  background: var(--bg-secondary);
  color: var(--text-secondary);
}

/* ═══════════════════════════════════════════════════════════════
   META — type-badge / meta line / actions zone (типовые внутренние блоки)
   ═══════════════════════════════════════════════════════════════ */
.card-type {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: var(--radius-md);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.card-meta {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  font-size: 0.78rem;
  color: var(--text-secondary, var(--gray-500));
}

.card-actions {
  margin-top: auto;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* card-title — для inline-titles внутри .card (без header'а) */
.card-title-inline {
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.3;
  margin: 0;
  color: var(--text-primary, var(--gray-800));
}

.card-description {
  font-size: 0.85rem;
  color: var(--text-secondary, var(--gray-500));
  line-height: 1.45;
  margin: 0;
}

/* ═══════════════════════════════════════════════════════════════
   GRID — стандартная сетка карточек (auto-fill responsive)
   Использовать как .card-grid поверх контейнера со множеством .card.
   ═══════════════════════════════════════════════════════════════ */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}
.card-grid--wide {
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 20px;
}
.card-grid--compact {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 12px;
}
/* ── css/components/kb-studio.css ── */
/* components/kb-studio.css — KB Studio (оверлей «Студия базы знаний»).
 * DS-03 (§12, ТЗ 2026-07-16): единый источник истины вместо 4 почти
 * идентичных копий в producer.css / community_architect.css /
 * citizen_dashboard.css / methodologist_layout.css (расхождения были
 * только в z-index/alpha бордеров/gap — унифицировано к producer-варианту,
 * z-index = --z-modal-backdrop как у остальных модальных оверлеев).
 *
 * Role-CSS оставляет ТОЛЬКО scoped-уникальное поверх этого канона:
 *   • okc_module.css — светлая тема #okc-overlay .kb-studio (override);
 *   • data_engineer_layout.css — fullscreen/live-banner/refresh расширения;
 *   • methodologist_layout.css — своя ширина/бордер (переопределение);
 *   • historian.css — ДРУГОЙ виджет (kb-studio-modal/-title, dash-нейминг).
 */

.kb-studio-overlay {
    position: fixed;
    inset: 0;
    background: color-mix(in srgb, var(--dark) 85%, transparent);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    z-index: var(--z-modal-backdrop);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s ease, visibility 0.2s ease;
}
.kb-studio-overlay.is-visible {
    opacity: 1;
    visibility: visible;
}
.kb-studio {
    width: min(1100px, 100%);
    max-height: 92vh;
    background: var(--bg-code);
    border-radius: var(--radius-2xl);
    border: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
    color: var(--text-code);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    box-shadow: 0 30px 80px rgba(2, 6, 23, 0.7);
}
.kb-studio__header {
    padding: 20px 24px;
    border-bottom: 1px solid color-mix(in srgb, var(--gray-400) 25%, transparent);
    display: flex;
    justify-content: space-between;
    gap: 12px;
}
.kb-studio__header-actions {
    display: flex;
    gap: 0.4rem;
}
.kb-studio__eyebrow {
    margin: 0;
    font-size: 0.8rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.kb-studio__tabs {
    display: flex;
    gap: 6px;
    padding: 0 24px;
    border-bottom: 1px solid color-mix(in srgb, var(--gray-400) 25%, transparent);
}
.kb-studio__tab {
    border: none;
    background: transparent;
    padding: 10px 14px;
    color: var(--text-muted);
    font-size: 0.9rem;
    cursor: pointer;
    border-bottom: 2px solid transparent;
}
.kb-studio__tab.active {
    color: var(--white);
    border-bottom-color: var(--color-accent);
}
.kb-studio__panels {
    flex: 1;
    overflow-y: auto;
    padding: 20px 24px;
}
.kb-studio__panel {
    display: none;
    animation: fadeIn 0.2s ease;
}
.kb-studio__panel.active {
    display: block;
}
.kb-studio__panel--graph {
    min-height: 420px;
}
.kb-studio__close {
    border: none;
    background: transparent;
    color: var(--text-muted);
    font-size: 1.5rem;
    cursor: pointer;
}

/* Контент-примитивы внутри студии (были у citizen/architect) */
.kb-studio-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 14px;
}
.kb-studio-card {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 0.75rem;
    background: var(--bg-tertiary);
}
.kb-studio-card h4 {
    margin: 0 0 0.35rem;
    font-size: 0.95rem;
}
.kb-studio-card p {
    margin: 0.25rem 0;
    font-size: 0.85rem;
    color: var(--text-secondary);
}
.kb-studio-meta {
    font-size: 0.75rem;
    color: var(--text-muted);
}
/* ── css/components/utilities.css ── */
/**
 * Utility classes (shared)
 * Used to remove inline styles from templates in a safe, incremental way.
 */

/* Layout */
.u-flex { display: flex; }
.u-inline-flex { display: inline-flex; }
.u-flex-col { flex-direction: column; }
.u-flex-wrap { flex-wrap: wrap; }
.u-flex-1 { flex: 1; }
.u-items-center { align-items: center; }
.u-items-start { align-items: flex-start; }
.u-items-end { align-items: flex-end; }
.u-justify-between { justify-content: space-between; }
.u-justify-center { justify-content: center; }
.u-gap-xs { gap: var(--spacing-xs); }
.u-gap-sm { gap: var(--spacing-sm); }
.u-gap-md { gap: var(--spacing-md); }
.u-gap-lg { gap: var(--spacing-lg); }
.u-gap-8 { gap: 8px; }
.u-w-full { width: 100%; }
.u-min-w-0 { min-width: 0; }
.u-overflow-auto { overflow: auto; }
.u-block { display: block; }

/* Spacing */
.u-m-0 { margin: 0; }
.u-mt-xs { margin-top: var(--spacing-xs); }
.u-mt-sm { margin-top: var(--spacing-sm); }
.u-mt-md { margin-top: var(--spacing-md); }
.u-mb-xs { margin-bottom: var(--spacing-xs); }
.u-mb-sm { margin-bottom: var(--spacing-sm); }
.u-mb-md { margin-bottom: var(--spacing-md); }
.u-mb-0 { margin-bottom: 0; }
.u-mr-xs { margin-right: 4px; }
.u-mr-sm { margin-right: 8px; }
.u-p-0 { padding: 0; }
.u-p-sm { padding: var(--spacing-sm); }
.u-p-md { padding: var(--spacing-md); }
.u-p-lg { padding: var(--spacing-lg); }

/* Text */
.u-text-center { text-align: center; }
.u-text-left { text-align: left; }
.u-text-muted { color: var(--text-muted); }
.u-text-secondary { color: var(--text-secondary); }
.u-text-primary { color: var(--text-primary); }
.u-text-white { color: var(--text-inverse); }
.u-text-danger { color: var(--danger); }
.u-text-success { color: var(--success); }
.u-text-warning { color: var(--warning); }
.u-fw-400 { font-weight: var(--font-weight-normal, 400); }
.u-fw-500 { font-weight: var(--font-weight-medium, 500); }
.u-fw-600 { font-weight: var(--font-weight-semibold, 600); }
.u-fw-700 { font-weight: var(--font-weight-bold, 700); }
.u-text-xs { font-size: var(--font-size-xs, 0.75rem); }
.u-text-sm { font-size: var(--font-size-sm, 0.8125rem); }
.u-text-base { font-size: var(--font-size-base, 0.875rem); }
.u-text-md { font-size: var(--font-size-md, 1rem); }
.u-text-lg { font-size: var(--font-size-lg, 1.125rem); }
.u-text-xl { font-size: var(--font-size-xl, 1.25rem); }
.u-text-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.u-text-nowrap { white-space: nowrap; }
.u-va-n3 { vertical-align: -3px; }
.u-va-n4 { vertical-align: -4px; }

/* Sizing helpers */
.u-max-h-360 { max-height: 360px; }
.u-max-h-500 { max-height: 500px; }

/* Grid */
.u-grid { display: grid; }
.u-grid-cols-2 { grid-template-columns: 1fr 1fr; }
.u-grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* Visibility */
.u-hidden { display: none !important; }

/* Screen-reader only (and safe "hidden" for file inputs: keeps element clickable via user gesture) */
.u-sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

/* File input overlay: ensures the click is on the real <input type="file"> (Safari-friendly). */
.upload-file-input-overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Keep nearly invisible but not fully 0 to avoid rare Safari quirks */
  opacity: 0.001;
  cursor: pointer;
  z-index: 2;
  pointer-events: auto;
}



/* ========================================================================
 * KPI chips — компактные показатели для header'ов и панелей.
 * Используется через {% from 'components/_macros.html' import kpi_chip %}
 * См. _macros.html для API.
 * Унификация: producer .header-kpi и moderator .moderator-header-stats
 * следуют тому же визуальному паттерну.
 * ======================================================================== */

.kpi-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.8125rem;
    color: var(--text-secondary);
    white-space: nowrap;
}
.kpi-chip-label {
    font-weight: 400;
}
.kpi-chip-value {
    font-weight: 600;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}
.kpi-chip--success .kpi-chip-value { color: var(--success); }
.kpi-chip--warning .kpi-chip-value { color: var(--warning); }
.kpi-chip--danger .kpi-chip-value { color: var(--danger); }

/* hide-mobile utility — для текста, скрывающегося на узких экранах
 * (используется в icon_button macro и producer/moderator headers) */
@media (max-width: 768px) {
    .hide-mobile { display: none !important; }
}

/* loading-state мини-вариант — spinner + текст внутри секции
 * (для inline-loading в карточках, не путать с глобальным
 * #loading-overlay, который полноэкранный) */
.loading-state {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem;
    color: var(--text-muted);
    font-size: 0.875rem;
}
.loading-state--center { justify-content: center; }
.loading-state--padded { padding: 1.5rem; }

/* BEM-modifier: маленький spinner (14px) — для inline-state.
 * Сбрасываем margin от базового .loading-spinner (там 16px снизу,
 * что добавляет лишний отступ во flex-родителе .loading-state). */
.loading-spinner--sm {
    width: 14px;
    height: 14px;
    border-width: 2px;
    margin: 0;
}

/* ==========================================================================
   SKELETON placeholders — для content-shimmer во время начальной загрузки.
   Альтернатива spinner'у для лучшего perceived performance.
   ==========================================================================
   Использование:
     <div class="skeleton skeleton-line"></div>
     <div class="skeleton skeleton-line skeleton-line--short"></div>
     <div class="skeleton skeleton-block"></div>
     <div class="skeleton skeleton-circle"></div>
   Или через макрос {{ skeleton(lines=3) }}. */

.skeleton {
    background: linear-gradient(
        90deg,
        var(--bg-secondary) 25%,
        var(--bg-elevated, var(--white)) 37%,
        var(--bg-secondary) 63%
    );
    background-size: 400% 100%;
    animation: skeleton-shimmer 1.4s ease-in-out infinite;
    border-radius: var(--radius-sm);
    display: block;
}
.skeleton-line {
    height: 12px;
    margin-bottom: 8px;
    width: 100%;
}
.skeleton-line:last-child { margin-bottom: 0; }
.skeleton-line--short { width: 60%; }
.skeleton-line--medium { width: 80%; }
.skeleton-line--title {
    height: 18px;
    width: 40%;
    margin-bottom: 12px;
}
.skeleton-block {
    height: 80px;
    margin-bottom: 8px;
    border-radius: var(--radius);
}
.skeleton-circle {
    width: 40px; height: 40px;
    border-radius: 50%;
}

@keyframes skeleton-shimmer {
    0% { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

@media (prefers-reduced-motion: reduce) {
    .skeleton { animation: none; }
}
/* ── css/components/kb_widget.css ── */
/* =====================================================
   KB Dashboard Widget (Data Engineer / Historian)
   Component-level, theme-neutral.
   ===================================================== */

.kb-widget-actions {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.kb-widget-actions .btn {
  width: 100%;
}

.kb-health {
  background: var(--surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  padding: 12px;
}

.kb-health-row {
  display: flex;
  gap: 12px;
  justify-content: space-between;
  flex-wrap: wrap;
  font-size: 0.85rem;
  color: var(--text-secondary);
  margin-bottom: 10px;
}

.kb-health-row strong {
  color: var(--text-primary);
  font-weight: 600;
}

.kb-health-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

@media (max-width: 1200px) {
  .kb-health-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 640px) {
  .kb-health-grid {
    grid-template-columns: 1fr;
  }
}

.kb-health-card {
  border: 1px solid var(--border-color);
  background: var(--background);
  border-radius: 10px;
  padding: 10px;
}

.kb-health-card.is-warn {
  border-color: rgba(245, 158, 11, 0.55);
  background: rgba(245, 158, 11, 0.08);
}

.kb-health-label {
  font-size: 0.75rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.kb-health-value {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.2;
  margin-top: 4px;
}

.kb-health-sub {
  font-size: 0.8rem;
  color: var(--text-secondary);
  margin-top: 4px;
}

.kb-health-alert {
  margin-top: 10px;
  padding: 10px;
  border-radius: 10px;
  border: 1px solid rgba(245, 158, 11, 0.55);
  background: rgba(245, 158, 11, 0.08);
  color: var(--text-primary);
  font-size: 0.85rem;
}

.kb-health-details {
  margin-top: 10px;
}

.kb-health-details summary {
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--text-primary);
}

.kb-health-splits {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin-top: 10px;
}

@media (max-width: 640px) {
  .kb-health-splits {
    grid-template-columns: 1fr;
  }
}

.kb-health-mini-title {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  margin-bottom: 6px;
}

.kb-health-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.kb-health-list li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 8px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--surface);
  margin-bottom: 6px;
  font-size: 0.85rem;
}

.kb-health-list li span {
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kb-health-list li strong {
  color: var(--text-primary);
  font-weight: 700;
}

/* Historian-specific helpers (no dependency on Bootstrap, but plays well with it) */
.kb-panel details > summary {
  user-select: none;
}
