@charset "UTF-8";/*!
 * Pico CSS ✨ v2.1.1 (https://picocss.com)
 * Copyright 2019-2025 - Licensed under MIT
 */:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:0.1rem;--pico-border-radius:0.25rem;--pico-border-width:0.0625rem;--pico-outline-width:0.125rem;--pico-transition:0.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:0.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 0.0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(0.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:0.5rem;--pico-nav-link-spacing-vertical:0.5rem;--pico-nav-link-spacing-horizontal:0.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--pico-icon-close:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--pico-icon-loading:url("data:image/svg+xml,%3Csvg fill='none' height='24' width='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3E%3Cstyle%3E g %7B animation: rotate 2s linear infinite; transform-origin: center center; %7D circle %7B stroke-dasharray: 75,100; stroke-dashoffset: -5; animation: dash 1.5s ease-in-out infinite; stroke-linecap: round; %7D @keyframes rotate %7B 0%25 %7B transform: rotate(0deg); %7D 100%25 %7B transform: rotate(360deg); %7D %7D @keyframes dash %7B 0%25 %7B stroke-dasharray: 1,100; stroke-dashoffset: 0; %7D 50%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -17.5; %7D 100%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -62; %7D %7D %3C/style%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='10' fill='none' stroke='rgb(136, 145, 164)' stroke-width='4' /%3E%3C/g%3E%3C/svg%3E")}@media (min-width:576px){:host,:root{--pico-font-size:106.25%}}@media (min-width:768px){:host,:root{--pico-font-size:112.5%}}@media (min-width:1024px){:host,:root{--pico-font-size:118.75%}}@media (min-width:1280px){:host,:root{--pico-font-size:125%}}@media (min-width:1536px){:host,:root{--pico-font-size:131.25%}}a{--pico-text-decoration:underline}a.contrast,a.secondary{--pico-text-decoration:underline}small{--pico-font-size:0.875em}h1,h2,h3,h4,h5,h6{--pico-font-weight:700}h1{--pico-font-size:2rem;--pico-line-height:1.125;--pico-typography-spacing-top:3rem}h2{--pico-font-size:1.75rem;--pico-line-height:1.15;--pico-typography-spacing-top:2.625rem}h3{--pico-font-size:1.5rem;--pico-line-height:1.175;--pico-typography-spacing-top:2.25rem}h4{--pico-font-size:1.25rem;--pico-line-height:1.2;--pico-typography-spacing-top:1.874rem}h5{--pico-font-size:1.125rem;--pico-line-height:1.225;--pico-typography-spacing-top:1.6875rem}h6{--pico-font-size:1rem;--pico-line-height:1.25;--pico-typography-spacing-top:1.5rem}tfoot td,tfoot th,thead td,thead th{--pico-font-weight:600;--pico-border-width:0.1875rem}code,kbd,pre,samp{--pico-font-family:var(--pico-font-family-monospace)}kbd{--pico-font-weight:bolder}:where(select,textarea),input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pico-outline-width:0.0625rem}[type=search]{--pico-border-radius:5rem}[type=checkbox],[type=radio]{--pico-border-width:0.125rem}[type=checkbox][role=switch]{--pico-border-width:0.1875rem}details.dropdown summary:not([role=button]){--pico-outline-width:0.0625rem}nav details.dropdown summary:focus-visible{--pico-outline-width:0.125rem}[role=search]{--pico-border-radius:5rem}[role=group]:has(button.secondary:focus,[type=submit].secondary:focus,[type=button].secondary:focus,[role=button].secondary:focus),[role=search]:has(button.secondary:focus,[type=submit].secondary:focus,[type=button].secondary:focus,[role=button].secondary:focus){--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-secondary-focus)}[role=group]:has(button.contrast:focus,[type=submit].contrast:focus,[type=button].contrast:focus,[role=button].contrast:focus),[role=search]:has(button.contrast:focus,[type=submit].contrast:focus,[type=button].contrast:focus,[role=button].contrast:focus){--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-contrast-focus)}[role=group] [role=button],[role=group] [type=button],[role=group] [type=submit],[role=group] button,[role=search] [role=button],[role=search] [type=button],[role=search] [type=submit],[role=search] button{--pico-form-element-spacing-horizontal:2rem}details summary[role=button]:not(.outline)::after{filter:brightness(0) invert(1)}[aria-busy=true]:not(input,select,textarea):is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline)::before{filter:brightness(0) invert(1)}:host(:not([data-theme=dark])),:root:not([data-theme=dark]),[data-theme=light]{color-scheme:light;--pico-background-color:#fff;--pico-color:#373c44;--pico-text-selection-color:rgba(2, 154, 232, 0.25);--pico-muted-color:#646b79;--pico-muted-border-color:rgb(231, 234, 239.5);--pico-primary:#0172ad;--pico-primary-background:#0172ad;--pico-primary-border:var(--pico-primary-background);--pico-primary-underline:rgba(1, 114, 173, 0.5);--pico-primary-hover:#015887;--pico-primary-hover-background:#02659a;--pico-primary-hover-border:var(--pico-primary-hover-background);--pico-primary-hover-underline:var(--pico-primary-hover);--pico-primary-focus:rgba(2, 154, 232, 0.5);--pico-primary-inverse:#fff;--pico-secondary:#5d6b89;--pico-secondary-background:#525f7a;--pico-secondary-border:var(--pico-secondary-background);--pico-secondary-underline:rgba(93, 107, 137, 0.5);--pico-secondary-hover:#48536b;--pico-secondary-hover-background:#48536b;--pico-secondary-hover-border:var(--pico-secondary-hover-background);--pico-secondary-hover-underline:var(--pico-secondary-hover);--pico-secondary-focus:rgba(93, 107, 137, 0.25);--pico-secondary-inverse:#fff;--pico-contrast:#181c25;--pico-contrast-background:#181c25;--pico-contrast-border:var(--pico-contrast-background);--pico-contrast-underline:rgba(24, 28, 37, 0.5);--pico-contrast-hover:#000;--pico-contrast-hover-background:#000;--pico-contrast-hover-border:var(--pico-contrast-hover-background);--pico-contrast-hover-underline:var(--pico-secondary-hover);--pico-contrast-focus:rgba(93, 107, 137, 0.25);--pico-contrast-inverse:#fff;--pico-box-shadow:0.0145rem 0.029rem 0.174rem rgba(129, 145, 181, 0.01698),0.0335rem 0.067rem 0.402rem rgba(129, 145, 181, 0.024),0.0625rem 0.125rem 0.75rem rgba(129, 145, 181, 0.03),0.1125rem 0.225rem 1.35rem rgba(129, 145, 181, 0.036),0.2085rem 0.417rem 2.502rem rgba(129, 145, 181, 0.04302),0.5rem 1rem 6rem rgba(129, 145, 181, 0.06),0 0 0 0.0625rem rgba(129, 145, 181, 0.015);--pico-h1-color:#2d3138;--pico-h2-color:#373c44;--pico-h3-color:#424751;--pico-h4-color:#4d535e;--pico-h5-color:#5c6370;--pico-h6-color:#646b79;--pico-mark-background-color:rgb(252.5, 230.5, 191.5);--pico-mark-color:#0f1114;--pico-ins-color:rgb(28.5, 105.5, 84);--pico-del-color:rgb(136, 56.5, 53);--pico-blockquote-border-color:var(--pico-muted-border-color);--pico-blockquote-footer-color:var(--pico-muted-color);--pico-button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-table-border-color:var(--pico-muted-border-color);--pico-table-row-stripped-background-color:rgba(111, 120, 135, 0.0375);--pico-code-background-color:rgb(243, 244.5, 246.75);--pico-code-color:#646b79;--pico-code-kbd-background-color:var(--pico-color);--pico-code-kbd-color:var(--pico-background-color);--pico-form-element-background-color:rgb(251, 251.5, 252.25);--pico-form-element-selected-background-color:#dfe3eb;--pico-form-element-border-color:#cfd5e2;--pico-form-element-color:#23262c;--pico-form-element-placeholder-color:var(--pico-muted-color);--pico-form-element-active-background-color:#fff;--pico-form-element-active-border-color:var(--pico-primary-border);--pico-form-element-focus-color:var(--pico-primary-border);--pico-form-element-disabled-opacity:0.5;--pico-form-element-invalid-border-color:rgb(183.5, 105.5, 106.5);--pico-form-element-invalid-active-border-color:rgb(200.25, 79.25, 72.25);--pico-form-element-invalid-focus-color:var(--pico-form-element-invalid-active-border-color);--pico-form-element-valid-border-color:rgb(76, 154.5, 137.5);--pico-form-element-valid-active-border-color:rgb(39, 152.75, 118.75);--pico-form-element-valid-focus-color:var(--pico-form-element-valid-active-border-color);--pico-switch-background-color:#bfc7d9;--pico-switch-checked-background-color:var(--pico-primary-background);--pico-switch-color:#fff;--pico-switch-thumb-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-range-border-color:#dfe3eb;--pico-range-active-border-color:#bfc7d9;--pico-range-thumb-border-color:var(--pico-background-color);--pico-range-thumb-color:var(--pico-secondary-background);--pico-range-thumb-active-color:var(--pico-primary-background);--pico-accordion-border-color:var(--pico-muted-border-color);--pico-accordion-active-summary-color:var(--pico-primary-hover);--pico-accordion-close-summary-color:var(--pico-color);--pico-accordion-open-summary-color:var(--pico-muted-color);--pico-card-background-color:var(--pico-background-color);--pico-card-border-color:var(--pico-muted-border-color);--pico-card-box-shadow:var(--pico-box-shadow);--pico-card-sectioning-background-color:rgb(251, 251.5, 252.25);--pico-dropdown-background-color:#fff;--pico-dropdown-border-color:#eff1f4;--pico-dropdown-box-shadow:var(--pico-box-shadow);--pico-dropdown-color:var(--pico-color);--pico-dropdown-hover-background-color:#eff1f4;--pico-loading-spinner-opacity:0.5;--pico-modal-overlay-background-color:rgba(232, 234, 237, 0.75);--pico-progress-background-color:#dfe3eb;--pico-progress-color:var(--pico-primary-background);--pico-tooltip-background-color:var(--pico-contrast-background);--pico-tooltip-color:var(--pico-contrast-inverse);--pico-icon-valid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 154.5, 137.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-invalid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200.25, 79.25, 72.25)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E")}:host(:not([data-theme=dark])) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:root:not([data-theme=dark]) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),[data-theme=light] input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pico-form-element-focus-color:var(--pico-primary-focus)}@media only screen and (prefers-color-scheme:dark){:host(:not([data-theme])),:root:not([data-theme]){color-scheme:dark;--pico-background-color:rgb(19, 22.5, 30.5);--pico-color:#c2c7d0;--pico-text-selection-color:rgba(1, 170, 255, 0.1875);--pico-muted-color:#7b8495;--pico-muted-border-color:#202632;--pico-primary:#01aaff;--pico-primary-background:#0172ad;--pico-primary-border:var(--pico-primary-background);--pico-primary-underline:rgba(1, 170, 255, 0.5);--pico-primary-hover:#79c0ff;--pico-primary-hover-background:#017fc0;--pico-primary-hover-border:var(--pico-primary-hover-background);--pico-primary-hover-underline:var(--pico-primary-hover);--pico-primary-focus:rgba(1, 170, 255, 0.375);--pico-primary-inverse:#fff;--pico-secondary:#969eaf;--pico-secondary-background:#525f7a;--pico-secondary-border:var(--pico-secondary-background);--pico-secondary-underline:rgba(150, 158, 175, 0.5);--pico-secondary-hover:#b3b9c5;--pico-secondary-hover-background:#5d6b89;--pico-secondary-hover-border:var(--pico-secondary-hover-background);--pico-secondary-hover-underline:var(--pico-secondary-hover);--pico-secondary-focus:rgba(144, 158, 190, 0.25);--pico-secondary-inverse:#fff;--pico-contrast:#dfe3eb;--pico-contrast-background:#eff1f4;--pico-contrast-border:var(--pico-contrast-background);--pico-contrast-underline:rgba(223, 227, 235, 0.5);--pico-contrast-hover:#fff;--pico-contrast-hover-background:#fff;--pico-contrast-hover-border:var(--pico-contrast-hover-background);--pico-contrast-hover-underline:var(--pico-contrast-hover);--pico-contrast-focus:rgba(207, 213, 226, 0.25);--pico-contrast-inverse:#000;--pico-box-shadow:0.0145rem 0.029rem 0.174rem rgba(7, 8.5, 12, 0.01698),0.0335rem 0.067rem 0.402rem rgba(7, 8.5, 12, 0.024),0.0625rem 0.125rem 0.75rem rgba(7, 8.5, 12, 0.03),0.1125rem 0.225rem 1.35rem rgba(7, 8.5, 12, 0.036),0.2085rem 0.417rem 2.502rem rgba(7, 8.5, 12, 0.04302),0.5rem 1rem 6rem rgba(7, 8.5, 12, 0.06),0 0 0 0.0625rem rgba(7, 8.5, 12, 0.015);--pico-h1-color:#f0f1f3;--pico-h2-color:#e0e3e7;--pico-h3-color:#c2c7d0;--pico-h4-color:#b3b9c5;--pico-h5-color:#a4acba;--pico-h6-color:#8891a4;--pico-mark-background-color:#014063;--pico-mark-color:#fff;--pico-ins-color:#62af9a;--pico-del-color:rgb(205.5, 126, 123);--pico-blockquote-border-color:var(--pico-muted-border-color);--pico-blockquote-footer-color:var(--pico-muted-color);--pico-button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-table-border-color:var(--pico-muted-border-color);--pico-table-row-stripped-background-color:rgba(111, 120, 135, 0.0375);--pico-code-background-color:rgb(26, 30.5, 40.25);--pico-code-color:#8891a4;--pico-code-kbd-background-color:var(--pico-color);--pico-code-kbd-color:var(--pico-background-color);--pico-form-element-background-color:rgb(28, 33, 43.5);--pico-form-element-selected-background-color:#2a3140;--pico-form-element-border-color:#2a3140;--pico-form-element-color:#e0e3e7;--pico-form-element-placeholder-color:#8891a4;--pico-form-element-active-background-color:rgb(26, 30.5, 40.25);--pico-form-element-active-border-color:var(--pico-primary-border);--pico-form-element-focus-color:var(--pico-primary-border);--pico-form-element-disabled-opacity:0.5;--pico-form-element-invalid-border-color:rgb(149.5, 74, 80);--pico-form-element-invalid-active-border-color:rgb(183.25, 63.5, 59);--pico-form-element-invalid-focus-color:var(--pico-form-element-invalid-active-border-color);--pico-form-element-valid-border-color:#2a7b6f;--pico-form-element-valid-active-border-color:rgb(22, 137, 105.5);--pico-form-element-valid-focus-color:var(--pico-form-element-valid-active-border-color);--pico-switch-background-color:#333c4e;--pico-switch-checked-background-color:var(--pico-primary-background);--pico-switch-color:#fff;--pico-switch-thumb-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-range-border-color:#202632;--pico-range-active-border-color:#2a3140;--pico-range-thumb-border-color:var(--pico-background-color);--pico-range-thumb-color:var(--pico-secondary-background);--pico-range-thumb-active-color:var(--pico-primary-background);--pico-accordion-border-color:var(--pico-muted-border-color);--pico-accordion-active-summary-color:var(--pico-primary-hover);--pico-accordion-close-summary-color:var(--pico-color);--pico-accordion-open-summary-color:var(--pico-muted-color);--pico-card-background-color:#181c25;--pico-card-border-color:var(--pico-card-background-color);--pico-card-box-shadow:var(--pico-box-shadow);--pico-card-sectioning-background-color:rgb(26, 30.5, 40.25);--pico-dropdown-background-color:#181c25;--pico-dropdown-border-color:#202632;--pico-dropdown-box-shadow:var(--pico-box-shadow);--pico-dropdown-color:var(--pico-color);--pico-dropdown-hover-background-color:#202632;--pico-loading-spinner-opacity:0.5;--pico-modal-overlay-background-color:rgba(7.5, 8.5, 10, 0.75);--pico-progress-background-color:#202632;--pico-progress-color:var(--pico-primary-background);--pico-tooltip-background-color:var(--pico-contrast-background);--pico-tooltip-color:var(--pico-contrast-inverse);--pico-icon-valid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-invalid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E")}:host(:not([data-theme])) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:root:not([data-theme]) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pico-form-element-focus-color:var(--pico-primary-focus)}:host(:not([data-theme])) details summary[role=button].contrast:not(.outline)::after,:root:not([data-theme]) details summary[role=button].contrast:not(.outline)::after{filter:brightness(0)}:host(:not([data-theme])) [aria-busy=true]:not(input,select,textarea).contrast:is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline)::before,:root:not([data-theme]) [aria-busy=true]:not(input,select,textarea).contrast:is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline)::before{filter:brightness(0)}}[data-theme=dark]{color-scheme:dark;--pico-background-color:rgb(19, 22.5, 30.5);--pico-color:#c2c7d0;--pico-text-selection-color:rgba(1, 170, 255, 0.1875);--pico-muted-color:#7b8495;--pico-muted-border-color:#202632;--pico-primary:#01aaff;--pico-primary-background:#0172ad;--pico-primary-border:var(--pico-primary-background);--pico-primary-underline:rgba(1, 170, 255, 0.5);--pico-primary-hover:#79c0ff;--pico-primary-hover-background:#017fc0;--pico-primary-hover-border:var(--pico-primary-hover-background);--pico-primary-hover-underline:var(--pico-primary-hover);--pico-primary-focus:rgba(1, 170, 255, 0.375);--pico-primary-inverse:#fff;--pico-secondary:#969eaf;--pico-secondary-background:#525f7a;--pico-secondary-border:var(--pico-secondary-background);--pico-secondary-underline:rgba(150, 158, 175, 0.5);--pico-secondary-hover:#b3b9c5;--pico-secondary-hover-background:#5d6b89;--pico-secondary-hover-border:var(--pico-secondary-hover-background);--pico-secondary-hover-underline:var(--pico-secondary-hover);--pico-secondary-focus:rgba(144, 158, 190, 0.25);--pico-secondary-inverse:#fff;--pico-contrast:#dfe3eb;--pico-contrast-background:#eff1f4;--pico-contrast-border:var(--pico-contrast-background);--pico-contrast-underline:rgba(223, 227, 235, 0.5);--pico-contrast-hover:#fff;--pico-contrast-hover-background:#fff;--pico-contrast-hover-border:var(--pico-contrast-hover-background);--pico-contrast-hover-underline:var(--pico-contrast-hover);--pico-contrast-focus:rgba(207, 213, 226, 0.25);--pico-contrast-inverse:#000;--pico-box-shadow:0.0145rem 0.029rem 0.174rem rgba(7, 8.5, 12, 0.01698),0.0335rem 0.067rem 0.402rem rgba(7, 8.5, 12, 0.024),0.0625rem 0.125rem 0.75rem rgba(7, 8.5, 12, 0.03),0.1125rem 0.225rem 1.35rem rgba(7, 8.5, 12, 0.036),0.2085rem 0.417rem 2.502rem rgba(7, 8.5, 12, 0.04302),0.5rem 1rem 6rem rgba(7, 8.5, 12, 0.06),0 0 0 0.0625rem rgba(7, 8.5, 12, 0.015);--pico-h1-color:#f0f1f3;--pico-h2-color:#e0e3e7;--pico-h3-color:#c2c7d0;--pico-h4-color:#b3b9c5;--pico-h5-color:#a4acba;--pico-h6-color:#8891a4;--pico-mark-background-color:#014063;--pico-mark-color:#fff;--pico-ins-color:#62af9a;--pico-del-color:rgb(205.5, 126, 123);--pico-blockquote-border-color:var(--pico-muted-border-color);--pico-blockquote-footer-color:var(--pico-muted-color);--pico-button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-table-border-color:var(--pico-muted-border-color);--pico-table-row-stripped-background-color:rgba(111, 120, 135, 0.0375);--pico-code-background-color:rgb(26, 30.5, 40.25);--pico-code-color:#8891a4;--pico-code-kbd-background-color:var(--pico-color);--pico-code-kbd-color:var(--pico-background-color);--pico-form-element-background-color:rgb(28, 33, 43.5);--pico-form-element-selected-background-color:#2a3140;--pico-form-element-border-color:#2a3140;--pico-form-element-color:#e0e3e7;--pico-form-element-placeholder-color:#8891a4;--pico-form-element-active-background-color:rgb(26, 30.5, 40.25);--pico-form-element-active-border-color:var(--pico-primary-border);--pico-form-element-focus-color:var(--pico-primary-border);--pico-form-element-disabled-opacity:0.5;--pico-form-element-invalid-border-color:rgb(149.5, 74, 80);--pico-form-element-invalid-active-border-color:rgb(183.25, 63.5, 59);--pico-form-element-invalid-focus-color:var(--pico-form-element-invalid-active-border-color);--pico-form-element-valid-border-color:#2a7b6f;--pico-form-element-valid-active-border-color:rgb(22, 137, 105.5);--pico-form-element-valid-focus-color:var(--pico-form-element-valid-active-border-color);--pico-switch-background-color:#333c4e;--pico-switch-checked-background-color:var(--pico-primary-background);--pico-switch-color:#fff;--pico-switch-thumb-box-shadow:0 0 0 rgba(0, 0, 0, 0);--pico-range-border-color:#202632;--pico-range-active-border-color:#2a3140;--pico-range-thumb-border-color:var(--pico-background-color);--pico-range-thumb-color:var(--pico-secondary-background);--pico-range-thumb-active-color:var(--pico-primary-background);--pico-accordion-border-color:var(--pico-muted-border-color);--pico-accordion-active-summary-color:var(--pico-primary-hover);--pico-accordion-close-summary-color:var(--pico-color);--pico-accordion-open-summary-color:var(--pico-muted-color);--pico-card-background-color:#181c25;--pico-card-border-color:var(--pico-card-background-color);--pico-card-box-shadow:var(--pico-box-shadow);--pico-card-sectioning-background-color:rgb(26, 30.5, 40.25);--pico-dropdown-background-color:#181c25;--pico-dropdown-border-color:#202632;--pico-dropdown-box-shadow:var(--pico-box-shadow);--pico-dropdown-color:var(--pico-color);--pico-dropdown-hover-background-color:#202632;--pico-loading-spinner-opacity:0.5;--pico-modal-overlay-background-color:rgba(7.5, 8.5, 10, 0.75);--pico-progress-background-color:#202632;--pico-progress-color:var(--pico-primary-background);--pico-tooltip-background-color:var(--pico-contrast-background);--pico-tooltip-color:var(--pico-contrast-inverse);--pico-icon-valid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-invalid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E")}[data-theme=dark] input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pico-form-element-focus-color:var(--pico-primary-focus)}[data-theme=dark] details summary[role=button].contrast:not(.outline)::after{filter:brightness(0)}[data-theme=dark] [aria-busy=true]:not(input,select,textarea).contrast:is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline)::before{filter:brightness(0)}[type=checkbox],[type=radio],[type=range],progress{accent-color:var(--pico-primary)}*,::after,::before{box-sizing:border-box;background-repeat:no-repeat}::after,::before{text-decoration:inherit;vertical-align:inherit}:where(:host),:where(:root){-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--pico-background-color);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:var(--pico-font-size);line-height:var(--pico-line-height);font-family:var(--pico-font-family);text-underline-offset:var(--pico-text-underline-offset);text-rendering:optimizeLegibility;overflow-wrap:break-word;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{width:100%;margin:0}main{display:block}body>footer,body>header,body>main{padding-block:var(--pico-block-spacing-vertical)}section{margin-bottom:var(--pico-block-spacing-vertical)}.container,.container-fluid{width:100%;margin-right:auto;margin-left:auto;padding-right:var(--pico-spacing);padding-left:var(--pico-spacing)}@media (min-width:576px){.container{max-width:510px;padding-right:0;padding-left:0}}@media (min-width:768px){.container{max-width:700px}}@media (min-width:1024px){.container{max-width:950px}}@media (min-width:1280px){.container{max-width:1200px}}@media (min-width:1536px){.container{max-width:1450px}}.grid{grid-column-gap:var(--pico-grid-column-gap);grid-row-gap:var(--pico-grid-row-gap);display:grid;grid-template-columns:1fr}@media (min-width:768px){.grid{grid-template-columns:repeat(auto-fit,minmax(0%,1fr))}}.grid>*{min-width:0}.overflow-auto{overflow:auto}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}address,blockquote,dl,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--pico-typography-spacing-vertical);color:var(--pico-color);font-style:normal;font-weight:var(--pico-font-weight)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--pico-typography-spacing-vertical);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:var(--pico-font-size);line-height:var(--pico-line-height);font-family:var(--pico-font-family)}h1{--pico-color:var(--pico-h1-color)}h2{--pico-color:var(--pico-h2-color)}h3{--pico-color:var(--pico-h3-color)}h4{--pico-color:var(--pico-h4-color)}h5{--pico-color:var(--pico-h5-color)}h6{--pico-color:var(--pico-h6-color)}:where(article,address,blockquote,dl,figure,form,ol,p,pre,table,ul)~:is(h1,h2,h3,h4,h5,h6){margin-top:var(--pico-typography-spacing-top)}p{margin-bottom:var(--pico-typography-spacing-vertical)}hgroup{margin-bottom:var(--pico-typography-spacing-vertical)}hgroup>*{margin-top:0;margin-bottom:0}hgroup>:not(:first-child):last-child{--pico-color:var(--pico-muted-color);--pico-font-weight:unset;font-size:1rem}:where(ol,ul) li{margin-bottom:calc(var(--pico-typography-spacing-vertical) * .25)}:where(dl,ol,ul) :where(dl,ol,ul){margin:0;margin-top:calc(var(--pico-typography-spacing-vertical) * .25)}ul li{list-style:square}mark{padding:.125rem .25rem;background-color:var(--pico-mark-background-color);color:var(--pico-mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--pico-typography-spacing-vertical) 0;padding:var(--pico-spacing);border-right:none;border-left:.25rem solid var(--pico-blockquote-border-color);border-inline-start:0.25rem solid var(--pico-blockquote-border-color);border-inline-end:none}blockquote footer{margin-top:calc(var(--pico-typography-spacing-vertical) * .5);color:var(--pico-blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--pico-ins-color);text-decoration:none}del{color:var(--pico-del-color)}::-moz-selection{background-color:var(--pico-text-selection-color)}::selection{background-color:var(--pico-text-selection-color)}:where(a:not([role=button])),[role=link]{--pico-color:var(--pico-primary);--pico-background-color:transparent;--pico-underline:var(--pico-primary-underline);outline:0;background-color:var(--pico-background-color);color:var(--pico-color);-webkit-text-decoration:var(--pico-text-decoration);text-decoration:var(--pico-text-decoration);text-decoration-color:var(--pico-underline);text-underline-offset:0.125em;transition:background-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition),-webkit-text-decoration var(--pico-transition);transition:background-color var(--pico-transition),color var(--pico-transition),text-decoration var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),color var(--pico-transition),text-decoration var(--pico-transition),box-shadow var(--pico-transition),-webkit-text-decoration var(--pico-transition)}:where(a:not([role=button])):is([aria-current]:not([aria-current=false]),:hover,:active,:focus),[role=link]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-primary-hover);--pico-underline:var(--pico-primary-hover-underline);--pico-text-decoration:underline}:where(a:not([role=button])):focus-visible,[role=link]:focus-visible{box-shadow:0 0 0 var(--pico-outline-width) var(--pico-primary-focus)}:where(a:not([role=button])).secondary,[role=link].secondary{--pico-color:var(--pico-secondary);--pico-underline:var(--pico-secondary-underline)}:where(a:not([role=button])).secondary:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),[role=link].secondary:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-secondary-hover);--pico-underline:var(--pico-secondary-hover-underline)}:where(a:not([role=button])).contrast,[role=link].contrast{--pico-color:var(--pico-contrast);--pico-underline:var(--pico-contrast-underline)}:where(a:not([role=button])).contrast:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),[role=link].contrast:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-contrast-hover);--pico-underline:var(--pico-contrast-hover-underline)}a[role=button]{display:inline-block}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[role=button],[type=button],[type=file]::file-selector-button,[type=reset],[type=submit],button{--pico-background-color:var(--pico-primary-background);--pico-border-color:var(--pico-primary-border);--pico-color:var(--pico-primary-inverse);--pico-box-shadow:var(--pico-button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);border:var(--pico-border-width) solid var(--pico-border-color);border-radius:var(--pico-border-radius);outline:0;background-color:var(--pico-background-color);box-shadow:var(--pico-box-shadow);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:1rem;line-height:var(--pico-line-height);text-align:center;text-decoration:none;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;transition:background-color var(--pico-transition),border-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition)}[role=button]:is(:hover,:active,:focus),[role=button]:is([aria-current]:not([aria-current=false])),[type=button]:is(:hover,:active,:focus),[type=button]:is([aria-current]:not([aria-current=false])),[type=file]::file-selector-button:is(:hover,:active,:focus),[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])),[type=reset]:is(:hover,:active,:focus),[type=reset]:is([aria-current]:not([aria-current=false])),[type=submit]:is(:hover,:active,:focus),[type=submit]:is([aria-current]:not([aria-current=false])),button:is(:hover,:active,:focus),button:is([aria-current]:not([aria-current=false])){--pico-background-color:var(--pico-primary-hover-background);--pico-border-color:var(--pico-primary-hover-border);--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--pico-color:var(--pico-primary-inverse)}[role=button]:focus,[role=button]:is([aria-current]:not([aria-current=false])):focus,[type=button]:focus,[type=button]:is([aria-current]:not([aria-current=false])):focus,[type=file]::file-selector-button:focus,[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])):focus,[type=reset]:focus,[type=reset]:is([aria-current]:not([aria-current=false])):focus,[type=submit]:focus,[type=submit]:is([aria-current]:not([aria-current=false])):focus,button:focus,button:is([aria-current]:not([aria-current=false])):focus{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-primary-focus)}[type=button],[type=reset],[type=submit]{margin-bottom:var(--pico-spacing)}:is(button,[type=submit],[type=button],[role=button]).secondary,[type=file]::file-selector-button,[type=reset]{--pico-background-color:var(--pico-secondary-background);--pico-border-color:var(--pico-secondary-border);--pico-color:var(--pico-secondary-inverse);cursor:pointer}:is(button,[type=submit],[type=button],[role=button]).secondary:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),[type=file]::file-selector-button:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),[type=reset]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-background-color:var(--pico-secondary-hover-background);--pico-border-color:var(--pico-secondary-hover-border);--pico-color:var(--pico-secondary-inverse)}:is(button,[type=submit],[type=button],[role=button]).secondary:focus,:is(button,[type=submit],[type=button],[role=button]).secondary:is([aria-current]:not([aria-current=false])):focus,[type=file]::file-selector-button:focus,[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])):focus,[type=reset]:focus,[type=reset]:is([aria-current]:not([aria-current=false])):focus{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-secondary-focus)}:is(button,[type=submit],[type=button],[role=button]).contrast{--pico-background-color:var(--pico-contrast-background);--pico-border-color:var(--pico-contrast-border);--pico-color:var(--pico-contrast-inverse)}:is(button,[type=submit],[type=button],[role=button]).contrast:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-background-color:var(--pico-contrast-hover-background);--pico-border-color:var(--pico-contrast-hover-border);--pico-color:var(--pico-contrast-inverse)}:is(button,[type=submit],[type=button],[role=button]).contrast:focus,:is(button,[type=submit],[type=button],[role=button]).contrast:is([aria-current]:not([aria-current=false])):focus{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-contrast-focus)}:is(button,[type=submit],[type=button],[role=button]).outline,[type=reset].outline{--pico-background-color:transparent;--pico-color:var(--pico-primary);--pico-border-color:var(--pico-primary)}:is(button,[type=submit],[type=button],[role=button]).outline:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),[type=reset].outline:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-background-color:transparent;--pico-color:var(--pico-primary-hover);--pico-border-color:var(--pico-primary-hover)}:is(button,[type=submit],[type=button],[role=button]).outline.secondary,[type=reset].outline{--pico-color:var(--pico-secondary);--pico-border-color:var(--pico-secondary)}:is(button,[type=submit],[type=button],[role=button]).outline.secondary:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),[type=reset].outline:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-secondary-hover);--pico-border-color:var(--pico-secondary-hover)}:is(button,[type=submit],[type=button],[role=button]).outline.contrast{--pico-color:var(--pico-contrast);--pico-border-color:var(--pico-contrast)}:is(button,[type=submit],[type=button],[role=button]).outline.contrast:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pico-color:var(--pico-contrast-hover);--pico-border-color:var(--pico-contrast-hover)}:where(button,[type=submit],[type=reset],[type=button],[role=button])[disabled],:where(fieldset[disabled]) :is(button,[type=submit],[type=button],[type=reset],[role=button]){opacity:.5;pointer-events:none}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}td,th{padding:calc(var(--pico-spacing)/ 2) var(--pico-spacing);border-bottom:var(--pico-border-width) solid var(--pico-table-border-color);background-color:var(--pico-background-color);color:var(--pico-color);font-weight:var(--pico-font-weight);text-align:left;text-align:start}tfoot td,tfoot th{border-top:var(--pico-border-width) solid var(--pico-table-border-color);border-bottom:0}table.striped tbody tr:nth-child(odd) td,table.striped tbody tr:nth-child(odd) th{background-color:var(--pico-table-row-stripped-background-color)}:where(audio,canvas,iframe,img,svg,video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img{max-width:100%;height:auto;border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:host),svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-size:.875em;font-family:var(--pico-font-family)}pre code,pre samp{font-size:inherit;font-family:inherit}pre{-ms-overflow-style:scrollbar;overflow:auto}code,kbd,pre,samp{border-radius:var(--pico-border-radius);background:var(--pico-code-background-color);color:var(--pico-code-color);font-weight:var(--pico-font-weight);line-height:initial}code,kbd,samp{display:inline-block;padding:.375rem}pre{display:block;margin-bottom:var(--pico-spacing);overflow-x:auto}pre>code,pre>samp{display:block;padding:var(--pico-spacing);background:0 0;line-height:var(--pico-line-height)}kbd{background-color:var(--pico-code-kbd-background-color);color:var(--pico-code-kbd-color);vertical-align:baseline}figure{display:block;margin:0;padding:0}figure figcaption{padding:calc(var(--pico-spacing) * .5) 0;color:var(--pico-muted-color)}hr{height:0;margin:var(--pico-typography-spacing-vertical) 0;border:0;border-top:1px solid var(--pico-muted-border-color);color:inherit}[hidden],template{display:none!important}canvas{display:inline-block}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--pico-line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:0}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type=file],[type=range]{padding:0;border-width:0}input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2)}fieldset{width:100%;margin:0;margin-bottom:var(--pico-spacing);padding:0;border:0}fieldset legend,label{display:block;margin-bottom:calc(var(--pico-spacing) * .375);color:var(--pico-color);font-weight:var(--pico-form-label-font-weight,var(--pico-font-weight))}fieldset legend{margin-bottom:calc(var(--pico-spacing) * .5)}button[type=submit],input:not([type=checkbox],[type=radio]),select,textarea{width:100%}input:not([type=checkbox],[type=radio],[type=range],[type=file]),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal)}input,select,textarea{--pico-background-color:var(--pico-form-element-background-color);--pico-border-color:var(--pico-form-element-border-color);--pico-color:var(--pico-form-element-color);--pico-box-shadow:none;border:var(--pico-border-width) solid var(--pico-border-color);border-radius:var(--pico-border-radius);outline:0;background-color:var(--pico-background-color);box-shadow:var(--pico-box-shadow);color:var(--pico-color);font-weight:var(--pico-font-weight);transition:background-color var(--pico-transition),border-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition)}:where(select,textarea):not([readonly]):is(:active,:focus),input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]):is(:active,:focus){--pico-background-color:var(--pico-form-element-active-background-color)}:where(select,textarea):not([readonly]):is(:active,:focus),input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(:active,:focus){--pico-border-color:var(--pico-form-element-active-border-color)}:where(select,textarea):not([readonly]):focus,input:not([type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]):focus{--pico-box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-focus-color)}:where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea),input:not([type=submit],[type=button],[type=reset])[disabled],label[aria-disabled=true],select[disabled],textarea[disabled]{opacity:var(--pico-form-element-disabled-opacity);pointer-events:none}label[aria-disabled=true] input[disabled]{opacity:1}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid]{padding-right:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem)!important;padding-left:var(--pico-form-element-spacing-horizontal);padding-inline-start:var(--pico-form-element-spacing-horizontal)!important;padding-inline-end:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem)!important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid=false]:not(select){background-image:var(--pico-icon-valid)}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid=true]:not(select){background-image:var(--pico-icon-invalid)}:where(input,select,textarea)[aria-invalid=false]{--pico-border-color:var(--pico-form-element-valid-border-color)}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--pico-border-color:var(--pico-form-element-valid-active-border-color)!important}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus):not([type=checkbox],[type=radio]){--pico-box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-valid-focus-color)!important}:where(input,select,textarea)[aria-invalid=true]{--pico-border-color:var(--pico-form-element-invalid-border-color)}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--pico-border-color:var(--pico-form-element-invalid-active-border-color)!important}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus):not([type=checkbox],[type=radio]){--pico-box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-invalid-focus-color)!important}[dir=rtl] :where(input,select,textarea):not([type=checkbox],[type=radio]):is([aria-invalid],[aria-invalid=true],[aria-invalid=false]){background-position:center left .75rem}input::-webkit-input-placeholder,input::placeholder,select:invalid,textarea::-webkit-input-placeholder,textarea::placeholder{color:var(--pico-form-element-placeholder-color);opacity:1}input:not([type=checkbox],[type=radio]),select,textarea{margin-bottom:var(--pico-spacing)}select::-ms-expand{border:0;background-color:transparent}select:not([multiple],[size]){padding-right:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem);padding-left:var(--pico-form-element-spacing-horizontal);padding-inline-start:var(--pico-form-element-spacing-horizontal);padding-inline-end:calc(var(--pico-form-element-spacing-horizontal) + 1.5rem);background-image:var(--pico-icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}select[multiple] option:checked{background:var(--pico-form-element-selected-background-color);color:var(--pico-form-element-color)}[dir=rtl] select:not([multiple],[size]){background-position:center left .75rem}textarea{display:block;resize:vertical}textarea[aria-invalid]{--pico-icon-height:calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2);background-position:top right .75rem!important;background-size:1rem var(--pico-icon-height)!important}:where(input,select,textarea,fieldset,.grid)+small{display:block;width:100%;margin-top:calc(var(--pico-spacing) * -.75);margin-bottom:var(--pico-spacing);color:var(--pico-muted-color)}:where(input,select,textarea,fieldset,.grid)[aria-invalid=false]+small{color:var(--pico-ins-color)}:where(input,select,textarea,fieldset,.grid)[aria-invalid=true]+small{color:var(--pico-del-color)}label>:where(input,select,textarea){margin-top:calc(var(--pico-spacing) * .25)}label:has([type=checkbox],[type=radio]){width:-moz-fit-content;width:fit-content;cursor:pointer}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-.125em;margin-inline-end:.5em;border-width:var(--pico-border-width);vertical-align:middle;cursor:pointer}[type=checkbox]::-ms-check,[type=radio]::-ms-check{display:none}[type=checkbox]:checked,[type=checkbox]:checked:active,[type=checkbox]:checked:focus,[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--pico-background-color:var(--pico-primary-background);--pico-border-color:var(--pico-primary-border);background-image:var(--pico-icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=checkbox]~label,[type=radio]~label{display:inline-block;margin-bottom:0;cursor:pointer}[type=checkbox]~label:not(:last-of-type),[type=radio]~label:not(:last-of-type){margin-inline-end:1em}[type=checkbox]:indeterminate{--pico-background-color:var(--pico-primary-background);--pico-border-color:var(--pico-primary-border);background-image:var(--pico-icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=radio]{border-radius:50%}[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--pico-background-color:var(--pico-primary-inverse);border-width:.35em;background-image:none}[type=checkbox][role=switch]{--pico-background-color:var(--pico-switch-background-color);--pico-color:var(--pico-switch-color);width:2.25em;height:1.25em;border:var(--pico-border-width) solid var(--pico-border-color);border-radius:1.25em;background-color:var(--pico-background-color);line-height:1.25em}[type=checkbox][role=switch]:not([aria-invalid]){--pico-border-color:var(--pico-switch-background-color)}[type=checkbox][role=switch]:before{display:block;aspect-ratio:1;height:100%;border-radius:50%;background-color:var(--pico-color);box-shadow:var(--pico-switch-thumb-box-shadow);content:"";transition:margin .1s ease-in-out}[type=checkbox][role=switch]:focus{--pico-background-color:var(--pico-switch-background-color);--pico-border-color:var(--pico-switch-background-color)}[type=checkbox][role=switch]:checked{--pico-background-color:var(--pico-switch-checked-background-color);--pico-border-color:var(--pico-switch-checked-background-color);background-image:none}[type=checkbox][role=switch]:checked::before{margin-inline-start:calc(2.25em - 1.25em)}[type=checkbox][role=switch][disabled]{--pico-background-color:var(--pico-border-color)}[type=checkbox][aria-invalid=false]:checked,[type=checkbox][aria-invalid=false]:checked:active,[type=checkbox][aria-invalid=false]:checked:focus,[type=checkbox][role=switch][aria-invalid=false]:checked,[type=checkbox][role=switch][aria-invalid=false]:checked:active,[type=checkbox][role=switch][aria-invalid=false]:checked:focus{--pico-background-color:var(--pico-form-element-valid-border-color)}[type=checkbox]:checked:active[aria-invalid=true],[type=checkbox]:checked:focus[aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=checkbox][role=switch]:checked:active[aria-invalid=true],[type=checkbox][role=switch]:checked:focus[aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true]{--pico-background-color:var(--pico-form-element-invalid-border-color)}[type=checkbox][aria-invalid=false]:checked,[type=checkbox][aria-invalid=false]:checked:active,[type=checkbox][aria-invalid=false]:checked:focus,[type=checkbox][role=switch][aria-invalid=false]:checked,[type=checkbox][role=switch][aria-invalid=false]:checked:active,[type=checkbox][role=switch][aria-invalid=false]:checked:focus,[type=radio][aria-invalid=false]:checked,[type=radio][aria-invalid=false]:checked:active,[type=radio][aria-invalid=false]:checked:focus{--pico-border-color:var(--pico-form-element-valid-border-color)}[type=checkbox]:checked:active[aria-invalid=true],[type=checkbox]:checked:focus[aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=checkbox][role=switch]:checked:active[aria-invalid=true],[type=checkbox][role=switch]:checked:focus[aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true],[type=radio]:checked:active[aria-invalid=true],[type=radio]:checked:focus[aria-invalid=true],[type=radio]:checked[aria-invalid=true]{--pico-border-color:var(--pico-form-element-invalid-border-color)}[type=color]::-webkit-color-swatch-wrapper{padding:0}[type=color]::-moz-focus-inner{padding:0}[type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--pico-border-radius) * .5)}[type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--pico-border-radius) * .5)}input:not([type=checkbox],[type=radio],[type=range],[type=file]):is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){--pico-icon-position:0.75rem;--pico-icon-width:1rem;padding-right:calc(var(--pico-icon-width) + var(--pico-icon-position));background-image:var(--pico-icon-date);background-position:center right var(--pico-icon-position);background-size:var(--pico-icon-width) auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=time]{background-image:var(--pico-icon-time)}[type=date]::-webkit-calendar-picker-indicator,[type=datetime-local]::-webkit-calendar-picker-indicator,[type=month]::-webkit-calendar-picker-indicator,[type=time]::-webkit-calendar-picker-indicator,[type=week]::-webkit-calendar-picker-indicator{width:var(--pico-icon-width);margin-right:calc(var(--pico-icon-width) * -1);margin-left:var(--pico-icon-position);opacity:0}@-moz-document url-prefix(){[type=date],[type=datetime-local],[type=month],[type=time],[type=week]{padding-right:var(--pico-form-element-spacing-horizontal)!important;background-image:none!important}}[dir=rtl] :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:right}[type=file]{--pico-color:var(--pico-muted-color);margin-left:calc(var(--pico-outline-width) * -1);padding:calc(var(--pico-form-element-spacing-vertical) * .5) 0;padding-left:var(--pico-outline-width);border:0;border-radius:0;background:0 0}[type=file]::file-selector-button{margin-right:calc(var(--pico-spacing)/ 2);padding:calc(var(--pico-form-element-spacing-vertical) * .5) var(--pico-form-element-spacing-horizontal)}[type=file]:is(:hover,:active,:focus)::file-selector-button{--pico-background-color:var(--pico-secondary-hover-background);--pico-border-color:var(--pico-secondary-hover-border)}[type=file]:focus::file-selector-button{--pico-box-shadow:var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--pico-outline-width) var(--pico-secondary-focus)}[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:0 0}[type=range]::-webkit-slider-runnable-track{width:100%;height:.375rem;border-radius:var(--pico-border-radius);background-color:var(--pico-range-border-color);-webkit-transition:background-color var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),box-shadow var(--pico-transition)}[type=range]::-moz-range-track{width:100%;height:.375rem;border-radius:var(--pico-border-radius);background-color:var(--pico-range-border-color);-moz-transition:background-color var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),box-shadow var(--pico-transition)}[type=range]::-ms-track{width:100%;height:.375rem;border-radius:var(--pico-border-radius);background-color:var(--pico-range-border-color);-ms-transition:background-color var(--pico-transition),box-shadow var(--pico-transition);transition:background-color var(--pico-transition),box-shadow var(--pico-transition)}[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pico-range-thumb-border-color);border-radius:50%;background-color:var(--pico-range-thumb-color);cursor:pointer;-webkit-transition:background-color var(--pico-transition),transform var(--pico-transition);transition:background-color var(--pico-transition),transform var(--pico-transition)}[type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pico-range-thumb-border-color);border-radius:50%;background-color:var(--pico-range-thumb-color);cursor:pointer;-moz-transition:background-color var(--pico-transition),transform var(--pico-transition);transition:background-color var(--pico-transition),transform var(--pico-transition)}[type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pico-range-thumb-border-color);border-radius:50%;background-color:var(--pico-range-thumb-color);cursor:pointer;-ms-transition:background-color var(--pico-transition),transform var(--pico-transition);transition:background-color var(--pico-transition),transform var(--pico-transition)}[type=range]:active,[type=range]:focus-within{--pico-range-border-color:var(--pico-range-active-border-color);--pico-range-thumb-color:var(--pico-range-thumb-active-color)}[type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}[type=range]:active::-moz-range-thumb{transform:scale(1.25)}[type=range]:active::-ms-thumb{transform:scale(1.25)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{padding-inline-start:calc(var(--pico-form-element-spacing-horizontal) + 1.75rem);background-image:var(--pico-icon-search);background-position:center left calc(var(--pico-form-element-spacing-horizontal) + .125rem);background-size:1rem auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{padding-inline-start:calc(var(--pico-form-element-spacing-horizontal) + 1.75rem)!important;background-position:center left 1.125rem,center right .75rem}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=false]{background-image:var(--pico-icon-search),var(--pico-icon-valid)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=true]{background-image:var(--pico-icon-search),var(--pico-icon-invalid)}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{background-position:center right 1.125rem}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}details{display:block;margin-bottom:var(--pico-spacing)}details summary{line-height:1rem;list-style-type:none;cursor:pointer;transition:color var(--pico-transition)}details summary:not([role]){color:var(--pico-accordion-close-summary-color)}details summary::-webkit-details-marker{display:none}details summary::marker{display:none}details summary::-moz-list-bullet{list-style-type:none}details summary::after{display:block;width:1rem;height:1rem;margin-inline-start:calc(var(--pico-spacing,1rem) * .5);float:right;transform:rotate(-90deg);background-image:var(--pico-icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:"";transition:transform var(--pico-transition)}details summary:focus{outline:0}details summary:focus:not([role]){color:var(--pico-accordion-active-summary-color)}details summary:focus-visible:not([role]){outline:var(--pico-outline-width) solid var(--pico-primary-focus);outline-offset:calc(var(--pico-spacing,1rem) * 0.5);color:var(--pico-primary)}details summary[role=button]{width:100%;text-align:left}details summary[role=button]::after{height:calc(1rem * var(--pico-line-height,1.5))}details[open]>summary{margin-bottom:var(--pico-spacing)}details[open]>summary:not([role]):not(:focus){color:var(--pico-accordion-open-summary-color)}details[open]>summary::after{transform:rotate(0)}[dir=rtl] details summary{text-align:right}[dir=rtl] details summary::after{float:left;background-position:left center}article{margin-bottom:var(--pico-block-spacing-vertical);padding:var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal);border-radius:var(--pico-border-radius);background:var(--pico-card-background-color);box-shadow:var(--pico-card-box-shadow)}article>footer,article>header{margin-right:calc(var(--pico-block-spacing-horizontal) * -1);margin-left:calc(var(--pico-block-spacing-horizontal) * -1);padding:calc(var(--pico-block-spacing-vertical) * .66) var(--pico-block-spacing-horizontal);background-color:var(--pico-card-sectioning-background-color)}article>header{margin-top:calc(var(--pico-block-spacing-vertical) * -1);margin-bottom:var(--pico-block-spacing-vertical);border-bottom:var(--pico-border-width) solid var(--pico-card-border-color);border-top-right-radius:var(--pico-border-radius);border-top-left-radius:var(--pico-border-radius)}article>footer{margin-top:var(--pico-block-spacing-vertical);margin-bottom:calc(var(--pico-block-spacing-vertical) * -1);border-top:var(--pico-border-width) solid var(--pico-card-border-color);border-bottom-right-radius:var(--pico-border-radius);border-bottom-left-radius:var(--pico-border-radius)}details.dropdown{position:relative;border-bottom:none}details.dropdown>a::after,details.dropdown>button::after,details.dropdown>summary::after{display:block;width:1rem;height:calc(1rem * var(--pico-line-height,1.5));margin-inline-start:.25rem;float:right;transform:rotate(0) translateX(.2rem);background-image:var(--pico-icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:""}nav details.dropdown{margin-bottom:0}details.dropdown>summary:not([role]){height:calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2);padding:var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);border:var(--pico-border-width) solid var(--pico-form-element-border-color);border-radius:var(--pico-border-radius);background-color:var(--pico-form-element-background-color);color:var(--pico-form-element-placeholder-color);line-height:inherit;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;transition:background-color var(--pico-transition),border-color var(--pico-transition),color var(--pico-transition),box-shadow var(--pico-transition)}details.dropdown>summary:not([role]):active,details.dropdown>summary:not([role]):focus{border-color:var(--pico-form-element-active-border-color);background-color:var(--pico-form-element-active-background-color)}details.dropdown>summary:not([role]):focus{box-shadow:0 0 0 var(--pico-outline-width) var(--pico-form-element-focus-color)}details.dropdown>summary:not([role]):focus-visible{outline:0}details.dropdown>summary:not([role])[aria-invalid=false]{--pico-form-element-border-color:var(--pico-form-element-valid-border-color);--pico-form-element-active-border-color:var(--pico-form-element-valid-focus-color);--pico-form-element-focus-color:var(--pico-form-element-valid-focus-color)}details.dropdown>summary:not([role])[aria-invalid=true]{--pico-form-element-border-color:var(--pico-form-element-invalid-border-color);--pico-form-element-active-border-color:var(--pico-form-element-invalid-focus-color);--pico-form-element-focus-color:var(--pico-form-element-invalid-focus-color)}nav details.dropdown{display:inline;margin:calc(var(--pico-nav-element-spacing-vertical) * -1) 0}nav details.dropdown>summary::after{transform:rotate(0) translateX(0)}nav details.dropdown>summary:not([role]){height:calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 2);padding:calc(var(--pico-nav-link-spacing-vertical) - var(--pico-border-width) * 2) var(--pico-nav-link-spacing-horizontal)}nav details.dropdown>summary:not([role]):focus-visible{box-shadow:0 0 0 var(--pico-outline-width) var(--pico-primary-focus)}details.dropdown>summary+ul{display:flex;z-index:99;position:absolute;left:0;flex-direction:column;width:100%;min-width:-moz-fit-content;min-width:fit-content;margin:0;margin-top:var(--pico-outline-width);padding:0;border:var(--pico-border-width) solid var(--pico-dropdown-border-color);border-radius:var(--pico-border-radius);background-color:var(--pico-dropdown-background-color);box-shadow:var(--pico-dropdown-box-shadow);color:var(--pico-dropdown-color);white-space:nowrap;opacity:0;transition:opacity var(--pico-transition),transform 0s ease-in-out 1s}details.dropdown>summary+ul[dir=rtl]{right:0;left:auto}details.dropdown>summary+ul li{width:100%;margin-bottom:0;padding:calc(var(--pico-form-element-spacing-vertical) * .5) var(--pico-form-element-spacing-horizontal);list-style:none}details.dropdown>summary+ul li:first-of-type{margin-top:calc(var(--pico-form-element-spacing-vertical) * .5)}details.dropdown>summary+ul li:last-of-type{margin-bottom:calc(var(--pico-form-element-spacing-vertical) * .5)}details.dropdown>summary+ul li a{display:block;margin:calc(var(--pico-form-element-spacing-vertical) * -.5) calc(var(--pico-form-element-spacing-horizontal) * -1);padding:calc(var(--pico-form-element-spacing-vertical) * .5) var(--pico-form-element-spacing-horizontal);overflow:hidden;border-radius:0;color:var(--pico-dropdown-color);text-decoration:none;text-overflow:ellipsis}details.dropdown>summary+ul li a:active,details.dropdown>summary+ul li a:focus,details.dropdown>summary+ul li a:focus-visible,details.dropdown>summary+ul li a:hover,details.dropdown>summary+ul li a[aria-current]:not([aria-current=false]){background-color:var(--pico-dropdown-hover-background-color)}details.dropdown>summary+ul li label{width:100%}details.dropdown>summary+ul li:has(label):hover{background-color:var(--pico-dropdown-hover-background-color)}details.dropdown[open]>summary{margin-bottom:0}details.dropdown[open]>summary+ul{transform:scaleY(1);opacity:1;transition:opacity var(--pico-transition),transform 0s ease-in-out 0s}details.dropdown[open]>summary::before{display:block;z-index:1;position:fixed;width:100vw;height:100vh;inset:0;background:0 0;content:"";cursor:default}label>details.dropdown{margin-top:calc(var(--pico-spacing) * .25)}[role=group],[role=search]{display:inline-flex;position:relative;width:100%;margin-bottom:var(--pico-spacing);border-radius:var(--pico-border-radius);box-shadow:var(--pico-group-box-shadow,0 0 0 transparent);vertical-align:middle;transition:box-shadow var(--pico-transition)}[role=group] input:not([type=checkbox],[type=radio]),[role=group] select,[role=group]>*,[role=search] input:not([type=checkbox],[type=radio]),[role=search] select,[role=search]>*{position:relative;flex:1 1 auto;margin-bottom:0}[role=group] input:not([type=checkbox],[type=radio]):not(:first-child),[role=group] select:not(:first-child),[role=group]>:not(:first-child),[role=search] input:not([type=checkbox],[type=radio]):not(:first-child),[role=search] select:not(:first-child),[role=search]>:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}[role=group] input:not([type=checkbox],[type=radio]):not(:last-child),[role=group] select:not(:last-child),[role=group]>:not(:last-child),[role=search] input:not([type=checkbox],[type=radio]):not(:last-child),[role=search] select:not(:last-child),[role=search]>:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}[role=group] input:not([type=checkbox],[type=radio]):focus,[role=group] select:focus,[role=group]>:focus,[role=search] input:not([type=checkbox],[type=radio]):focus,[role=search] select:focus,[role=search]>:focus{z-index:2}[role=group] [role=button]:not(:first-child),[role=group] [type=button]:not(:first-child),[role=group] [type=reset]:not(:first-child),[role=group] [type=submit]:not(:first-child),[role=group] button:not(:first-child),[role=group] input:not([type=checkbox],[type=radio]):not(:first-child),[role=group] select:not(:first-child),[role=search] [role=button]:not(:first-child),[role=search] [type=button]:not(:first-child),[role=search] [type=reset]:not(:first-child),[role=search] [type=submit]:not(:first-child),[role=search] button:not(:first-child),[role=search] input:not([type=checkbox],[type=radio]):not(:first-child),[role=search] select:not(:first-child){margin-left:calc(var(--pico-border-width) * -1)}[role=group] [role=button],[role=group] [type=button],[role=group] [type=reset],[role=group] [type=submit],[role=group] button,[role=search] [role=button],[role=search] [type=button],[role=search] [type=reset],[role=search] [type=submit],[role=search] button{width:auto}@supports selector(:has(*)){[role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus),[role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus){--pico-group-box-shadow:var(--pico-group-box-shadow-focus-with-button)}[role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) input:not([type=checkbox],[type=radio]),[role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) select,[role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) input:not([type=checkbox],[type=radio]),[role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) select{border-color:transparent}[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus),[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus){--pico-group-box-shadow:var(--pico-group-box-shadow-focus-with-input)}[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button],[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],[role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) button,[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button],[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],[role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) button{--pico-button-box-shadow:0 0 0 var(--pico-border-width) var(--pico-primary-border);--pico-button-hover-box-shadow:0 0 0 var(--pico-border-width) var(--pico-primary-hover-border)}[role=group] [role=button]:focus,[role=group] [type=button]:focus,[role=group] [type=reset]:focus,[role=group] [type=submit]:focus,[role=group] button:focus,[role=search] [role=button]:focus,[role=search] [type=button]:focus,[role=search] [type=reset]:focus,[role=search] [type=submit]:focus,[role=search] button:focus{box-shadow:none}}[role=search]>:first-child{border-top-left-radius:5rem;border-bottom-left-radius:5rem}[role=search]>:last-child{border-top-right-radius:5rem;border-bottom-right-radius:5rem}[aria-busy=true]:not(input,select,textarea,html,form){white-space:nowrap}[aria-busy=true]:not(input,select,textarea,html,form)::before{display:inline-block;width:1em;height:1em;background-image:var(--pico-icon-loading);background-size:1em auto;background-repeat:no-repeat;content:"";vertical-align:-.125em}[aria-busy=true]:not(input,select,textarea,html,form):not(:empty)::before{margin-inline-end:calc(var(--pico-spacing) * .5)}[aria-busy=true]:not(input,select,textarea,html,form):empty{text-align:center}[role=button][aria-busy=true],[type=button][aria-busy=true],[type=reset][aria-busy=true],[type=submit][aria-busy=true],a[aria-busy=true],button[aria-busy=true]{pointer-events:none}:host,:root{--pico-scrollbar-width:0px}dialog{display:flex;z-index:999;position:fixed;top:0;right:0;bottom:0;left:0;align-items:center;justify-content:center;width:inherit;min-width:100%;height:inherit;min-height:100%;padding:0;border:0;-webkit-backdrop-filter:var(--pico-modal-overlay-backdrop-filter);backdrop-filter:var(--pico-modal-overlay-backdrop-filter);background-color:var(--pico-modal-overlay-background-color);color:var(--pico-color)}dialog>article{width:100%;max-height:calc(100vh - var(--pico-spacing) * 2);margin:var(--pico-spacing);overflow:auto}@media (min-width:576px){dialog>article{max-width:510px}}@media (min-width:768px){dialog>article{max-width:700px}}dialog>article>header>*{margin-bottom:0}dialog>article>header .close,dialog>article>header :is(a,button)[rel=prev]{margin:0;margin-left:var(--pico-spacing);padding:0;float:right}dialog>article>footer{text-align:right}dialog>article>footer [role=button],dialog>article>footer button{margin-bottom:0}dialog>article>footer [role=button]:not(:first-of-type),dialog>article>footer button:not(:first-of-type){margin-left:calc(var(--pico-spacing) * .5)}dialog>article .close,dialog>article :is(a,button)[rel=prev]{display:block;width:1rem;height:1rem;margin-top:calc(var(--pico-spacing) * -1);margin-bottom:var(--pico-spacing);margin-left:auto;border:none;background-image:var(--pico-icon-close);background-position:center;background-size:auto 1rem;background-repeat:no-repeat;background-color:transparent;opacity:.5;transition:opacity var(--pico-transition)}dialog>article .close:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),dialog>article :is(a,button)[rel=prev]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){opacity:1}dialog:not([open]),dialog[open=false]{display:none}.modal-is-open{padding-right:var(--pico-scrollbar-width,0);overflow:hidden;pointer-events:none;touch-action:none}.modal-is-open dialog{pointer-events:auto;touch-action:auto}:where(.modal-is-opening,.modal-is-closing) dialog,:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-duration:.2s;animation-timing-function:ease-in-out;animation-fill-mode:both}:where(.modal-is-opening,.modal-is-closing) dialog{animation-duration:.8s;animation-name:modal-overlay}:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-delay:.2s;animation-name:modal}.modal-is-closing dialog,.modal-is-closing dialog>article{animation-delay:0s;animation-direction:reverse}@keyframes modal-overlay{from{-webkit-backdrop-filter:none;backdrop-filter:none;background-color:transparent}}@keyframes modal{from{transform:translateY(-100%);opacity:0}}:where(nav li)::before{float:left;content:"​"}nav,nav ul{display:flex}nav{justify-content:space-between;overflow:visible}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--pico-nav-element-spacing-horizontal) * -1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--pico-nav-element-spacing-horizontal) * -1)}nav li{display:inline-block;margin:0;padding:var(--pico-nav-element-spacing-vertical) var(--pico-nav-element-spacing-horizontal)}nav li :where(a,[role=link]){display:inline-block;margin:calc(var(--pico-nav-link-spacing-vertical) * -1) calc(var(--pico-nav-link-spacing-horizontal) * -1);padding:var(--pico-nav-link-spacing-vertical) var(--pico-nav-link-spacing-horizontal);border-radius:var(--pico-border-radius)}nav li :where(a,[role=link]):not(:hover){text-decoration:none}nav li [role=button],nav li [type=button],nav li button,nav li input:not([type=checkbox],[type=radio],[type=range],[type=file]),nav li select{height:auto;margin-right:inherit;margin-bottom:0;margin-left:inherit;padding:calc(var(--pico-nav-link-spacing-vertical) - var(--pico-border-width) * 2) var(--pico-nav-link-spacing-horizontal)}nav[aria-label=breadcrumb]{align-items:center;justify-content:start}nav[aria-label=breadcrumb] ul li:not(:first-child){margin-inline-start:var(--pico-nav-link-spacing-horizontal)}nav[aria-label=breadcrumb] ul li a{margin:calc(var(--pico-nav-link-spacing-vertical) * -1) 0;margin-inline-start:calc(var(--pico-nav-link-spacing-horizontal) * -1)}nav[aria-label=breadcrumb] ul li:not(:last-child)::after{display:inline-block;position:absolute;width:calc(var(--pico-nav-link-spacing-horizontal) * 4);margin:0 calc(var(--pico-nav-link-spacing-horizontal) * -1);content:var(--pico-nav-breadcrumb-divider);color:var(--pico-muted-color);text-align:center;text-decoration:none;white-space:nowrap}nav[aria-label=breadcrumb] a[aria-current]:not([aria-current=false]){background-color:transparent;color:inherit;text-decoration:none;pointer-events:none}aside li,aside nav,aside ol,aside ul{display:block}aside li{padding:calc(var(--pico-nav-element-spacing-vertical) * .5) var(--pico-nav-element-spacing-horizontal)}aside li a{display:block}aside li [role=button]{margin:inherit}[dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{content:"\\"}progress{display:inline-block;vertical-align:baseline}progress{-webkit-appearance:none;-moz-appearance:none;display:inline-block;appearance:none;width:100%;height:.5rem;margin-bottom:calc(var(--pico-spacing) * .5);overflow:hidden;border:0;border-radius:var(--pico-border-radius);background-color:var(--pico-progress-background-color);color:var(--pico-progress-color)}progress::-webkit-progress-bar{border-radius:var(--pico-border-radius);background:0 0}progress[value]::-webkit-progress-value{background-color:var(--pico-progress-color);-webkit-transition:inline-size var(--pico-transition);transition:inline-size var(--pico-transition)}progress::-moz-progress-bar{background-color:var(--pico-progress-color)}@media (prefers-reduced-motion:no-preference){progress:indeterminate{background:var(--pico-progress-background-color) linear-gradient(to right,var(--pico-progress-color) 30%,var(--pico-progress-background-color) 30%) top left/150% 150% no-repeat;animation:progress-indeterminate 1s linear infinite}progress:indeterminate[value]::-webkit-progress-value{background-color:transparent}progress:indeterminate::-moz-progress-bar{background-color:transparent}}@media (prefers-reduced-motion:no-preference){[dir=rtl] progress:indeterminate{animation-direction:reverse}}@keyframes progress-indeterminate{0%{background-position:200% 0}100%{background-position:-200% 0}}[data-tooltip]{position:relative}[data-tooltip]:not(a,button,input,[role=button]){border-bottom:1px dotted;text-decoration:none;cursor:help}[data-tooltip]::after,[data-tooltip]::before,[data-tooltip][data-placement=top]::after,[data-tooltip][data-placement=top]::before{display:block;z-index:99;position:absolute;bottom:100%;left:50%;padding:.25rem .5rem;overflow:hidden;transform:translate(-50%,-.25rem);border-radius:var(--pico-border-radius);background:var(--pico-tooltip-background-color);content:attr(data-tooltip);color:var(--pico-tooltip-color);font-style:normal;font-weight:var(--pico-font-weight);font-size:.875rem;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;opacity:0;pointer-events:none}[data-tooltip]::after,[data-tooltip][data-placement=top]::after{padding:0;transform:translate(-50%,0);border-top:.3rem solid;border-right:.3rem solid transparent;border-left:.3rem solid transparent;border-radius:0;background-color:transparent;content:"";color:var(--pico-tooltip-background-color)}[data-tooltip][data-placement=bottom]::after,[data-tooltip][data-placement=bottom]::before{top:100%;bottom:auto;transform:translate(-50%,.25rem)}[data-tooltip][data-placement=bottom]:after{transform:translate(-50%,-.3rem);border:.3rem solid transparent;border-bottom:.3rem solid}[data-tooltip][data-placement=left]::after,[data-tooltip][data-placement=left]::before{top:50%;right:100%;bottom:auto;left:auto;transform:translate(-.25rem,-50%)}[data-tooltip][data-placement=left]:after{transform:translate(.3rem,-50%);border:.3rem solid transparent;border-left:.3rem solid}[data-tooltip][data-placement=right]::after,[data-tooltip][data-placement=right]::before{top:50%;right:auto;bottom:auto;left:100%;transform:translate(.25rem,-50%)}[data-tooltip][data-placement=right]:after{transform:translate(-.3rem,-50%);border:.3rem solid transparent;border-right:.3rem solid}[data-tooltip]:focus::after,[data-tooltip]:focus::before,[data-tooltip]:hover::after,[data-tooltip]:hover::before{opacity:1}@media (hover:hover) and (pointer:fine){[data-tooltip]:focus::after,[data-tooltip]:focus::before,[data-tooltip]:hover::after,[data-tooltip]:hover::before{--pico-tooltip-slide-to:translate(-50%, -0.25rem);transform:translate(-50%,.75rem);animation-duration:.2s;animation-fill-mode:forwards;animation-name:tooltip-slide;opacity:0}[data-tooltip]:focus::after,[data-tooltip]:hover::after{--pico-tooltip-caret-slide-to:translate(-50%, 0rem);transform:translate(-50%,-.25rem);animation-name:tooltip-caret-slide}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:focus::before,[data-tooltip][data-placement=bottom]:hover::after,[data-tooltip][data-placement=bottom]:hover::before{--pico-tooltip-slide-to:translate(-50%, 0.25rem);transform:translate(-50%,-.75rem);animation-name:tooltip-slide}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover::after{--pico-tooltip-caret-slide-to:translate(-50%, -0.3rem);transform:translate(-50%,-.5rem);animation-name:tooltip-caret-slide}[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:focus::before,[data-tooltip][data-placement=left]:hover::after,[data-tooltip][data-placement=left]:hover::before{--pico-tooltip-slide-to:translate(-0.25rem, -50%);transform:translate(.75rem,-50%);animation-name:tooltip-slide}[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:hover::after{--pico-tooltip-caret-slide-to:translate(0.3rem, -50%);transform:translate(.05rem,-50%);animation-name:tooltip-caret-slide}[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:focus::before,[data-tooltip][data-placement=right]:hover::after,[data-tooltip][data-placement=right]:hover::before{--pico-tooltip-slide-to:translate(0.25rem, -50%);transform:translate(-.75rem,-50%);animation-name:tooltip-slide}[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:hover::after{--pico-tooltip-caret-slide-to:translate(-0.3rem, -50%);transform:translate(-.05rem,-50%);animation-name:tooltip-caret-slide}}@keyframes tooltip-slide{to{transform:var(--pico-tooltip-slide-to);opacity:1}}@keyframes tooltip-caret-slide{50%{opacity:0}to{transform:var(--pico-tooltip-caret-slide-to);opacity:1}}[aria-controls]{cursor:pointer}[aria-disabled=true],[disabled]{cursor:not-allowed}[aria-hidden=false][hidden]{display:initial}[aria-hidden=false][hidden]:not(:focus){clip:rect(0,0,0,0);position:absolute}[tabindex],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation}[dir=rtl]{direction:rtl}@media (prefers-reduced-motion:reduce){:not([aria-busy=true]),:not([aria-busy=true])::after,:not([aria-busy=true])::before{background-attachment:initial!important;animation-duration:1ms!important;animation-delay:-1ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-delay:0s!important;transition-duration:0s!important}}/* Fermi Obra — Custom Styles (on top of Pico CSS) */

/* --- Fonts --- */

@font-face {
  font-family: 'Clash Display';
  src: url('/fonts/ClashDisplay-Regular.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Clash Display';
  src: url('/fonts/ClashDisplay-Medium.otf') format('opentype');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Clash Display';
  src: url('/fonts/ClashDisplay-Semibold.otf') format('opentype');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Clash Display';
  src: url('/fonts/ClashDisplay-Bold.otf') format('opentype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* --- Variables --- */

:root {
  /* Brand colors */
  --ol-primary: #030531;
  --ol-primary-hover: #0a0d4a;
  --ol-accent: #21F2C0;
  --ol-accent-hover: #1ad9ab;
  /* a11y (css-a11y-pass): text-safe dark counterpart of the turquoise accent.
     #21F2C0 on white is ~1.45:1 — fine for decorations (underlines, borders,
     dots, rings on the navy sidebar) but ILLEGIBLE as text (WCAG 1.4.3 needs
     ≥4.5:1). Use --ol-accent-ink for accent-coloured TEXT on light surfaces:
     #0b7a63 is the same brand teal hue at 5.28:1 on #fff / 5.05:1 on gray-50.
     Keep --ol-accent itself for non-text accents. */
  --ol-accent-ink: #0b7a63;
  --ol-accent-ink-hover: #095f4e;
  --ol-green: #16a34a;
  --ol-green-bg: #f0fdf4;
  --ol-green-50: #f0fdf4;
  --ol-green-100: #dcfce7;
  --ol-green-border: #bbf7d0;
  --ol-green-text: #1a7f37;
  --ol-yellow: #ca8a04;
  --ol-yellow-bg: #fefce8;
  --ol-yellow-border: #fef08a;
  --ol-yellow-text: #9a6700;
  --ol-red: #dc2626;
  --ol-red-bg: #fef2f2;
  --ol-red-border: #fecaca;
  --ol-blue: #2563eb;
  --ol-blue-bg: #eff6ff;
  --ol-blue-border: #bfdbfe;
  --ol-purple: #7c3aed;
  --ol-purple-bg: #f5f3ff;
  --ol-purple-border: #ddd6fe;
  --ol-amber: #d97706;
  --ol-amber-bg: #fffbeb;
  --ol-amber-border: #fde68a;
  --ol-amber-text: #92400e;
  --ol-orange: #ea580c;
  --ol-orange-bg: #fff7ed;
  --ol-orange-border: #fed7aa;
  --ol-orange-text: #9a3412;
  --ol-blue-hover: #1d4ed8;
  --ol-gray-50: #f9fafb;
  --ol-gray-100: #f3f4f6;
  --ol-gray-200: #e5e7eb;
  --ol-gray-300: #d1d5db;
  --ol-gray-400: #9ca3af;
  --ol-gray-500: #6b7280;
  --ol-gray-600: #4b5563;
  --ol-gray-700: #374151;
  --ol-gray-800: #1f2937;
  --ol-gray-900: #111827;
  /* --ol-gray-bg is an alias of --ol-gray-100; existed only as the inline-style
     fallback in ImportTrabalhadores StatCard tone. Define so the alias resolves. */
  --ol-gray-bg: #f3f4f6;

  /* Extended Tailwind-style scale aliases — these were already in use as
     defensive `var(--ol-X, #fallback)` syntax across the app; defining them
     here kills the drift risk (the fallback would silently win otherwise).
     Hex values match the existing inline fallbacks 1:1 for behavior parity. */
  --ol-blue-50: #eff6ff;       /* alias of --ol-blue-bg */
  --ol-blue-100: #dbeafe;
  --ol-blue-700: #1d4ed8;      /* alias of --ol-blue-hover */
  --ol-red-500: #ef4444;
  --ol-red-600: #b91c1c;
  --ol-red-700: #991b1b;
  --ol-orange-600: #c2410c;
  --ol-orange-700: #c2410c;    /* caller used 700, value was 600's; preserve */
  --ol-green-200: #bbf7d0;     /* alias of --ol-green-border */
  --ol-green-500: #22c55e;
  --ol-amber-600: #d97706;     /* alias of --ol-amber */
  --ol-indigo: #4f46e5;
  --ol-indigo-50: #eef2ff;

  /* Sidebar */
  --sidebar-width: 240px;

  /* Spacing scale — use instead of ad-hoc rem values */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;

  /* Elevation scale — brand-tinted (navy) rather than pure black */
  --shadow-xs: 0 1px 2px rgba(3, 5, 49, 0.04);
  --shadow-sm: 0 1px 2px rgba(3, 5, 49, 0.04), 0 1px 3px rgba(3, 5, 49, 0.07);
  --shadow-md: 0 4px 12px rgba(3, 5, 49, 0.09);
  --shadow-lg: 0 16px 40px rgba(3, 5, 49, 0.16);

  /* Pico overrides — buttons, links, focus rings */
  --pico-primary: var(--ol-primary);
  --pico-primary-hover: var(--ol-primary-hover);
  --pico-primary-background: var(--ol-primary);
  --pico-primary-border: var(--ol-primary);
  --pico-primary-background-hover: var(--ol-primary-hover);
  --pico-primary-border-hover: var(--ol-primary-hover);
  --pico-primary-underline: var(--ol-accent);
  --pico-primary-focus: rgba(33, 242, 192, 0.25);
  --pico-primary-inverse: #fff;
  --pico-border-radius: 8px;
  --pico-font-size: 15px;
}

/* Links — Fermi teal accent, in the text-safe dark shade (see --ol-accent-ink:
   the raw turquoise was ~1.45:1 on white, far below the 4.5:1 WCAG minimum). */
a {
  color: var(--ol-accent-ink, #0b7a63);
}

a:hover {
  color: var(--ol-accent-ink-hover, #095f4e);
}

/* Neutral inline link — a de-emphasised text action that reads as a normal
   inline control, not a highlighted accent link. Used for secondary
   navigations like "Abrir na validação →" and the cross-doc group labels, so
   they don't compete visually with primary accent links. Underline-on-hover
   keeps the affordance. */
.link-neutral,
a.link-neutral,
a.link-neutral:hover {
  color: var(--ol-gray-700, #374151);
  text-decoration: none;
}
.link-neutral:hover {
  color: var(--ol-gray-900, #111827);
  text-decoration: underline;
}

/* Entity-name links inside data tables / detail panels (obra, subempreiteiro,
   trabalhador names). These should read as part of the surrounding text rather
   than standing out as turquoise underlined links. Still clickable; an
   underline returns on hover to preserve the affordance. */
.entity-link {
  /* popup-unify follow-up (2026-07-28) — an entity link must LOOK clickable
     without a pointer. Measured before this rule: `.entity-link` computed to
     rgb(55,60,68) — byte-identical to plain body text — with no underline, so
     the only affordance was `cursor: pointer` + a hover underline, i.e. NOTHING
     on touch. That undercut the whole point of making obra/empresa/trabalhador
     names live everywhere: they were functionally clickable and visually dead.
     A persistent DOTTED underline is the minimal honest fix — it marks every
     entity reference as interactive at rest, on any input device, without
     recolouring the hundreds of names that use this class inside tables (a
     full recolour to --ol-accent-ink is the bigger option, deliberately not
     taken; see docs/popup-spec/DECISIONS.md). */
  color: inherit;
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  text-decoration-color: var(--ol-gray-400, #9ca3af);
}

.entity-link:hover,
.entity-link:focus-visible {
  /* Resolve to the brand's text-safe teal on interaction (5.28:1 on white) and
     promote the dotted rule to a solid one, so hover still reads as a state
     CHANGE now that the resting state is already underlined. */
  color: var(--ol-accent-ink, #0b7a63);
  text-decoration: underline solid;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  text-decoration-color: currentColor;
}

/* Focus ring (a11y, css-a11y-pass) — a keyboard user must SEE where focus is
   (WCAG 2.4.7/2.4.13). The old rule only tinted the UA outline turquoise
   (~1.45:1 on white — effectively invisible) behind a 25%-alpha halo. Paint a
   solid 2px navy ring (≥17:1 on the app's light surfaces) with the turquoise
   halo kept as a secondary brand glow. Component-level :focus-visible rules
   (docks, badges, pills…) out-specify this universal rule and still win. */
*:focus-visible {
  outline: 2px solid var(--ol-primary, #030531);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(33, 242, 192, 0.35);
}

/* Text-like form controls take the Combobox trigger's focus treatment instead of
   the universal ring above. A filter row sets an <input> directly beside
   .ol-combobox__trigger, and the universal 2px offset + 5px/35% halo made the
   pair glow two different ways — the input read as a much heavier contour.
   Single source of truth: keep these four declarations identical to
   .ol-combobox__trigger:focus-visible.

   The ring is the turquoise accent, NOT the navy of `*:focus-visible`: a field's
   focused contour must read as one accent colour. This is a deliberate product
   decision that trades away the WCAG 2.4.11 focus-indicator contrast floor —
   --ol-accent is 1.45:1 on white and 1.02:1 against the resting --ol-gray-300
   border, both under the 3:1 minimum. Do NOT "fix" it back to navy; if the
   contrast has to return, use --ol-accent-ink (#0b7a63, 5.28:1 on white), which
   keeps the accent hue. Buttons, links and the bespoke controls
   (checkbox/radio/range/file/color, e.g. the .switch toggle) are excluded here
   and keep the compliant navy ring.

   `:where()` holds the negation at zero specificity, so the whole selector is
   0,1,1: enough to beat `*:focus-visible` (0,1,0), but low enough that
   component-level class rules still out-specify it — including
   `.sidebar :focus-visible` (0,2,0), which keeps its turquoise ring on the navy
   sidebar where a navy ring would vanish. */
input:where(:not([type="submit"], [type="button"], [type="reset"], [type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"])):focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--ol-accent, #21f2c0);
  outline-offset: 1px;
  border-color: var(--ol-accent);
  box-shadow: 0 0 0 3px rgba(33, 242, 192, 0.25);
}

/* On the navy sidebar the navy ring would vanish — use the turquoise accent
   there (≈9:1 against #030531). */
.sidebar :focus-visible {
  outline-color: var(--ol-accent, #21f2c0);
  box-shadow: none;
}

/* Input/select focus */
input:focus, select:focus, textarea:focus {
  border-color: var(--ol-accent);
  --pico-border-color: var(--ol-accent);
}

/* An INVALID field keeps its error colour while focused.
   Without this, the two rules above win on order and repaint a failing field in
   the turquoise accent the moment the operator clicks into it — the same ring a
   VALID focused field gets. Measured on the «Criar novo» NISS field: the red
   aria-invalid border rgb(184,106,107) became rgb(41,234,187), indistinguishable
   from the valid-field ring rgb(33,242,192). So the field reads as "success"
   at exactly the moment the user is typing the value that is wrong.

   App-wide on purpose: `aria-invalid` is set on the login email/password, the
   set-password pair, the subcontractor NIF field and the NISS field, and every
   one of them had this. Specificity 0,2,0 (attribute + element) beats the
   0,1,1 focus-visible rule and the 0,1,0 :focus rule above, so it wins in both
   the keyboard (:focus-visible) and pointer (:focus) cases without !important. */
input[aria-invalid="true"]:focus,
select[aria-invalid="true"]:focus,
textarea[aria-invalid="true"]:focus,
input[aria-invalid="true"]:focus-visible,
select[aria-invalid="true"]:focus-visible,
textarea[aria-invalid="true"]:focus-visible {
  outline: 2px solid var(--ol-red-600, #c62828);
  outline-offset: 1px;
  border-color: var(--ol-red-600, #c62828);
  --pico-border-color: var(--ol-red-600, #c62828);
  box-shadow: 0 0 0 3px rgba(198, 40, 40, 0.22);
}

/* Primary button — navy bg. `.btn-link` / `.day-toggle` opt out (like .outline /
   .secondary) so tertiary/disclosure/toggle controls don't read as loud primaries. */
[type="submit"], button:not(.outline):not(.secondary):not(.upload-toggle):not(.tab-bar *):not(.tabs__tab):not(.settings-tab):not(.btn-link):not(.day-toggle) {
  --pico-background-color: var(--ol-primary);
  --pico-border-color: var(--ol-primary);
  --pico-color: #fff;
}

[type="submit"]:hover, button:not(.outline):not(.secondary):not(.upload-toggle):not(.tab-bar *):not(.tabs__tab):not(.settings-tab):not(.btn-link):not(.day-toggle):hover {
  --pico-background-color: var(--ol-primary-hover);
  --pico-border-color: var(--ol-primary-hover);
}

/* --- Global --- */

/* Always reserve the vertical scrollbar gutter. Expanding a dashboard row (or
   any content growth that crosses the no-scrollbar → scrollbar threshold) would
   otherwise make a classic scrollbar appear, narrow the viewport, and reflow
   the whole page — so the table text visibly "jumped" sideways on click. With
   the gutter reserved the layout width is constant whether or not a scrollbar
   is showing. (No-op on platforms with overlay scrollbars.) */
html {
  scrollbar-gutter: stable;
}

body {
  background-color: var(--ol-gray-50);
  color: var(--ol-gray-800);
}

h1, h2, h3, h4 {
  font-family: 'Clash Display', system-ui, sans-serif;
  color: var(--ol-gray-900);
  /* uipolish7 D-12 (GLOBAL): Clash Display's tight default word spacing glued
     multi-word headings ("Utilizadores (3)" read as one token). A hair of
     word-spacing separates words without touching letter tracking. Applies to
     every h1–h4. */
  word-spacing: 0.08em;
}

h1 {
  font-size: 1.5rem;
  font-weight: 700;
  margin-bottom: 1.25rem;
}

h2 {
  font-size: 1.5rem;
  font-weight: 700;
  margin-bottom: 1.25rem;
}

h3 {
  font-size: 1.15rem;
  font-weight: 600;
}

/* registo-t2 — in-panel SECTION titles (worker / equipamento / empresa detail
   pop-ups). The global `h1..h4` rule above puts every heading in the decorative
   'Clash Display' font; inside the detail panels the section titles must instead
   read in the NORMAL body font, weight 600, matching the "Identidade" span
   (WorkerDetailsPanel). This class is scoped to those titles only — it does NOT
   touch the global h1..h4 rule, so the rest of the app's headings are unchanged.
   `word-spacing: normal` cancels the Clash-Display word-spacing the global rule
   adds. Sizes mirror the Identidade span: 1rem on the full page, 0.85rem in the
   embedded (pop-up / validador) context via the `--embedded` modifier. */
.detail-panel-section-title {
  font-family: var(--pico-font-family, system-ui, sans-serif);
  font-weight: 600;
  font-size: 1rem;
  color: var(--ol-gray-900);
  word-spacing: normal;
}
.detail-panel-section-title--embedded {
  font-size: 0.85rem;
}

/* --- Sidebar Layout --- */

.app-layout {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  min-height: 100vh;
}

.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  width: var(--sidebar-width);
  height: 100vh;
  background-color: var(--ol-primary);
  display: flex;
  flex-direction: column;
  z-index: 100;
  overflow-y: auto;
}

.sidebar-logo {
  font-family: 'Clash Display', system-ui, sans-serif;
  font-size: 1.5rem;
  font-weight: 700;
  color: #fff;
  padding: 1.5rem 1.25rem 2rem;
  letter-spacing: -0.02em;
}

.sidebar-logo span {
  color: var(--ol-accent);
}

.sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0 0.75rem;
}

.sidebar-nav a {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.65rem 0.75rem;
  border-radius: 8px;
  color: rgba(255, 255, 255, 0.6);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 500;
  transition: all 0.15s;
  border-left: 3px solid transparent;
  line-height: 1.3;
}

.sidebar-nav a:hover {
  color: #fff;
  background-color: rgba(255, 255, 255, 0.08);
}

.sidebar-nav a.active {
  color: #fff;
  background-color: rgba(255, 255, 255, 0.1);
  border-left-color: var(--ol-accent);
}

.sidebar-nav a svg {
  flex-shrink: 0;
}

.nav-badge {
  margin-left: auto;
  background-color: var(--ol-red);
  color: #fff;
  border-radius: 99px;
  padding: 1px 7px;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.4;
}

.sidebar-footer {
  margin-top: auto;
  padding: 0.75rem;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.sidebar-user {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.sidebar-user-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.sidebar-user-email {
  color: rgba(255, 255, 255, 0.8);
  font-size: 0.75rem;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-user-role {
  color: rgba(255, 255, 255, 0.45);
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.sidebar-logout-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.5);
  cursor: pointer;
  padding: 0.4rem;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.15s;
}

.sidebar-logout-btn:hover {
  color: #fff;
  background-color: rgba(255, 255, 255, 0.1);
}

.main-content {
  grid-column: 2;
  padding: 2rem 2.5rem 3rem;
  max-width: 1200px;
  width: 100%;
  overflow-x: hidden;
}

/* --- Tables --- */

table {
  background-color: #fff;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  border: 1px solid var(--ol-gray-200);
}

thead th {
  background-color: var(--ol-gray-50);
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ol-gray-500);
  border-bottom: 2px solid var(--ol-gray-200);
  padding: 0.75rem 1rem;
}

tbody td {
  padding: 0.75rem 1rem;
  font-size: 0.9rem;
  border-bottom: 1px solid var(--ol-gray-100);
  vertical-align: middle;
}

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

/* #3 — fixed-layout documents table. With the default `table-layout: auto` the
   browser re-measures EVERY row (including the full-width colSpan detail/edit
   panel) on each render, so expanding a row visibly reflowed the column widths
   on click. `table-layout: fixed` + the component's <colgroup> pins them. */
.doc-list-table {
  table-layout: fixed;
  width: 100%;
}
.doc-list-table th,
.doc-list-table td {
  overflow-wrap: anywhere;
}

tbody tr:hover {
  background-color: var(--ol-gray-50);
}

tbody tr[style*="cursor: pointer"]:hover {
  background-color: rgba(33, 242, 192, 0.06);
}

/* --- Cards / Articles --- */

article {
  background: #fff;
  border: 1px solid var(--ol-gray-200);
  border-radius: 10px;
  box-shadow: var(--shadow-sm);
}

article header {
  background-color: var(--ol-gray-50);
  border-bottom: 1px solid var(--ol-gray-200);
  padding: 0.75rem 1rem;
  border-radius: 10px 10px 0 0;
}

/* --- Forms --- */

input, select, textarea {
  font-size: 0.9rem;
  border-radius: 6px;
}

label {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--ol-gray-600);
}

/* sp-label-scope-restructure — the Subempreiteiro field group in Validation.tsx.
   It used to be a <label> wrapping the picker AND ~410 lines of notes, chips and
   warnings, so clicking any of that inert text forwarded the click to the
   Combobox trigger and opened the dropdown (a wrapping <label> labels its first
   labelable descendant). The wrapper is now a plain <div>; this rule reproduces
   the <label>'s computed style EXACTLY so the demotion is invisible.

   Each declaration is load-bearing — do not "simplify" this to a font-size:
     display / margin-bottom  ← pico's `label, fieldset legend` rule
                                 (pico.css:1373 in the UNMINIFIED copy; the app
                                 actually loads pico.min.css, main.tsx:9).
                                 Dropping margin-bottom collapses the 6px gap
                                 above the validity-dates row below.
     font-size / font-weight / color  ← the `label` rule directly above.
   font-size and font-weight INHERIT, so every <small> note and every
   `font: inherit` chip button inside the group is sized by this rule. Without
   it they all render a step larger (0.875rem vs 0.744rem) and unweighted.

   `color` is deliberately last-wins over pico's `color: var(--pico-color)`
   because styles.css is imported after pico (main.tsx:9-10). */
.sub-picker-group {
  display: block;
  margin-bottom: calc(var(--pico-spacing) * 0.375);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--ol-gray-600);
}

/* Two more `label` rules were audited and deliberately NOT carried over,
   because neither could ever have matched this element:
     pico `label > :where(input, select, textarea)` (margin-top) — all three
       <SubPickerField> branches wrap their control in a <div>, so no bare
       control is ever a direct child. A mirrored rule here would be dead CSS.
     pico `label:has([type=checkbox], [type=radio])` (width: fit-content) —
       there is no checkbox or radio anywhere in the subtree.
   One declaration is genuinely NOT reproduced: pico lists `label` in its
   `-ms-touch-action: manipulation` group. That is an IE10-only prefixed
   property with no effect in any browser this app supports, so "reproduces the
   label's computed style exactly" means exactly-as-rendered, not
   byte-for-byte across every declared property. */

/* fin-fe-sp-label-clicktarget-siblings — the "Data de validade efetiva"
   field group in Validation.tsx (validity-dates-grid, 2nd column). Same fix
   shape as .sub-picker-group above: it used to be a <label> wrapping the
   date <input> AND two conditional caption <small>s, so clicking either
   caption — or selecting that text to read it — forwarded the click to the
   input (a wrapping <label> targets its one labelable descendant). The
   wrapper is now a plain <div>; this rule reproduces the <label>'s computed
   style so the demotion is invisible.

   display / margin-bottom / font-size / font-weight / color — identical
   reasoning to .sub-picker-group (pico's `label, fieldset legend` rule +
   styles.css's own `label` rule two rules up).

   UNLIKE .sub-picker-group, the `<input>` here IS a bare direct child (no
   wrapping div) — so pico's `label > :where(input, select, textarea)`
   (margin-top) DID match the original label and must be carried over
   explicitly, or the input loses its top gap under the field name. */
.validity-end-group {
  display: block;
  margin-bottom: calc(var(--pico-spacing) * 0.375);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--ol-gray-600);
}
.validity-end-group > input {
  margin-top: calc(var(--pico-spacing) * 0.25);
}

fieldset {
  border: 1px solid var(--ol-gray-200);
  border-radius: 8px;
  padding: 1rem;
}

legend {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ol-gray-500);
  padding: 0 0.5rem;
}

/* --- Buttons --- */

/* uipolish7 C-07 (GLOBAL): `[role="button"]` folded into the base button rule
   so `<Link role="button">` action links (e.g. the "Rever" links) match the
   size of real <button>s instead of falling back to Pico's larger default
   sizing. Touches every element with role="button" — intentional
   normalisation. */
button,
[role="button"] {
  font-size: 0.875rem;
  font-weight: 500;
  border-radius: 6px;
  padding: 0.5rem 1rem;
  transition: all 0.15s;
}

button.outline {
  border-width: 1.5px;
  /* T-fix: guarantee a legible label + visible border on outline buttons.
     Pico's default leaves the text/border colour up to inherited custom
     properties; in dense table cells (e.g. the "Rever" action button) that
     could resolve to a near-white tone, rendering the button effectively
     invisible. Pin the navy brand colour explicitly + a transparent fill so
     it always reads as an outline button. */
  color: var(--ol-primary);
  border-color: var(--ol-primary);
  background: transparent;
}
button.outline:hover {
  background: var(--ol-primary);
  color: #fff;
}
/* The secondary modifier (used by the Validados "Reverter" button) keeps the
   neutral grey treatment but must still be legible — override the brand colour
   the rule above would otherwise apply. */
button.outline.secondary {
  color: var(--ol-gray-700);
  border-color: var(--ol-gray-300);
}
button.outline.secondary:hover {
  background: var(--ol-gray-100);
  color: var(--ol-gray-900);
}

/* Soft button — a filled-but-gentle button for in-table affordances (e.g. the
   validation-queue "Rever") where the chunky navy outline reads as too heavy.
   Faint navy-tinted fill, no hard border; still clearly a button.
   presencas01 Wave 2 (gestao-presencas.md point 5): the queue's "Rever" is a
   navigating `<Link role="button" className="btn-soft btn-compact">`, not a
   `<button>` — the element-scoped `button.btn-soft` selector never matched
   it, so the soft fill silently fell back to Pico's default link styling.
   Broaden to any `.btn-soft` on an anchor / role="button" element. */
button.btn-soft,
a.btn-soft,
[role="button"].btn-soft {
  border: none;
  background: var(--ol-gray-100);
  color: var(--ol-primary);
  font-weight: 600;
}
button.btn-soft:hover,
a.btn-soft:hover,
[role="button"].btn-soft:hover {
  background: var(--ol-gray-200);
  color: var(--ol-primary-hover);
}

/* Danger variant — destructive actions (deactivate, delete). Pairs with
   .outline so the button reads as "outline-style, but red". */
button.btn-danger,
button.outline.btn-danger {
  color: var(--ol-red);
  border-color: var(--ol-red-border);
  background: transparent;
}
button.btn-danger:hover,
button.outline.btn-danger:hover {
  background: var(--ol-red-bg);
}

/* --- Utility classes --- */

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.stat-card {
  background: #fff;
  border: 1px solid var(--ol-gray-200);
  border-radius: 10px;
  padding: 1rem 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}

/* Stat cards that link somewhere get a subtle hover lift */
a.stat-card:hover,
.stat-card--interactive:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.stat-card .stat-value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
  font-family: 'Clash Display', system-ui, sans-serif;
}

/* A `dot` ComplianceBadge used AS a stat-card value (the Conformidade cards).
   Keep the dot+text style, but restore the headline prominence the old pill
   carried so the percentage matches the sibling stat numbers instead of
   shrinking to flag-line's default 0.75rem. white-space:normal lets the rare
   "Por configurar" / multi-chip (noWorkers) case wrap rather than overflow —
   nowrap01-b made that the `.flag-line` default anyway, this rule just keeps
   its own font metrics. `align-items: center` overrides the base's (now)
   `flex-start`, and the dot's `margin-top` is reset to 0: the base
   `--flag-dot-first-line-offset` is an `em` value derived for a 0.75rem
   `.flag-dot` and resolves to ~9.5px of unwanted drop at this rule's 1.75rem
   — see the trap called out at `.flag-dot`'s own declaration. */
.stat-card .flag-line {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
  font-family: 'Clash Display', system-ui, sans-serif;
  margin: 0;
  white-space: normal;
  align-items: center;
}
.stat-card .flag-line .flag-dot {
  width: 0.5em;
  height: 0.5em;
  margin-right: 0.28em;
  margin-top: 0;
}

.stat-card .stat-label {
  /* P2 — darker and slightly heavier so the all-caps micro-label clears the
     contrast floor (was gray-500, near-illegible on white). */
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ol-gray-700);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* P2 — document summary tiles: a "/ total" suffix and a per-category sub-line */
.stat-card .stat-value-sub {
  font-size: 1rem;
  font-weight: 600;
  color: var(--ol-gray-400);
}

.stat-card .stat-breakdown {
  /* P2 — bump from 0.72rem/gray-500 (too small + too faint) for legibility. */
  font-size: 0.78rem;
  color: var(--ol-gray-600);
  letter-spacing: 0.01em;
}

/* Provisional-cover note under "Documentos válidos" — a subset of the green
   that is only provisionally satisfied (alternative doc within a legal window). */
.stat-card .stat-provisional {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--ol-yellow);
  letter-spacing: 0.01em;
}

.doc-summary-grid {
  margin-bottom: 1rem;
}

.alert-banner {
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
  border-radius: 8px;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.9rem;
  font-weight: 500;
}

.alert-banner--warning {
  background-color: var(--ol-yellow-bg);
  border: 1px solid var(--ol-yellow-border);
  color: var(--ol-yellow);
}

.alert-banner--danger {
  background-color: var(--ol-red-bg);
  border: 1px solid var(--ol-red-border);
  color: var(--ol-red);
}

.sortable-th {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

.alert-banner--info {
  background-color: var(--ol-blue-bg);
  border: 1px solid var(--ol-blue-border);
  color: var(--ol-blue);
}

.alert-banner--success {
  background-color: var(--ol-green-bg);
  border: 1px solid var(--ol-green-border);
  color: var(--ol-green-text);
}

.alert-banner a {
  margin-left: auto;
  font-weight: 600;
  white-space: nowrap;
}

/* S5 — stacked metric rows inside an alert banner (replaces the old
   pipe/slash/parens single-line mash-up on the expiry banner). */
.alert-banner__rows {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

/* P1 — shared <Alert variant="warning|info|danger"> component (components/Alert.tsx).
   Consolidates the ad-hoc `alert-banner` / inline-styled banners into one token-backed
   class set. The legacy `.alert-banner` rules above are left intact (still used by
   Dashboard/EmailLog); migrating those call sites is a separate follow-up. */
.alert {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
  border-radius: 8px;
  border: 1px solid transparent;
  font-size: 0.9rem;
  line-height: 1.4;
}
.alert__icon {
  flex: 0 0 auto;
  font-weight: 700;
  line-height: 1.4;
}
.alert__body {
  flex: 1 1 auto;
  min-width: 0;
}
.alert__title {
  font-weight: 600;
  margin-bottom: 0.15rem;
}
.alert--warning {
  background-color: var(--ol-yellow-bg);
  border-color: var(--ol-yellow-border);
  color: var(--ol-yellow-text);
}
.alert--info {
  background-color: var(--ol-blue-bg);
  border-color: var(--ol-blue-border);
  color: var(--ol-blue);
}
.alert--danger {
  background-color: var(--ol-red-bg);
  border-color: var(--ol-red-border);
  color: var(--ol-red);
}

/* P9 — give the dashboard tiles (TSU/DMR card et al.) a subtle shadow so they
   read as contained cards rather than floating tinted rectangles. */
.tile {
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.tile:hover {
  box-shadow: var(--shadow-md);
}

/* S7 — dashboard heading row: title on the left, freshness label + manual
   refresh on the right. Wraps on narrow viewports. */
.dashboard-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-bottom: 0.75rem;
}
.dashboard-heading h1 {
  margin: 0;
}
.dashboard-heading__meta {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.btn-refresh {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.8rem;
  font-size: 0.8rem;
  width: auto;
}
.btn-refresh__icon--spin {
  transform-origin: center;
  animation: dashboard-refresh-spin 0.8s linear infinite;
}
@keyframes dashboard-refresh-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .btn-refresh__icon--spin {
    animation: none;
  }
}

.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1rem;
}

.section-header h3 {
  margin: 0;
}

.text-muted {
  color: var(--ol-gray-500);
}

/* Inline help icon (InfoHint). Muted, ~13px, sits next to a label without
   affecting line height; `cursor: help` signals the hover tooltip. */
.info-hint {
  display: inline-block;
  margin-left: 0.3rem;
  color: var(--ol-gray-500);
  font-size: 0.8125rem;
  line-height: 1;
  cursor: help;
  user-select: none;
  vertical-align: baseline;
}

.info-hint:hover,
.info-hint:focus-visible {
  color: var(--ol-gray-700, var(--ol-gray-500));
}

.info-hint:focus-visible {
  outline: 2px solid var(--ol-primary, currentColor);
  outline-offset: 1px;
  border-radius: 2px;
}

.empty-state {
  text-align: center;
  padding: 3rem 1.5rem;
  color: var(--ol-gray-500);
}

.empty-state p {
  margin: 0;
  font-size: 0.95rem;
}

.empty-state .empty-state__subtitle {
  margin-top: 0.5rem;
  font-size: 0.85rem;
  color: var(--ol-gray-400);
}

.empty-state .empty-state__action {
  margin-top: 1rem;
}

/* Badges render as a leading colored dot + colored text (no pill / rounded
   rectangle). This unifies every `.badge` with the `.flag-line` treatment used
   by EstrangeiroBadge — see the "Estrangeiro" flag in dashboard rows (A5). The
   colour comes from the `.badge--*` modifier; the dot inherits it via
   `currentColor`. Unvariant'd badges fall back to neutral gray.
   nowrap01-b: same inversion as `.flag-line` above — wrap is now the
   default, not an opt-out re-derived per long badge. `line-height: 1.3` +
   `align-items: flex-start` + the `::before` dot's `margin-top` offset are
   the same recipe as `.flag-line`/`.flag-dot`, reusing the SAME derived
   `--flag-dot-first-line-offset` variable: this dot is the same 7px circle
   at the same 0.75rem/1.3 font metrics, so the (line-height − dot height)/2
   derivation is identical. If a badge ever needs to render at a different
   font-size (the way `.stat-card .flag-line` does for `.flag-line`), reset
   `align-items` to `center` and `::before`'s `margin-top` to `0` there —
   the em-unit trap documented at `.flag-dot`'s declaration applies here too. */
.badge {
  display: inline-flex;
  align-items: flex-start;
  gap: 4px;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 0;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.3;
  white-space: normal;
  overflow-wrap: break-word;
  color: var(--ol-gray-500);
}

.badge::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
  /* Same derivation as `--flag-dot-first-line-offset` on `.flag-dot` — this
     is a plain 0.34em literal, NOT a `var()` reference to that custom
     property, because custom properties only inherit through the DOM tree
     from where they're declared, and `.flag-dot` is a different element
     from `.badge::before` with no ancestor relationship between them. A
     `var(--flag-dot-first-line-offset)` here would silently resolve to
     nothing (0) outside `.flag-dot`'s subtree — the exact trap called out
     at that variable's own declaration ("CONSUME IT ONLY ON .flag-dot
     ITSELF"). */
  margin-top: 0.34em;
}

.badge--green { color: var(--ol-green); }
.badge--yellow { color: var(--ol-yellow); }
.badge--red { color: var(--ol-red); }
.badge--gray { color: var(--ol-gray-500); }
/* Overdue / needs-attention amber — distinct from yellow (simulado) so a held
   email past its expected send reads as urgent in the unified email table. */
.badge--amber { color: var(--ol-amber-600); }
/* Forecast projection ("Previsão") — distinct from blue (por aprovar / agendado)
   and gray (a enviar / recebido) so a not-yet-minted projection reads apart. */
.badge--indigo { color: var(--ol-indigo); }

/* Inactive-state flag (P14): darker neutral than badge--gray so a deactivated
   obra/sub/equipment reads clearly. Now a dark-gray dot+text instead of the old
   filled pill. */
.badge--inactive { color: var(--ol-gray-600); }

/* Compact size for the generic <Badge size="sm">; tightens the font for dense
   rows. Padding/background no longer apply to the dot+text style. */
.badge--sm {
  font-size: 0.7rem;
}

/* Interactive badge-buttons (e.g. IssuesBadge) — restore a visible focus ring
   that the .badge rules would otherwise erase. Hover lift gives a secondary
   affordance distinct from the static pills next to it. */
button.badge:focus-visible {
  outline: 2px solid var(--ol-blue, #2563eb);
  outline-offset: 2px;
}
button.badge:hover {
  text-decoration: underline;
}

/* Button sizes */
.btn-sm {
  padding: 0.25rem 0.5rem;
  font-size: 0.8rem;
}

.btn-md {
  padding: 0.4rem 0.75rem;
  font-size: 0.85rem;
}

/* Breadcrumb navigation */
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0;
  list-style: none;
  padding: 0;
  margin: 0 0 0.25rem;
  font-size: 0.85rem;
}

.breadcrumb li {
  display: flex;
  align-items: center;
}

.breadcrumb li + li::before {
  content: '/';
  margin: 0 0.4rem;
  color: var(--ol-gray-400);
}

.breadcrumb a {
  color: var(--ol-gray-500);
  text-decoration: none;
}

.breadcrumb a:hover {
  color: var(--ol-accent-ink, #0b7a63); /* a11y: raw turquoise text is ~1.45:1 on white */
  text-decoration: underline;
}

.breadcrumb .breadcrumb__current {
  color: var(--ol-gray-500);
}

/* Modal overlay & content.
   `.modal-overlay` is the ONE remaining non-native-dialog modal scrim
   (RequestCartDrawer only). Its backdrop is CSS-aligned to the native
   `.modal-content::backdrop` standard below (navy-tinted dim + blur) so it
   reads as a real modal rather than a flat black scrim. It stays a <div> (NOT
   a native <dialog>) — keeping z-index:1000 here is correct; this is the one
   place z-index stacking is still legitimate. */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.45);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

/* Match the native-dialog entrance: fade the scrim and lift the drawer panel in
   (same 0.16s pop/fade as .modal-content[open] above). Disabled under
   prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .modal-overlay {
    animation: modal-fade 0.16s ease-out;
  }
  .modal-overlay > .modal-content {
    animation: modal-pop 0.16s ease-out;
  }
}

.modal-content {
  background: #fff;
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
  max-width: 560px;
  width: calc(100% - 2rem);
  max-height: calc(100vh - 4rem);
  /* Keep the dialog itself as the scroll container — Settings.tsx and the
     request-cart drawer both rely on `.modal-content` (not `.modal-body`)
     owning overflow. Do NOT switch this to flex / overflow:hidden. */
  overflow-y: auto;
}

/* Pico v2 styles the bare `dialog` element as a FULL-VIEWPORT flex overlay that
   centres a `dialog > article` card (display:flex; flex-direction:row;
   min-width/height:100%). This app uses `.modal-content` AS the <dialog> (no
   inner <article>), so Pico's row-flex laid the modal's <header> and
   .modal-body out SIDE BY SIDE — the title rendered to the LEFT of the body
   instead of above it. Re-assert a top-anchored vertical stack and undo Pico's
   100% min-size so the card keeps its own max-width/height. Scoped to
   `dialog.modal-content` so the <div>-based modals (ConfirmDialog,
   RequestCartDrawer) — which Pico's `dialog` rule never touched — are
   unaffected. */
dialog.modal-content {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  min-width: 0;
  min-height: 0;
  /* uipolish7 FIX-4 / dlgslab1: size the card to its CONTENT, and keep it
     vertically centred.

     Pico v2 lays the <dialog> out as `position: fixed` with `inset: 0` (top AND
     bottom pinned to 0) plus `height: inherit`. FIX-4 replaced that with
     `height: auto` — but for an out-of-flow box, `height: auto` with BOTH `top`
     and `bottom` non-auto is defined to STRETCH between them, so the card still
     filled the viewport (clamped by `max-height`) regardless of content: a
     confirm box with ~175px of content still measured ~655px tall, painting a
     ~490px white slab under its buttons. `height: auto` did not fix the slab; it
     just stopped inheriting.

     `fit-content` makes the height a resolved (non-auto) value, which does two
     things at once: the box sizes to its content, AND — because `top`/`bottom`
     are now over-constrained against a definite height — the `auto` block
     margins below absorb the slack equally, re-centring the card vertically.
     That centring is why this is NOT `bottom: auto`, which would top-anchor
     every modal, and NOT a `transform: translateY(-50%)`, which would make the
     dialog a containing block for `position: fixed` descendants and break the
     portalled Combobox panel exactly as the backdrop-filter note below explains.

     `max-height` (on `.modal-content`) still clamps tall ones and hands the
     overflow to the dialog's own `overflow-y: auto`. */
  height: fit-content;
  margin-block: auto;
  /* Pico applies a `backdrop-filter: blur(...)` to the <dialog> ELEMENT itself
     (not just ::backdrop). A non-`none` backdrop-filter makes the dialog a
     CONTAINING BLOCK for `position: fixed` descendants — which silently breaks
     the dialog-portalled Combobox panel: its fixed `left` (a viewport coordinate
     from getBoundingClientRect) then resolves against the dialog's box instead
     of the viewport, so inside a narrow centred modal the dropdown jumps ~the
     modal's left offset to the side. The dialog card is opaque white, so this
     filter is visually a no-op anyway. The dim+blur AROUND the modal lives on
     `.modal-content::backdrop` and is unaffected. */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* The `display: flex` above is an author rule, so it overrides the UA's
   `dialog:not([open]) { display: none }` and a CLOSED <dialog> would still
   render. Modal.tsx / ConfirmDialog unmount when closed so never hit this, but
   CommandPalette stays mounted for its whole life and toggles open/closed —
   without this rule a closed palette renders inline, permanently visible, with
   no backdrop. Re-assert hidden-when-closed (higher specificity than the rule
   above wins). */
dialog.modal-content:not([open]) {
  display: none;
}

/* Native <dialog> backdrop — dim + soften the page behind a modal. Without
   this the dialog popped in over an undimmed page, reading as a "floating box"
   rather than a modal. Applies to all real <dialog> elements: the shared Modal,
   CommandPalette, RowDetailModal, and ConfirmDialog (now a native <dialog> too,
   so it stacks in the top layer above an open detail modal). Any remaining
   `.modal-overlay`-based drawer keeps its own dimming. */
.modal-content::backdrop {
  background: rgba(15, 23, 42, 0.45);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

/* uipolish7 C-03: DIRECT-CHILD combinator only. The request-cart drawer and
   other modal bodies nest their own group-card `<header>` elements; a
   descendant `.modal-content header` selector leaked this modal-titlebar
   styling (22px indent + a stray bottom divider) onto them. Scope to the
   modal's own titlebar. */
.modal-content > header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.5rem;
  border-bottom: 1px solid var(--ol-gray-200);
}

.modal-content > header h3 {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1.3;
}

/* Modal.tsx imperatively focuses the title on open (B-10: a11y reading order,
   and it keeps the header × from taking the ring on every mouse-open). Chromium
   treats that programmatic focus as :focus-visible, so the universal ring rule
   painted a 2px NAVY box (--ol-primary #030531, effectively black) + turquoise
   halo around the title of every shared-Modal pop-up — read as a stray "black
   contour" on the card. `.row-detail-modal > header h3` has carried this exact
   suppression since it was built; the plain `.modal-content` header never got
   it, which is why the row-detail pop-ups looked clean and these did not. Same
   reasoning as there: the heading is tabIndex={-1}, NOT a tab stop, so it has no
   focus indicator to communicate. Keyboard a11y is unchanged — focus stays in
   the top-layer dialog and Tab reaches the × (which keeps its own ring). Pico
   paints focus as a box-shadow as well as an outline, so suppress both. */
.modal-content > header h3:focus,
.modal-content > header h3:focus-visible {
  outline: none;
  box-shadow: none;
}

.modal-content .modal-body {
  padding: 1.5rem;
}

.modal-content .modal-close {
  background: none;
  border: none;
  /* uipolish7 C-05: kill the inherited Pico button padding/margin so the ×
     glyph sits centred on the title baseline (it was ~8px above centre). The
     2rem box + flex-centring below still gives it a comfortable hit area. */
  padding: 0;
  margin: 0;
  /* a11y: gray-400 (#9ca3af) was 2.5:1 on white — below the 3:1 minimum for
     UI glyphs (WCAG 1.4.11). gray-500 reads 4.8:1 and stays muted. */
  color: var(--ol-gray-500);
  cursor: pointer;
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  font-size: 1.4rem;
  line-height: 1;
  transition: background 0.12s ease, color 0.12s ease;
}

.modal-content .modal-close:hover {
  color: var(--ol-gray-700);
  background: var(--ol-gray-100);
}

/* Subtle entrance: fade the backdrop and lift the panel in. Runs once on open
   (showModal sets [open]; <div> modals never match so they're untouched).
   Disabled for users who prefer reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .modal-content[open] {
    animation: modal-pop 0.16s ease-out;
  }
  .modal-content[open]::backdrop {
    animation: modal-fade 0.16s ease-out;
  }
}

@keyframes modal-pop {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes modal-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* ── Shared confirm / pop-up box styling (P3a) ──────────────────────────────
   The four imperative confirm dialogs (ConfirmDialog, ChoiceDialog,
   ChildCascadeDialog, GlobalSaveSummaryDialog) all render a native
   `<dialog className="modal-content">` (top-layer stacking contract — see the
   Pico-override + ::backdrop blocks above; do NOT fold these into those) whose
   body previously repeated the same inline title / message / action-row / teal
   & red button styles. These classes consolidate that so every confirm box is
   minimalist and consistent. Title size is unified at 1.05rem (the size all
   four already used); spacing is tokenized to --sp-*. No behaviour change. */
.confirm-dialog {
  /* The <dialog> itself keeps maxWidth + border:none inline (per-dialog cap);
     these are the inner-body shared bits. */
}
/* Header-bar treatment so the confirm boxes read like the house detail modals
   (`.modal-content > header`): the title sits in a full-width bar with a bottom
   divider rather than floating inline in the body. Negative margins pull it out
   to the card edges (cancelling the `.modal-body` 1.5rem padding) so the divider
   spans the whole width exactly like the modal header; the shared size/weight
   (1.15rem/600) match `.modal-content > header h3`. Shared by all four confirm
   dialogs (ConfirmDialog / ChoiceDialog / ChildCascadeDialog /
   GlobalSaveSummaryDialog) so they stay consistent. */
.confirm-dialog__title {
  margin: -1.5rem -1.5rem var(--sp-4);
  padding: 1rem 1.5rem;
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1.3;
  border-bottom: 1px solid var(--ol-gray-200);
}
.confirm-dialog__message {
  white-space: pre-wrap;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--ol-gray-700);
}
.confirm-dialog__actions {
  display: flex;
  gap: var(--sp-3);
  justify-content: flex-end;
  margin-top: var(--sp-4);
}
/* Stacked variant — one full-width button per row (ChoiceDialog options). */
.confirm-dialog__actions--stack {
  flex-direction: column;
  gap: var(--sp-2);
}
/* Trim the heavy bottom gap under the action row so the card frames the buttons
   evenly (was the full 1.5rem `.modal-body` padding below the last button). */
.modal-content.confirm-dialog .modal-body {
  padding-bottom: var(--sp-4);
}
/* Pico's `[type=submit],[type=reset],[type=button] { margin-bottom: 1rem }` (a
   form-layout default) lands on every confirm button, since they are all
   `<button type="button">`. That margin sits INSIDE the action row, so it
   doubled the gap under the buttons (1rem margin + the 1rem padding above) while
   the gap above stayed 1rem — the row read as bottom-heavy. The flex `gap` owns
   the spacing between buttons here, and the `.modal-body` padding owns the gap
   to the card edge, so the per-button margin is pure noise. Note this also fixes
   the STACKED variant (ChoiceDialog), where Pico's margin was silently added to
   the `--sp-2` row gap. */
.confirm-dialog__actions button {
  margin-bottom: 0;
}

/* Shared filled action buttons used inside the confirm boxes. Scoped under the
   `confirm-dialog__` namespace so they never collide with the bare
   `.btn-primary` Pico-default class used elsewhere (e.g. Roster). Solid brand
   navy (not the bright accent) reads calmer / more professional inside a modal. */
.confirm-dialog__btn {
  font-weight: 600;
  border-radius: 8px;
  padding: 0.55rem 1rem;
  transition: background 0.12s ease, box-shadow 0.12s ease;
}
.confirm-dialog__btn--primary {
  background: var(--ol-primary, #030531);
  color: #fff;
  border: none;
}
.confirm-dialog__btn--primary:hover {
  background: #12163f;
}
.confirm-dialog__btn--destructive {
  background: var(--ol-red, #dc2626);
  color: #fff;
  border: none;
}
.confirm-dialog__btn--destructive:hover {
  background: #b91c1c;
}

/* Row-detail modal — a wide, scrollable variant of `.modal-content` for large
   detail content (DocumentList tables, worker/equipment panels) that replaces
   inline expandable table rows. The default `.modal-content` makes the dialog
   ITSELF the scroll container (`overflow-y:auto`); here we instead keep the
   dialog as a fixed-height column flex (header pinned, body scrolls) so the
   header stays visible while the body scrolls. The max-width is set inline by
   RowDetailModal (default ~900px), so this rule does NOT pin width. */
dialog.modal-content.row-detail-modal {
  /* Override the base `.modal-content { overflow-y:auto }` — the BODY scrolls,
     not the card, so the sticky header has somewhere to stick to. */
  overflow: hidden;
  /* Grow to fit content up to the viewport cap; beyond that the body scrolls.
     Short detail stays a compact card, tall detail fills the screen. The base
     `.modal-content` already sets `max-height: calc(100vh - 4rem)`; restated
     here for clarity alongside the column-flex body. */
  max-height: calc(100vh - 4rem);
}

/* popup-constant-height — entity detail popups (empresa / trabalhador / obra /
   equipamento, both the global entity popup and the Gestão overlay cards) pin
   the card to the viewport-cap height so switching tabs never grows/shrinks the
   card. The base `.modal-content` sizes to content (`height: fit-content`,
   which is exactly what makes the card jump per tab); a definite height here
   overrides it while `margin-block: auto` keeps the card vertically centred.
   Same value as the max-height cap, so nothing overflows the viewport; the
   `.modal-body` (flex: 1 1 auto, overflow-y: auto) absorbs the fixed height and
   scrolls. Opt-in via RowDetailModal's `constantHeight` prop — content-sized
   consumers (create sub-steps, EmailLog previews, validador full-screen) are
   untouched. */
dialog.modal-content.row-detail-modal.row-detail-modal--constant-height {
  height: calc(100vh - 4rem);
  /* Mobile: 100vh is the LARGEST viewport (URL bar collapsed), so a pinned
     100vh-based height sits under the browser chrome while the bar is visible
     — clipping the card's bottom edge. 100dvh tracks the *dynamic* viewport;
     browsers without dvh support ignore this line and keep the vh value. */
  height: calc(100dvh - 4rem);
}

.row-detail-modal > header {
  /* Pin the title/subtitle + close ✕ while the body scrolls beneath it. */
  position: sticky;
  top: 0;
  z-index: 1;
  background: #fff;
  border-radius: 14px 14px 0 0;
  align-items: flex-start;
  /* Reserve the SAME scrollbar gutter the body reserves (see `.modal-body`
     below). The header is a sibling of the scrolling body, not part of it, so
     without this its right edge (the ✕) would sit a scrollbar-width wider than
     the body content on classic-scrollbar platforms — they'd be misaligned.
     The header never scrolls, so this only reserves the band; no scrollbar
     appears. (No-op where the body's gutter is a no-op, i.e. overlay scrollbars.) */
  scrollbar-gutter: stable;
}

.row-detail-modal__titles {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
  /* A long unbroken title (e.g. a URL-style email subject) must wrap inside the
     sticky header rather than overflow it. */
  overflow-wrap: anywhere;
}

.row-detail-modal__subtitle {
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--ol-gray-500);
  line-height: 1.4;
}

.row-detail-modal > .modal-body {
  /* The scroll container. `overscroll-behavior: contain` stops the wheel from
     chaining to the page behind once the body hits its scroll boundary. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Reserve the vertical scrollbar gutter on THIS scroll container. The global
     `html { scrollbar-gutter: stable }` only governs the page scroller; the modal
     body is an independent scroll context, so without this an expandable/accordion
     row (Gestão sub-within-work, SubDetail/detail sections) that grows the content
     past `max-height` makes the body's own scrollbar appear, steals content width,
     and reflows the text containers — the layout visibly "jumps" on click. With the
     gutter reserved, the body width is constant whether or not the scrollbar shows.
     (No-op on platforms with overlay scrollbars.) */
  scrollbar-gutter: stable;
}

/* valpopup1 T2 C1 — fixed footer slot. A sibling of the scrolling `.modal-body`
   inside the column-flex card (`dialog.modal-content { flex-direction:column }`),
   so it never scrolls with the body; the body takes the remaining height. Pinned
   to the card bottom with a top divider so the action bar reads as a distinct
   band. Reserves the same scrollbar gutter as the header/body so its right edge
   lines up. */
.row-detail-modal > .row-detail-modal__footer {
  flex: 0 0 auto;
  background: #fff;
  border-top: 1px solid var(--ol-gray-200, #e5e7eb);
  border-radius: 0 0 14px 14px;
  padding: 0.85rem 1.5rem;
  scrollbar-gutter: stable;
  /* A tall footer (wrapped action bar + an open reject panel) must not push the
     body off-screen; cap it and let the footer scroll internally instead. */
  max-height: 45vh;
  overflow-y: auto;
}

/* valpopup1 T2 C2 — when the validador detail renders INSIDE the row-detail
   modal, the modal body must NOT itself scroll; each split-view column scrolls
   independently (viewer | Dados Extraídos). The body is a fixed-height flex box;
   the `.split-view` fills it and drops the 70vh page heuristic; each column owns
   its own overflow. Scoped to `.validation-modal-body` so the dashboard/other
   row-detail consumers (single scrolling body) are untouched. */
.row-detail-modal > .modal-body.validation-modal-body {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: 1.25rem 1.5rem;
}
.validation-modal-body .split-view {
  /* Fill the body; let the two columns scroll, not the grid. `minmax(0,1fr)` on
     the single implicit row bounds the grid to the flex height so the columns
     (not the grid) own the overflow — without it an `auto` row sizes to content
     and the panes never get a definite height to scroll within. Drops the 70vh
     page heuristic in the modal variant. */
  flex: 1 1 auto;
  min-height: 0;
  grid-template-rows: minmax(0, 1fr);
  align-items: stretch;
}
.validation-modal-body .split-view > .validation-pane {
  min-height: 0;
  /* min-width:0 was already inline on each pane; keep the column shrinkable. */
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* =====================================================================
   valpopup-viewer — full-bleed / chromeless RowDetailModal + the validador
   2-column full-height shell.
   ===================================================================== */

/* Full-bleed card: no full-width header / no fixed footer. `children` fill the
   whole dialog card top-to-bottom. The card itself is a column flex so the shell
   inside it can take 100% height. */
.row-detail-modal.row-detail-modal--full-bleed {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Fill the available height (capped at the viewport by .modal-content's
     max-height) so the left viewer pane can run the full height of the card even
     when Dados Extraídos is short. */
  height: calc(100vh - 4rem);
}

/* valpopup-afford FS — full-SCREEN sizing. The validador pop-up spans the whole
   viewport (edge-to-edge, minimal gutter) instead of the default centred card.
   Per-instance opt-in (only the consumer that passes `fullScreen`): other
   RowDetailModal consumers keep the compact centred card. Overrides the base
   `.modal-content` width/max-width (560px) and the row-detail max-height. Paired
   with `--full-bleed`, whose `height: calc(100vh - 4rem)` is overridden here so
   the shell fills the full height too. The tiny 0.5rem inset keeps the card off
   the very edge (and lets the backdrop read as a modal) without wasting space. */
/* Specificity NOTE: the base `dialog.modal-content.row-detail-modal` rule (above)
   sets `max-height: calc(100vh - 4rem)` at (0,2,1) — an element + two classes. A
   `.row-detail-modal.row-detail-modal--full-screen` selector is only (0,2,0), so
   it LOSES the max-height cascade and the card renders ~4rem short of full height
   (the user-reported "not full height"). Qualify the full-screen selectors with
   `dialog.modal-content` so they reach (0,4,1) and genuinely win max-height. */
dialog.modal-content.row-detail-modal.row-detail-modal--full-screen {
  width: 100vw;
  max-width: 100vw;
  height: 100vh;
  max-height: 100vh;
  margin: 0;
  border-radius: 0;
}
dialog.modal-content.row-detail-modal.row-detail-modal--full-screen.row-detail-modal--full-bleed {
  /* The full-bleed rule sets height: calc(100vh - 4rem); take the FULL height. */
  height: 100vh;
}

/* Visually-hidden heading: keeps the aria-labelledby target + the imperative
   focus-on-open landing point without painting a second (duplicate) title. */
.row-detail-modal__sr-title {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Floating ✕/minimize group pinned to the card's top-right corner, over the
   content, so the left viewer pane is truly full-height. A subtle translucent
   backing keeps the glyphs legible over a dark PDF or a light form alike. */
.row-detail-modal__floating-actions {
  position: absolute;
  top: 0.5rem;
  right: 0.6rem;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.1rem;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(2px);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}

/* The 2-column shell that fills the full-bleed card.
   col 1 = document viewer (full height), col 2 = the right column. */
.validation-shell {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: 0;
  flex: 1 1 auto;
  min-height: 0;
  height: 100%;
}
/* On a nested entity/action frame the viewer is taken OUT OF FLOW (it stays
   mounted and laid out — position:absolute + visibility:hidden — because the
   native PDF viewer exposes no page/zoom API to save and restore, so leaving its
   box alive is the only way to keep the operator's place; see lostplace01 #8 in
   docs/popup-nav-spec/finding-01-coverage-revert.md). It is no longer unmounted,
   as this comment used to say. Collapse to one col so the frame body uses the
   full width. */
.validation-shell.validation-shell--frame {
  grid-template-columns: minmax(0, 1fr);
}

/* Left column = the viewer pane, full height. valpopup-afford A — collapse the
   double scrollbar: the pane is a flex COLUMN whose viewer child flexes to fill
   the remaining height, so the pane itself NEVER needs to scroll (no
   `overflow-y:auto` on the pane). The ONLY scroller in the left column is then
   the document viewer's own native scroll (the PDF iframe / the zoom container)
   — one scrollbar, not a scrollbar-within-a-scrollbar. The small fixed header
   (filename + Fundido/Equivalente badges + provenance widget) sits above the
   flexing viewer. */
.validation-shell > .validation-pane {
  min-height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* overflow:hidden (NOT auto): the pane is exactly the column height; its viewer
     child owns the scroll. A defensive clip in case a child overruns. */
  overflow: hidden;
  padding: 1.25rem 1.25rem 1.25rem 1.5rem;
  border-right: 1px solid var(--ol-gray-200, #e5e7eb);
}
/* The viewer wrapper (the iframe's relative container OR the zoom/image
   container OR the error/loading placeholder) flexes to fill the pane height so
   the document fills the column and the iframe/zoom owns the only scroll. */
.validation-shell > .validation-pane > .validation-viewer-fill {
  flex: 1 1 auto;
  min-height: 0;
}
/* In the full-height pane the viewer must fill the flex slot, NOT a fixed 82vh
   (which left a gap and forced the pane to be a second scroller). Drop the fixed
   height for the in-shell instances so they take the flex height instead. */
.validation-shell > .validation-pane .validation-pdf-frame,
.validation-shell > .validation-pane .zoomable-image {
  height: 100%;
}

/* Right column = title block (top) + the Anterior/Seguinte nav BELOW it, then a
   scrolling region that holds Dados Extraídos + the action bar (they scroll
   together). The header (title + nav) is divided from the scroll by the scroll
   region's top border, NOT a divider under the title — so the nav reads as part
   of the same header block, below the document name/status line. */
.validation-right {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  /* Room on the right so the title/nav don't sit under the floating ✕/minimize. */
  padding: 1.25rem 1.5rem 0;
}
.validation-right__title {
  flex: 0 0 auto;
  margin: 0 0 0.4rem;
}
/* The title is always the top-most right-column element now (the nav moved below
   it), so it must keep clear of the floating ✕/minimize group (top-right of the
   card) — a long single-line filename wraps before running under the buttons. */
.validation-right > .validation-right__title:first-child {
  padding-right: 4.5rem;
}
.validation-right__nav {
  flex: 0 0 auto;
  min-height: 2rem;
  margin-bottom: 0.5rem;
}
/* Nav sits below the title, clear of the top-right floating controls, so it needs
   no right-corner reservation (drop the inherited clearance). */
.validation-right__title + .validation-right__nav {
  padding-right: 0;
}
.validation-right__filename {
  margin: 0;
  font-size: 1.05rem;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
.validation-right__meta {
  font-size: 0.85rem;
  color: var(--ol-gray-500, #6b7280);
  margin-top: 0.2rem;
}
/* The single scrolling region: Dados Extraídos + the action bar scroll together
   (the T2 fixed footer is un-pinned). A top border divides the pinned header
   (title + nav) above it from the scrolling content. */
.validation-right__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  border-top: 1px solid var(--ol-gray-200, #e5e7eb);
  padding-top: 0.85rem;
  padding-bottom: 1.25rem;
}
/* The action bar inside the scroll, set off from Dados Extraídos with a top
   divider so it still reads as a distinct band even though it scrolls. */
.validation-right__actions {
  margin-top: 1rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--ol-gray-200, #e5e7eb);
}

/* valpopup-afford B/C — the launcher rows above the approval buttons (the single
   "Registo de …" entity launcher + the "Ver email original" launcher). Each is a
   compact card: a muted description on the left, a small `.btn-sm` button on the
   right (the nicer styling the removed below-viewer widget had). */
.validation-launchers {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.validation-launcher-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  border: 1px solid var(--ol-gray-200, #e5e7eb);
  border-radius: 8px;
  background: var(--ol-gray-50, #f9fafb);
  padding: 0.6rem 0.85rem;
}
.validation-launcher-row__desc {
  /* uipolish7 B-07: `flex-basis: 0` (not auto) so the three launcher cards'
     descriptions all flex from the same zero base and the cards render one
     shape instead of three (auto seeded each row from its own text length). */
  flex: 1 1 0;
  min-width: 0;
}

/* Stack to a single column on narrow viewports (mirrors .split-view's collapse). */
@media (max-width: 900px) {
  .validation-shell {
    grid-template-columns: minmax(0, 1fr);
    /* Let the whole shell scroll as one on small screens (panes can't each get a
       usable height side-by-side). */
    overflow-y: auto;
  }
  .validation-shell > .validation-pane {
    border-right: none;
    border-bottom: 1px solid var(--ol-gray-200, #e5e7eb);
    /* valpopup-afford A — on mobile the shell scrolls as one, so the pane track
       auto-sizes to its content and a `flex:1` viewer would collapse to ~0px
       (there is no free vertical space to flex into). Drop the flex-fill column
       behaviour here and let the viewer take a definite height instead (the
       pre-change 82vh intent), so the document stays usable on small screens. */
    display: block;
    overflow: visible;
  }
  .validation-shell > .validation-pane > .validation-viewer-fill {
    height: calc(82vh - 2rem);
  }
}

/* The heading is programmatically focused on open (a11y reading order); never
   show a focus indicator for that imperative focus — it's not a tab stop. Pico
   paints focus as a box-shadow ring (not just `outline`), so suppress both. */
.row-detail-modal > header h3:focus,
.row-detail-modal > header h3:focus-visible {
  outline: none;
  box-shadow: none;
}

/* valpopup1 T3 — the inline (in-frame) variant of Modal: its content rendered
   as a plain block inside the validador pop-up's nav-stack frame, NOT a second
   native <dialog> (the "appear-behind" bug class). No card chrome / backdrop —
   the host frame's breadcrumb header owns the title + "← Voltar". */
.modal-inline {
  display: block;
}
.modal-inline > .modal-inline__header {
  margin-bottom: 0.75rem;
}
.modal-inline > .modal-inline__header h3 {
  margin: 0;
  font-size: 1.05rem;
}
/* The inline body is just content flow — drop the dialog body's scroll cap so it
   participates in the frame's own scroll container (the frame wrapper scrolls). */
.modal-inline > .modal-body {
  max-height: none;
  overflow: visible;
  padding: 0;
}

/* rowmodal1 wave-2 — "← Voltar" back affordance shown in the title row of an
   in-modal create sub-step (the create flow collapsed into the row modal). A
   compact inline link-button so it reads as secondary to the step title. */
.row-detail-back {
  font: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  padding: 0.1rem 0.4rem;
  margin: 0;
  width: auto;
  background: transparent;
  border: 1px solid var(--ol-gray-200, #e5e7eb);
  border-radius: 6px;
  color: var(--ol-gray-600, #4b5563);
  cursor: pointer;
  line-height: 1.4;
}
.row-detail-back:hover {
  background: var(--ol-gray-100, #f3f4f6);
  color: var(--ol-gray-800, #1f2937);
}

/* rowmodal2 wave-2 — pin/minimize ("Gmail draft") affordance for RowDetailModal.
   The header now holds a button group (minimize + close). When minimized, the
   dialog is `.close()`d (drops the top layer + backdrop, page becomes
   interactive) and the `.row-detail-dock` chip below is portal'd to <body>. */
.row-detail-modal__actions {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  /* Pin to the top so the buttons align with the first line of a wrapping
     title, matching the sticky header's `align-items: flex-start`. */
  flex: 0 0 auto;
}

/* The minimize button shares the close button's footprint/affordance so the two
   read as a matched pair (Pico would otherwise stretch a bare <button>). */
.row-detail-modal .modal-minimize {
  position: relative;
  background: none;
  border: none;
  color: var(--ol-gray-400);
  cursor: pointer;
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  line-height: 1;
  padding: 0;
  margin: 0;
  transition: background 0.12s ease, color 0.12s ease;
}
/* A CSS-drawn bar centred the SAME way as the close × (absolute top/left 50% +
   translate(-50%,-50%)), NOT by flexbox. Flex-centring and absolute-centring
   round to different sub-pixels when the dialog sits at a fractional coordinate
   (showModal centres it), so a flex bar vs an absolute × drifted ~0.2-1px
   vertically — the visible "minimize sits higher than ×" misalignment. Identical
   positioning math = pixel-identical centre at any coordinate. */
.row-detail-modal .modal-minimize__bar {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 0.9rem;
  height: 1.5px;
  border-radius: 1px;
  background: currentColor;
}
.row-detail-modal .modal-minimize:hover {
  color: var(--ol-gray-700);
  background: var(--ol-gray-100);
}
.row-detail-modal .modal-minimize:focus-visible {
  outline: 2px solid var(--ol-accent);
  outline-offset: 1px;
}

/* Draw the close × from two CSS bars too — scoped to the row-detail modal ONLY
   (the shared `.modal-content .modal-close` glyph used by every other modal is
   untouched). The `&times;` font glyph sits slightly above its line-box centre
   and carries its own stroke weight, so it never matched the minimize bar in
   vertical centre or thickness. Two absolutely-centred 1.5px bars share the
   minimize bar's geometry exactly, so the pair is aligned and equal-weight. */
.row-detail-modal .modal-close {
  position: relative;
  /* Identical box to .modal-minimize so the two are a TRUE matched pair. Don't
     lean on `.modal-content .modal-close` (same specificity, earlier in source)
     for sizing — it sets width/height but NOT padding/margin/border, so Pico's
     default button padding/border skewed the × box vertically against the
     minimize box (which zeroes them). Restate the full box here so both buttons
     are byte-identical and align with each other when top-pinned. */
  background: none;
  border: none;
  color: var(--ol-gray-400);
  cursor: pointer;
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  line-height: 1;
  padding: 0;
  margin: 0;
  /* Hide the literal × glyph; the two bars below render the icon. */
  font-size: 0;
}
.row-detail-modal .modal-close::before,
.row-detail-modal .modal-close::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0.9rem;
  height: 1.5px;
  border-radius: 1px;
  background: currentColor;
}
.row-detail-modal .modal-close::before {
  transform: translate(-50%, -50%) rotate(45deg);
}
.row-detail-modal .modal-close::after {
  transform: translate(-50%, -50%) rotate(-45deg);
}

/* dock-multi-minimize — the shared dock BAND: one fixed bottom-right container
   (portal'd to <body> by MinimizedDockProvider) that lays out ALL minimized
   chips in a row. Chips flow right-to-left (newest nearest the right) via
   row-reverse; the "+N" overflow pill is the FIRST DOM child so row-reverse
   parks it at the LEFT end (the oldest/overflow side). `flex-wrap: nowrap` keeps
   it to one band; per-chip `max-width` + the MAX_VISIBLE_CHIPS cap (→ overflow
   pill) keep it from ever exceeding the viewport width. */
/* dock-chip-toplayer — the band now portals INTO the top-most open dialog (so
   chips outpaint its backdrop and stay clickable; see lib/topLayerDialogs.ts).
   While the dialog's modal-pop ENTRANCE animation runs, the animated transform
   makes the dialog a containing block for fixed descendants, so for those
   ~160ms the band would paint relative to the card (clipped by its
   overflow:hidden) and then snap to the viewport corner. Mask it: on every
   (re)mount — the portal-target switch remounts the band's DOM — fade the band
   in only AFTER the entrance animation is over. Scoped to the same
   reduced-motion query as modal-pop: when that animation doesn't run there is
   no flash to hide, and the band appears instantly. */
@media (prefers-reduced-motion: no-preference) {
  .row-detail-dock-band {
    /* 0.18s delay > the 0.16s modal-pop; `backwards` holds opacity 0 during it. */
    animation: dock-band-in 0.12s ease-out 0.18s backwards;
  }
}
@keyframes dock-band-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.row-detail-dock-band {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  /* Above page content but below any real top-layer <dialog> (the top layer
     always paints over normal positioned content regardless of z-index), so a
     freshly-opened modal/confirm still covers the chips. */
  z-index: 60;
  display: flex;
  flex-direction: row-reverse;
  flex-wrap: nowrap;
  align-items: flex-end;
  gap: 0.5rem;
  /* Never run off the left edge on a narrow viewport. */
  max-width: calc(100vw - 2.5rem);
}

/* Minimized dock chip — a compact rounded rectangle, like a minimized Gmail
   compose draft. A normal element (no longer self-fixed; the band positions it),
   NOT a top-layer <dialog>, so it never blocks or dims the page. */
.row-detail-dock {
  display: flex;
  align-items: stretch;
  flex: 0 1 auto;
  /* Shrink toward a sensible min as the count grows (label ellipsizes), but keep
     restore + close usable. The cap routes the rest into the overflow pill. */
  min-width: 8.5rem;
  max-width: min(15rem, calc(100vw - 2.5rem));
  background: var(--ol-primary, #030531);
  color: #fff;
  border-radius: 14px;
  box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, 0.25));
  overflow: hidden;
}
.row-detail-dock:focus {
  outline: none;
}
@media (prefers-reduced-motion: no-preference) {
  .row-detail-dock {
    animation: dock-pop 0.16s ease-out;
  }
}
@keyframes dock-pop {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* The title doubles as the restore control (click anywhere on the label to
   re-expand), like Gmail. A flush, left-aligned button — no Pico chrome. */
.row-detail-dock__title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  width: auto;
  text-align: left;
  background: transparent;
  border: none;
  color: inherit;
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.3;
  padding: 0.6rem 0.4rem 0.6rem 0.9rem;
  cursor: pointer;
  display: flex;
  align-items: center;
}
.row-detail-dock__title:hover {
  background: rgba(255, 255, 255, 0.06);
}
.row-detail-dock__title:focus-visible {
  outline: 2px solid var(--ol-accent);
  outline-offset: -2px;
}
.row-detail-dock__label {
  display: block;
  min-width: 0;
  /* Keep the chip to a single tidy line. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.row-detail-dock__actions {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  padding-right: 0.35rem;
}
.row-detail-dock__btn {
  background: transparent;
  border: none;
  color: rgba(255, 255, 255, 0.75);
  cursor: pointer;
  width: 1.85rem;
  height: 1.85rem;
  margin: 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  font-size: 1.1rem;
  line-height: 1;
  transition: background 0.12s ease, color 0.12s ease;
}
.row-detail-dock__btn:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.12);
}
.row-detail-dock__btn:focus-visible {
  outline: 2px solid var(--ol-accent);
  outline-offset: -2px;
}

/* dock-multi-minimize — "+N" overflow: when more than MAX_VISIBLE_CHIPS drafts
   are minimized, the oldest fold into this pill (parked at the left end of the
   band) which opens a scrollable popover listing the hidden drafts. This is what
   keeps the dock spam-proof: the on-screen chip count is capped no matter how
   many drafts the operator opens, and the popover itself scrolls. */
.row-detail-dock-overflow {
  position: relative;
  flex: 0 0 auto;
}
.row-detail-dock-pill {
  background: var(--ol-primary, #030531);
  color: #fff;
  border: none;
  cursor: pointer;
  height: 2.6rem;
  min-width: 2.6rem;
  padding: 0 0.7rem;
  border-radius: 14px;
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1;
  box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, 0.25));
  transition: background 0.12s ease;
}
.row-detail-dock-pill:hover {
  background: var(--ol-primary-hover, #0a0d4a);
}
.row-detail-dock-pill:focus-visible {
  outline: 2px solid var(--ol-accent);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: no-preference) {
  .row-detail-dock-pill {
    animation: dock-pop 0.16s ease-out;
  }
}

/* The popover floats ABOVE the pill (anchored to its left so it never pushes off
   the right edge). Capped height with its own scroll → bounded DOM/jank even
   with very many hidden drafts. */
.row-detail-dock-overflow__menu {
  position: absolute;
  bottom: calc(100% + 0.5rem);
  left: 0;
  width: min(20rem, calc(100vw - 2.5rem));
  max-height: min(60vh, 24rem);
  display: flex;
  flex-direction: column;
  background: var(--ol-primary, #030531);
  color: #fff;
  border-radius: 12px;
  box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, 0.25));
  overflow: hidden;
}
.row-detail-dock-overflow__menu-head {
  flex: 0 0 auto;
  padding: 0.5rem 0.85rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.7);
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}
.row-detail-dock-overflow__list {
  list-style: none;
  margin: 0;
  padding: 0.25rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.row-detail-dock-overflow__item {
  display: flex;
  align-items: stretch;
  border-radius: 8px;
}
.row-detail-dock-overflow__item:hover {
  background: rgba(255, 255, 255, 0.06);
}
.row-detail-dock-overflow__restore {
  flex: 1 1 auto;
  min-width: 0;
  text-align: left;
  background: transparent;
  border: none;
  color: inherit;
  font: inherit;
  font-size: 0.88rem;
  padding: 0.5rem 0.4rem 0.5rem 0.6rem;
  cursor: pointer;
  display: flex;
  align-items: center;
}
.row-detail-dock-overflow__restore:focus-visible {
  outline: 2px solid var(--ol-accent);
  outline-offset: -2px;
  border-radius: 6px;
}

/* Skeleton loading animation */
.skeleton {
  background: linear-gradient(90deg, var(--ol-gray-100) 25%, var(--ol-gray-200) 50%, var(--ol-gray-100) 75%);
  background-size: 200% 100%;
  animation: skeleton-pulse 1.5s ease-in-out infinite;
  border-radius: 6px;
}

.skeleton-row {
  height: 1rem;
  margin-bottom: 0.75rem;
  background: linear-gradient(90deg, var(--ol-gray-100) 25%, var(--ol-gray-200) 50%, var(--ol-gray-100) 75%);
  background-size: 200% 100%;
  animation: skeleton-pulse 1.5s ease-in-out infinite;
  border-radius: 4px;
}

.skeleton-row:last-child {
  margin-bottom: 0;
  width: 60%;
}

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

/* Suppress Pico's aria-busy ::before spinner when the container already
   shows skeleton placeholders — otherwise spinner + skeleton both render.
   Screen readers still see aria-busy="true" for the loading announcement. */
[aria-busy="true"]:has(.skeleton, .skeleton-row)::before {
  display: none !important;
}

/* Sticky action bar */
.sticky-actions {
  position: sticky;
  bottom: 0;
  background: #fff;
  padding: 0.75rem 1rem;
  border-top: 1px solid var(--ol-gray-200);
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
  display: flex;
  gap: 0.5rem;
  align-items: center;
  z-index: 10;
}

/* The 6-action approval bar (Aprovar/Guardar/Rejeitar/Dividir/Fundir/Descartar)
   fits one row at rest. Pico's aria-busy spinner prepends a ::before of ~1em
   plus `margin-inline-end: spacing/2`, widening the busy button (e.g. Aprovar
   while approveMut is pending) just enough to wrap the last action (Descartar)
   onto a second line — a jarring shift on every click. Overlay the spinner
   instead of insetting it: a transparent label keeps the button's resting
   width, and the absolute-centred ::before adds zero layout width, so the busy
   state is a no-reflow swap. The spinner stays visible because Pico draws it
   from a background-image (--pico-icon-loading), not from currentColor, so the
   transparent text colour doesn't hide it.
   Specificity: Pico's base `[aria-busy=true]:not(input,select,textarea,html,form)
   ::before` is (0,1,2) — the single `:not(...)` over an element list counts as
   (0,0,1), not one class per arg — so this (0,2,2) rule wins `position`. (Pico's
   filter-only `:is(button…):not(.outline)::before` rule is (0,2,3) but sets only
   `filter`, which we don't touch, so the white spinner on the filled Aprovar
   button is preserved.) `position:absolute` also pulls the spinner out of flow,
   so its `margin-inline-end` no longer adds layout width regardless. */
.sticky-actions button[aria-busy="true"] {
  position: relative;
  color: transparent !important;
}
.sticky-actions button[aria-busy="true"]::before {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: 0;
  transform: translate(-50%, -50%);
}

/* Sticky table headers */
thead th {
  position: sticky;
  top: 0;
  z-index: 5;
}

/* UX-1 request cart pill — OPS_PLAN §6 */
.request-cart-pill {
  position: sticky;
  top: 0.75rem;
  float: right;
  margin: 0 0 0.5rem 1rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.85rem;
  background: var(--ol-blue, #2563eb);
  color: #fff;
  border: none;
  border-radius: 999px;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(37, 99, 235, 0.25);
  z-index: 20;
}

.request-cart-pill:hover {
  background: var(--ol-blue-hover, #1d4ed8);
  color: #fff;
}

.request-cart-pill:focus-visible {
  outline: 2px solid var(--ol-blue, #2563eb);
  outline-offset: 2px;
}

.request-cart-drawer {
  max-width: 720px;
  display: flex;
  flex-direction: column;
}

.request-cart-drawer .modal-body {
  flex: 1;
  overflow-y: auto;
}

/* Clickable row */
.clickable-row {
  cursor: pointer;
  transition: background-color 0.15s ease;
}

.clickable-row:hover {
  background-color: rgba(33, 242, 192, 0.06);
}

.clickable-row:focus-visible {
  /* a11y: turquoise on a white row was ~1.45:1 — invisible to keyboard users */
  outline: 2px solid var(--ol-primary, #030531);
  outline-offset: -2px;
}

/* Generic keyboard-focusable clickable element */
[role="button"][tabindex]:focus-visible {
  outline: 2px solid var(--ol-primary, #030531);
  outline-offset: 2px;
}

/* Table captions */
table caption {
  caption-side: top;
  text-align: left;
  font-size: 0.85rem;
  color: var(--ol-gray-500);
  padding-bottom: 0.5rem;
}

/* Expanded row aria hint */
tr[aria-expanded] {
  cursor: pointer;
}

tr[aria-expanded]:hover {
  background-color: rgba(33, 242, 192, 0.06);
}

/* Unified tabs component */
.tabs {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 1.5rem;
  /* B1-P1-2 (bughunt0707): the bar is horizontally scrollable at EVERY width,
     not just ≤768px — inside `.main-content { overflow-x: hidden }` a
     non-scrolling overflowing bar simply CLIPPED the trailing tabs, making
     "Equipamentos duplicados"/"Validados" unreachable between 769–~1310px.
     The grey baseline rule moves from `border-bottom` to an INSET shadow: a
     scroll container clips at its padding box, so the old “tab overhangs the
     border by margin-bottom:-2px” trick would get its active underline clipped.
     The inset shadow paints the same 2px line INSIDE the box (behind the tabs'
     transparent 3px bottom borders, covered by the active tab's accent border)
     — pixel-equivalent rendering, but nothing overflows vertically. */
  box-shadow: inset 0 -2px 0 var(--ol-gray-200);
  padding-bottom: 0;
  overflow-x: auto;
  overflow-y: hidden;
  flex-wrap: nowrap;
  -webkit-overflow-scrolling: touch;
  /* Desktop affordance: a thin scrollbar when (and only when) the bar
     overflows. The ≤768px media block below still hides it for touch. */
  scrollbar-width: thin;
  /* obra-popup-tabs follow-up (2026-07-28) — SCROLL CUE. Measured on a 390px
     viewport: the obra popup's 3-tab bar needs 359px in 315px, so "Histórico"
     clipped to "Histó…" hard against the card edge — and because the ≤768px
     block below hides the scrollbar, nothing at all signalled that a tab was
     off-screen. (The trabalhador bar ships 4 tabs, so it is worse in practice.)
     This is the classic CSS-only scroll shadow: the two `local` gradients are
     painted in the CONTENT box and scroll away with it, while the two `scroll`
     shadows stay pinned to the edges — so each fade auto-hides exactly when
     there is nothing more to scroll in that direction, with no JS and no
     resize observer. Deliberately NOT TableScroll: that wraps content in a
     focusable role="region" with its own Arrow-key handler, which would fight
     the tablist's roving-focus keyboard pattern. */
  background-image:
    linear-gradient(to right, var(--ol-surface, #fff) 30%, rgba(255, 255, 255, 0)),
    linear-gradient(to left, var(--ol-surface, #fff) 30%, rgba(255, 255, 255, 0)),
    linear-gradient(to right, rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0)),
    linear-gradient(to left, rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0));
  background-position: left center, right center, left center, right center;
  background-repeat: no-repeat;
  background-size: 32px 100%, 32px 100%, 12px 100%, 12px 100%;
  background-attachment: local, local, scroll, scroll;
}

.tabs__tab {
  border: none;
  border-bottom: 3px solid transparent;
  border-radius: 0;
  background: none !important;
  color: var(--ol-gray-500);
  font-weight: 500;
  font-size: 0.9rem;
  padding: 0.6rem 1.1rem;
  /* was -2px to overlap the container's border-bottom; the baseline is now an
     inset shadow inside the box (see .tabs above), so no overhang is needed —
     and none is allowed (the scroll container would clip it). */
  margin-bottom: 0;
  /* Never shrink a tab: the row scrolls instead (previously ≤768px-only). */
  flex: 0 0 auto;
  transition: color 0.15s, border-color 0.15s;
  white-space: nowrap;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  outline: none;
  box-shadow: none;
  --pico-background-color: transparent;
  --pico-border-color: transparent;
  --pico-color: var(--ol-gray-500);
}

.tabs__tab:hover {
  color: var(--ol-gray-700);
  background: none !important;
  --pico-background-color: transparent;
  --pico-border-color: transparent;
}

.tabs__tab:focus-visible {
  /* a11y: was outline:none + a 30%-alpha turquoise halo (≪3:1 — invisible).
     Inset ring so the mobile scrollable .tabs row can't clip it. */
  outline: 2px solid var(--ol-primary, #030531);
  outline-offset: -2px;
  box-shadow: none;
  border-radius: 4px 4px 0 0;
}

.tabs__tab--active {
  color: var(--ol-primary);
  font-weight: 600;
  border-bottom-color: var(--ol-accent);
  background: none !important;
  --pico-background-color: transparent;
  --pico-color: var(--ol-primary);
}

.tabs__badge {
  background: var(--ol-gray-100);
  color: var(--ol-gray-600);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 99px;
  line-height: 1.3;
}

.tabs__tab--active .tabs__badge {
  background: rgba(33, 242, 192, 0.15);
  color: var(--ol-primary);
}

/* Skip to main content (accessibility) */
.skip-to-main {
  position: absolute;
  top: -100%;
  left: 0;
  padding: 0.75rem 1.5rem;
  background: var(--ol-primary);
  color: #fff;
  z-index: 9999;
  font-weight: 600;
  text-decoration: none;
}

.skip-to-main:focus {
  top: 0;
}

/* Expanded row accent border + transition */
.expanded-row {
  padding: 1rem;
  background-color: var(--ol-gray-50);
  border-top: 1px solid var(--ol-gray-200);
  border-left: 4px solid var(--ol-accent);
  animation: expandRow 0.2s ease-out;
}

@keyframes expandRow {
  from {
    opacity: 0;
    max-height: 0;
    padding-top: 0;
    padding-bottom: 0;
  }
  to {
    opacity: 1;
    max-height: 600px;
    padding-top: 1rem;
    padding-bottom: 1rem;
  }
}

/* Blue badge */
.badge--blue {
  color: var(--ol-blue);
}

/* Subtle "overdue" emphasis layered on the "Por aprovar (passado)" badge when a
   held email has waited >24h. NOT a separate estado/filter — just an amber tint +
   slight weight nudging the operator that this one is stale. Follows badge--blue
   in the cascade so it overrides the base blue colour at equal specificity. */
.badge--overdue-emphasis { color: var(--ol-amber-600); font-weight: 600; }

/* Upload zone */
.upload-zone {
  border: 2px dashed var(--ol-gray-300);
  border-radius: 10px;
  padding: 2rem;
  text-align: center;
  cursor: pointer;
  transition: all 0.2s;
  background: #fff;
}

.upload-zone:hover {
  border-color: var(--ol-accent);
  background-color: rgba(33, 242, 192, 0.06);
}

.upload-zone--active {
  border-color: var(--ol-green);
  background-color: var(--ol-green-bg);
}

.upload-zone--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.upload-zone--disabled:hover {
  border-color: var(--ol-gray-300);
  background-color: #fff;
}

.upload-zone p {
  margin: 0;
  color: var(--ol-gray-500);
  font-size: 0.9rem;
}

/* File status cards */
.file-card {
  padding: 0.75rem 1rem;
  margin-bottom: 0.5rem;
  border-radius: 8px;
  border: 1px solid var(--ol-gray-200);
  background: #fff;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

.file-card--done {
  border-color: var(--ol-green-border);
  background-color: var(--ol-green-bg);
}

.file-card--error {
  border-color: var(--ol-red-border);
  background-color: var(--ol-red-bg);
}

.file-card__name {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--ol-gray-800);
  /* Truncate long Portuguese filenames instead of pushing the status off the
     row — the parent flex item already carries min-width: 0 so this clips. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-card__status {
  font-size: 0.85rem;
  white-space: nowrap;
  /* Never shrink/wrap the status — the name truncates instead, so the two
     can't overlap on narrow viewports. */
  flex-shrink: 0;
}

.file-card__detail {
  font-size: 0.8rem;
  color: var(--ol-gray-500);
  margin-top: 0.25rem;
  /* long unbroken duplicate filenames must not overflow the card / strike through the status at 390px */
  overflow-wrap: anywhere;
}

/* Create form panel */
.create-form {
  margin-bottom: 1.25rem;
  padding: 1.25rem;
  background-color: #fff;
  border-radius: 10px;
  border: 1px solid var(--ol-gray-200);
  box-shadow: var(--shadow-sm);
}

/* Tab buttons */
.tab-bar {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 1.5rem;
  border-bottom: 2px solid var(--ol-gray-200);
  padding-bottom: 0;
}

.tab-bar button {
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: none;
  color: var(--ol-gray-500);
  font-weight: 500;
  padding: 0.5rem 1rem;
  margin-bottom: -2px;
  transition: all 0.15s;
  outline: none;
  --pico-background-color: transparent;
  --pico-border-color: transparent;
  --pico-color: var(--ol-gray-500);
}

.tab-bar button:hover {
  color: var(--ol-gray-700);
  background: none;
  --pico-background-color: transparent;
}

.tab-bar button:focus-visible {
  /* a11y: was outline:none + faint turquoise halo — see .tabs__tab note */
  outline: 2px solid var(--ol-primary, #030531);
  outline-offset: -2px;
  box-shadow: none;
  border-radius: 4px 4px 0 0;
}

.tab-bar button.active {
  color: var(--ol-primary);
  border-bottom-color: var(--ol-accent);
  background: none;
  --pico-background-color: transparent;
  --pico-color: var(--ol-primary);
}

/* Filter row */
.filter-row {
  display: grid;
  /* C-01: auto-FIT (not auto-fill) so a short row — e.g. the 2-field /upload
     obra+sub selectors — expands its controls to fill the width instead of
     leaving phantom empty tracks that lock each field to ~251px and truncate.
     The 4-field dashboard row is unaffected (no empty tracks to collapse). */
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1rem;
  margin-bottom: 1.25rem;
}

/* FIX-2 — shared control-height normalisation. A bare Pico <input>/<select> that
   sits in a row of `.ol-combobox` triggers (Combobox controls) uses Pico's own
   vertical padding + a phantom ~15px margin-bottom, so it renders TALLER than the
   ~36px trigger and drops a gap below it. Match the trigger's box height and kill
   the margin. HEIGHT ONLY (padding-block + line-height + margin) — horizontal
   padding is left alone so <select> keeps room for Pico's chevron. Covers the
   dashboard filter row (A-04), the settings invite/test-email rows (D-03), and
   the equipment-create modal inputs (A-08). Checkbox/radio/range/file excluded so
   native controls keep their look. */
.filter-row input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
.filter-row select,
.settings-invite-row input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
.settings-invite-row select,
.modal-body form input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
.modal-body form select {
  padding-block: 0.5rem;
  line-height: 1.4;
  margin-bottom: 0;
}

/* …but the `padding-block` above never actually resized an <input>: Pico pins
   `input:not([type=checkbox],[type=radio],[type=range])` to
   `height: calc(1rem * var(--pico-line-height) + spacing*2 + border*2)` — a
   hardcoded 1rem, NOT the element's font-size — and a fixed height beats
   padding. That is why the search box still rendered ~11px taller than the
   .ol-combobox__trigger beside it. Release the pin so padding + line-height
   define the box (35.9px, exactly the trigger). The horizontal inset is pulled
   to the trigger's 0.7rem through the Pico var rather than `padding-inline`, so
   <select> keeps its `calc(var + 1.5rem)` chevron gutter.

   Applied to exactly the surfaces the `padding-block` above was written for —
   NOT globally. This app has two button heights: a bare/.primary/.outline
   <button> is 36.7px, while `type="button"`/`type="submit"` picks up Pico's own
   larger box at 47px (~half of the app's buttons each). A global rule would drop
   every field to 35.9px, including those sitting inline with a 47px button.
   Within these three scopes nothing sits shoulder-to-shoulder with a 47px
   button: .settings-invite-row pairs the field with a .primary (36.7px) and a
   Combobox trigger (35.9px), and .modal-body form stacks its fields vertically
   with the submit/cancel pair in their own footer row. */
.filter-row input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="submit"], [type="button"], [type="reset"]),
.filter-row select,
.settings-invite-row input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="submit"], [type="button"], [type="reset"]),
.settings-invite-row select,
.modal-body form input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="submit"], [type="button"], [type="reset"]),
.modal-body form select,
input.validation-queue-filter-name {
  height: auto;
  --pico-form-element-spacing-horizontal: 0.7rem;
}

/* FIX-2b — <select> height parity with <input>. Same root cause as the FIX-2
   note above, seen from the other side: Pico pins ONLY
   `input:not([type=checkbox],[type=radio],[type=range])` to
   `calc(1rem * line-height + spacing*2 + border*2)` (pico.css:1362) off a
   HARDCODED 1rem, and nothing pins <select> at all. Since the app puts every
   form control at `font-size: 0.9rem` (see the Forms block above), a <select>
   auto-sizes from the SMALLER font and renders 45px next to a 47px <input> —
   app-wide, in every panel that mixes the two.

   Measured (real Chromium, real pico.css + this file, 15px root):
   input 47 / select 45 / textarea 45. AutoGrowTextarea.tsx has no formula floor
   of its own — it sizes purely from `scrollHeight` at runtime (useLayoutEffect,
   AutoGrowTextarea.tsx:22-36), which already tracks its content and lands on
   47px for a single empty/short row; this rule closes the remaining gap, so
   every form control reads 47px.

   min-height (not height) so a <select multiple>/[size] still grows.

   This is NOT the "global control-height normalisation" the FIX-2 note rejects:
   that one would DROP fields to 35.9px and collide with the app's 47px buttons.
   This RAISES select to the height Pico itself already gives <input>, which is
   what those 47px buttons match. The three scopes FIX-2 deliberately un-pins are
   restored immediately below — there a select must stay ~35.9px beside a
   .ol-combobox__trigger, and min-height would otherwise beat FIX-2's height:auto. */
select {
  min-height: calc(1rem * var(--pico-line-height, 1.5) + var(--pico-form-element-spacing-vertical, 0.75rem) * 2 + var(--pico-border-width, 0.0625rem) * 2);
}

/* …and released again exactly where FIX-2 un-pins the box (0,1,1 beats the bare
   `select` above at 0,0,1, so this wins regardless of source order). Without
   this, the parity rule would re-inflate the very fields FIX-2 shrank to match
   the Combobox trigger — reintroducing the gap it exists to close. */
.filter-row select,
.settings-invite-row select,
.modal-body form select {
  min-height: auto;
}

/* A-06 — an explicit `.m-0` utility on a form control must beat Pico's per-element
   margin-bottom. The class already sets `margin:0` (0,1,0) which out-specifies
   Pico's element rule (0,0,1), but re-assert at 0,1,1 so it also wins over any
   later element-level margin (e.g. the company-modal "Adicionar" button was still
   hanging ~15px below its inputs). */
input.m-0, select.m-0, textarea.m-0 { margin-bottom: 0; }

/* Active-only / display toggles, left-aligned above the selectors so they
   don't sit in a filter-grid column and read as misaligned. */
.filter-toggles {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin-bottom: 0.85rem;
}

/* Toggle switch — an iOS-style on/off control built on a visually-hidden
   checkbox. The native input drives state + accessibility; the `.switch__track`
   sibling renders the pill + knob. Used for the "Apenas ativos" worker filter. */
.switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  white-space: nowrap;
  font-weight: 500;
  user-select: none;
}
.switch input[type="checkbox"] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
}
.switch__track {
  position: relative;
  flex: none;
  width: 34px;
  height: 18px;
  border-radius: 999px;
  background: var(--ol-gray-300, #cbd5e1);
  transition: background 0.15s ease;
}
.switch__track::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 0.15s ease;
}
.switch input[type="checkbox"]:checked + .switch__track {
  background: var(--ol-blue, #2563eb);
}
.switch input[type="checkbox"]:checked + .switch__track::after {
  transform: translateX(16px);
}
.switch input[type="checkbox"]:focus-visible + .switch__track {
  outline: 2px solid var(--ol-blue, #2563eb);
  outline-offset: 2px;
}
.switch input[type="checkbox"]:disabled + .switch__track {
  opacity: 0.5;
}

/* Split view for validation */
.split-view {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
  min-height: 70vh;
}

/* Document-classification group (Validation form): boxes the type selector,
   its "Outro" cascade, the "Documento equivalente" modifier and the
   re-extract action together as one unit, and divides them from the read-only
   Subempreiteiro/Obra block below so the controls no longer run into it. */
.doc-classify-group {
  padding: 0.75rem 0.85rem 0.85rem;
  margin-bottom: 1.25rem;
  border: 1px solid var(--ol-gray-200, #e5e7eb);
  border-radius: 8px;
  background: #fff;
}
/* "Documento equivalente" sits snug under the type selector it modifies. */
.doc-classify-group .doc-classify-equiv {
  margin-top: 0.4rem;
}

/* Compliance badge */
.compliance-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 99px;
  font-weight: 700;
  font-size: 0.8rem;
  color: #fff;
  min-width: 48px;
  text-align: center;
}

.compliance-badge--green { background-color: var(--ol-green); }
.compliance-badge--yellow { background-color: var(--ol-yellow); }
.compliance-badge--red { background-color: var(--ol-red); }
/* WA-B19: neutral "Por configurar" — entity has zero required doc codes. */
.compliance-badge--gray { background-color: var(--ol-gray-500); color: #fff; }

/* Non-colour status cue: a distinct ASCII prefix per severity so a
   colour-blind user gets the signal even if the hue is indistinguishable —
   conforme has no mark, caution is "!", não-conforme is "X". */
.compliance-badge::before {
  margin-right: 4px;
  font-weight: 700;
  display: inline-block;
  width: 0.75em;
  text-align: center;
}
.compliance-badge--green::before { content: ''; }
.compliance-badge--yellow::before { content: '\21'; }    /* ! */
.compliance-badge--red::before { content: '\58'; }      /* X — não conforme */
.compliance-badge--gray::before { content: '\2014'; }    /* — em dash */

/* approved-counts-compliant: the "tem avisos" marker rendered BESIDE a
   compliance badge (when an approved/counted document still carries validation
   warnings) now uses the shared `.flag-line .flag-line--yellow` dot+text style
   — the same visual language as every other status flag — so the old bespoke
   `.compliance-flagged-chip` amber pill has been removed. */

/* Collapsible upload toggle */
/* S6 — "Carregar Documentos" is the primary dashboard action, so it gets the
   filled primary (navy) treatment instead of the old ghost/outline button. */
.upload-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  margin-bottom: 1.25rem;
  background: var(--ol-primary);
  border: 1.5px solid var(--ol-primary);
  color: #fff;
  font-weight: 600;
  font-size: 0.875rem;
  cursor: pointer;
  border-radius: 8px;
  transition: all 0.15s;
}

.upload-toggle:hover {
  border-color: var(--ol-primary-hover);
  background: var(--ol-primary-hover);
  color: #fff;
}

.upload-toggle svg {
  transition: transform 0.2s;
}

.upload-toggle--open svg {
  transform: rotate(45deg);
}

/* Dashboard stacked cell styles */
.cell-primary {
  font-weight: 600;
}

.cell-secondary {
  font-size: 0.8rem;
  color: var(--ol-gray-500);
  margin-top: 2px;
}

.cell-nif {
  font-family: monospace;
  font-size: 0.8rem;
  color: var(--ol-gray-500);
  margin-top: 2px;
}

.cell-missing {
  font-size: 0.8rem;
  color: var(--ol-gray-500);
  margin-top: 2px;
}

/* --- Responsive table → card pattern --- */

.mobile-filter-toggle {
  display: none;
}

.upload-zone-camera {
  display: none;
}

/* Google Drive import button */
.google-drive-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 1rem;
  margin-top: 0.5rem;
  background: #fff;
  color: var(--ol-gray-700, #374151);
  border: 1px solid var(--ol-gray-300, #d1d5db);
  border-radius: 8px;
  font-size: 0.85rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.google-drive-btn:hover:not(:disabled) {
  background: var(--ol-gray-50, #f9fafb);
  border-color: var(--ol-gray-400, #9ca3af);
}

.google-drive-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.google-drive-btn svg {
  flex-shrink: 0;
}

/* --- Responsive: Mobile ---
   UA-P2-8: 11 nav items + tablet 240px sidebar are too cramped at phone
   widths (375px → 64% of viewport). The earlier bottom-bar approach broke
   tap targets below 480px (11 items × 34px each). Replaced with a slide-in
   drawer toggled by a hamburger button anchored top-left of the viewport. */

.mobile-nav-toggle {
  display: none;
  position: fixed;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 250;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  background: var(--ol-primary);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 8px;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  padding: 0;
}

.mobile-nav-toggle:hover {
  background: var(--ol-primary-dark, var(--ol-primary));
}

.mobile-nav-toggle:focus-visible {
  outline: 3px solid var(--ol-accent);
  outline-offset: 2px;
}

.mobile-nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 150;
}

@media (max-width: 768px) {
  .app-layout {
    display: block;
  }

  /* Tab bars (Dashboard explorer, Validação, Definições, Gestão…) hold up to
     five nowrap tabs. On narrow phones the row overflowed off-screen because
     the container clipped nothing and the tabs never shrink. Make the row
     horizontally scrollable instead of wrapping — wrapping would break the
     single underline and change the bar height. Hide the scrollbar; momentum
     scroll on touch. */
  .tabs {
    overflow-x: auto;
    flex-wrap: nowrap;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }

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

  .tabs__tab {
    flex: 0 0 auto;
  }

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

  .mobile-nav-backdrop {
    display: block;
  }

  .sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(280px, 80vw);
    height: 100vh;
    z-index: 200;
    transform: translateX(-100%);
    /* B10-F2 (bughunt0713): translateX(-100%) alone only hides the drawer
       VISUALLY — its ⌘K pill, 9 nav links and profile button stayed fully
       tabbable at x=-269 (11 invisible focus stops before the first real page
       control, and Enter on one navigated "spontaneously"). visibility:hidden
       removes the closed drawer from the tab order and the a11y tree; the 0.2s
       transition-delay keeps the slide-OUT animation visible before it hides. */
    visibility: hidden;
    transition: transform 0.2s ease-out, visibility 0s linear 0.2s;
    box-shadow: var(--shadow-md);
  }

  .sidebar--open {
    transform: translateX(0);
    /* Become visible immediately (no delay) so the slide-IN is seen. */
    visibility: visible;
    transition: transform 0.2s ease-out, visibility 0s linear 0s;
  }

  .sidebar-logo {
    padding: 1.25rem 1.25rem 1.5rem 3.5rem; /* leave room for the hamburger */
  }

  .main-content {
    padding: 3.5rem 1rem 2rem;
    max-width: 100%;
  }

  /* The skip-to-main link normally pops to top:0 left:0 on focus, which
     overlaps the hamburger button. Push it past the hamburger on mobile so
     focus stays visible. */
  .skip-to-main:focus,
  .skip-to-main:focus-visible {
    left: 3.5rem;
  }

  /* SupportWidget's FAB sits at z-index 900 — above the drawer (200) and the
     backdrop (150) — so it pierces the modal-ish drawer overlay. Hide it
     while the drawer is open. */
  .app-layout--nav-open .support-fab {
    display: none;
  }

  .filter-row { grid-template-columns: 1fr; }
  .split-view { grid-template-columns: 1fr; }
  .stats-grid { grid-template-columns: 1fr 1fr; }

  /* --- Responsive table → card layout --- */

  table.responsive-cards {
    border-collapse: separate;
    border-spacing: 0 0.5rem;
  }

  table.responsive-cards thead {
    display: none;
  }

  table.responsive-cards tbody tr {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.25rem 0.75rem;
    padding: 0.75rem;
    margin-bottom: 0.5rem;
    border: 1px solid var(--ol-gray-200);
    border-radius: 8px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  }

  table.responsive-cards tbody tr td {
    padding: 0;
    border: none;
  }

  /* Worker cards: 4 columns → 2x2 grid */
  table.responsive-cards.cards-worker tbody tr td:nth-child(1) { grid-column: 1; grid-row: 1; }
  table.responsive-cards.cards-worker tbody tr td:nth-child(2) { grid-column: 1; grid-row: 2; font-size: 0.85rem; }
  table.responsive-cards.cards-worker tbody tr td:nth-child(3) { grid-column: 2; grid-row: 2; text-align: right; }
  table.responsive-cards.cards-worker tbody tr td:nth-child(4) { grid-column: 2; grid-row: 1; text-align: right; }

  /* Relatórios worker cards carry 2 extra columns (Autorizado + Motivo) plus
     the "Em Falta" cell (5). Stack the trailing cells full-width below the 2×2
     head and label them — a bare "Sim/Não" or reason text would otherwise float
     in the card with no heading once the <thead> is hidden on mobile. */
  table.responsive-cards.cards-worker-auth tbody tr td:nth-child(5),
  table.responsive-cards.cards-worker-auth tbody tr td:nth-child(6),
  table.responsive-cards.cards-worker-auth tbody tr td:nth-child(7) {
    grid-column: 1 / -1;
    grid-row: auto;
    text-align: left;
    font-size: 0.85rem;
  }
  table.responsive-cards.cards-worker-auth tbody tr td[data-label]::before {
    content: attr(data-label) ': ';
    font-weight: 600;
    color: var(--ol-gray-500);
  }

  /* Company cards: 3 columns → 2 rows */
  table.responsive-cards.cards-company tbody tr td:nth-child(1) { grid-column: 1; grid-row: 1; }
  table.responsive-cards.cards-company tbody tr td:nth-child(2) { grid-column: 1; grid-row: 2; }
  table.responsive-cards.cards-company tbody tr td:nth-child(3) { grid-column: 2; grid-row: 1 / 3; text-align: right; align-self: center; }

  /* popup-workers-mobile-cards — the empresa detail's Trabalhadores tab
     (CompanyDetailsPanel, also inside the entity popup): 5 columns
     (Nome / Estado / Documentos / Em falta / Conformidade) → a 2×2 head with
     the missing-docs list full-width below. Without this the table kept its
     desktop columns on phones: content pushed off-screen, rows stretched to
     ~300px, and the whole tab scrolled horizontally. */
  table.responsive-cards.cards-popup-workers tbody tr td:nth-child(1) {
    grid-column: 1; grid-row: 1;
    /* A long worker name must wrap inside the card, not widen it. */
    overflow-wrap: anywhere;
  }
  table.responsive-cards.cards-popup-workers tbody tr td:nth-child(2) { grid-column: 2; grid-row: 1; text-align: right; }
  table.responsive-cards.cards-popup-workers tbody tr td:nth-child(3) { grid-column: 1; grid-row: 2; font-size: 0.85rem; }
  table.responsive-cards.cards-popup-workers tbody tr td:nth-child(5) { grid-column: 2; grid-row: 2; text-align: right; }
  table.responsive-cards.cards-popup-workers tbody tr td:nth-child(4) {
    grid-column: 1 / -1; grid-row: 3;
    text-align: left;
    font-size: 0.85rem;
    overflow-wrap: anywhere;
  }
  /* Label the red missing-list ("Em falta: …") — the thead is hidden, so bare
     doc-type names would float unlabelled. The td carries data-label ONLY in
     that branch (not for "Tudo em dia" / the self-labelled validade line). */
  table.responsive-cards.cards-popup-workers tbody tr td[data-label]::before {
    content: attr(data-label) ': ';
    font-weight: 600;
    color: var(--ol-gray-500);
    font-size: 0.85rem;
  }

  /* Equipment cards: 5 columns → grid */
  table.responsive-cards.cards-equip tbody tr {
    grid-template-columns: 1fr 1fr;
  }
  table.responsive-cards.cards-equip tbody tr td:nth-child(1) { grid-column: 1 / 3; grid-row: 1; }
  table.responsive-cards.cards-equip tbody tr td:nth-child(2) { grid-column: 1; grid-row: 2; font-size: 0.85rem; }
  table.responsive-cards.cards-equip tbody tr td:nth-child(3) { grid-column: 2; grid-row: 2; font-size: 0.85rem; }
  table.responsive-cards.cards-equip tbody tr td:nth-child(4) { grid-column: 1; grid-row: 3; }
  table.responsive-cards.cards-equip tbody tr td:nth-child(5) { grid-column: 2; grid-row: 3; text-align: right; }

  /* Expanded rows span full width */
  table.responsive-cards tbody tr td[colspan] {
    grid-column: 1 / -1;
    grid-row: auto;
    padding: 0;
  }

  /* Expanded row itself */
  table.responsive-cards tbody tr:has(td[colspan]) {
    display: block;
    padding: 0;
    margin-top: -0.75rem;
    border-top: none;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    box-shadow: none;
  }

  /* Mobile filter toggle */
  .mobile-filter-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.6rem 1rem;
    margin-bottom: 1rem;
    background: var(--ol-gray-50);
    border: 1px solid var(--ol-gray-200);
    border-radius: 8px;
    color: var(--ol-gray-600);
    font-weight: 500;
    font-size: 0.875rem;
    cursor: pointer;
  }

  .mobile-filter-toggle svg {
    transition: transform 0.2s;
  }

  .mobile-filter-toggle.open svg {
    transform: rotate(180deg);
  }

  .filter-row.collapsed {
    display: none;
  }

  /* Camera upload button on mobile */
  .upload-zone-camera {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.75rem;
    margin-top: 0.5rem;
    background: var(--ol-primary);
    color: #fff;
    border: none;
    border-radius: 8px;
    font-weight: 600;
    font-size: 0.9rem;
    cursor: pointer;
  }

  .upload-zone-camera:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  /* uipolish7 C-08: on mobile the camera button goes full-width navy while the
     Google Drive button kept its compact desktop inline sizing — the pair read
     ragged. Match the Drive button to the camera's full-width, centred box so
     the two stack as an even pair (primary navy + white outline). */
  .google-drive-btn {
    display: flex;
    width: 100%;
    justify-content: center;
    padding: 0.75rem;
  }

  /* Validation page table cards */
  table.responsive-cards.cards-validation tbody tr td:nth-child(1) { grid-column: 1; grid-row: 1; }
  table.responsive-cards.cards-validation tbody tr td:nth-child(2) { grid-column: 2; grid-row: 1; text-align: right; }
  table.responsive-cards.cards-validation tbody tr td:nth-child(3) { grid-column: 1 / 3; grid-row: 2; }
}

@media (max-width: 480px) {
  .stats-grid { grid-template-columns: 1fr; }
}

/* --- Settings page --- */

.settings-tabs {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 1.5rem;
  border-bottom: 2px solid var(--ol-gray-200);
  padding-bottom: 0;
  overflow-x: visible;
  overflow-y: visible;
}

.settings-tab {
  border: none;
  border-bottom: 3px solid transparent;
  border-radius: 0;
  background: none !important;
  color: var(--ol-gray-500);
  font-weight: 500;
  font-size: 0.9rem;
  padding: 0.6rem 1.1rem;
  margin-bottom: -2px;
  transition: color 0.15s, border-color 0.15s;
  white-space: nowrap;
  cursor: pointer;
  outline: none;
  box-shadow: none;
  --pico-background-color: transparent;
  --pico-border-color: transparent;
  --pico-color: var(--ol-gray-500);
}

.settings-tab:hover {
  color: var(--ol-gray-700);
  background: none !important;
  --pico-background-color: transparent;
}

.settings-tab:focus-visible {
  /* a11y: was outline:none + faint turquoise halo — see .tabs__tab note */
  outline: 2px solid var(--ol-primary, #030531);
  outline-offset: -2px;
  box-shadow: none;
  border-radius: 4px 4px 0 0;
}

.settings-tab.active {
  color: var(--ol-primary);
  font-weight: 600;
  border-bottom-color: var(--ol-accent);
  background: none !important;
  --pico-background-color: transparent;
  --pico-color: var(--ol-primary);
}

.settings-panel {
  max-width: 640px;
  font-size: 0.9rem;
}

.settings-panel h3 {
  font-size: 1.05rem;
  margin-bottom: 0.5rem;
}

.settings-form label {
  display: block;
  margin-bottom: 1rem;
  font-size: 0.9rem;
}

.settings-form label small {
  display: block;
  font-size: 0.85rem;
  color: var(--ol-gray-500);
  margin-bottom: 0.25rem;
}

.settings-form p {
  font-size: 0.9rem;
}

.settings-form button {
  font-size: 0.9rem;
}

.settings-actions {
  display: flex;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--ol-gray-200);
}

.settings-invite-row {
  display: flex;
  gap: 0.5rem;
  /* D-02: align to the TOP, not the bottom — when an inline field error (e.g.
     "Email inválido") grows the input's wrapper, flex-end shoved the Papel
     selector + Convidar button ~20px down. flex-start pins them to the first
     line so the error just extends below. */
  align-items: flex-start;
  margin-bottom: 0.5rem;
}

.settings-invite-row input,
.settings-invite-row select {
  margin: 0;
}

.settings-users-table {
  width: 100%;
  font-size: 0.9rem;
}

.settings-users-table th {
  text-align: left;
  font-weight: 600;
  font-size: 0.8rem;
  color: var(--ol-gray-500);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.5rem 0.75rem;
  border-bottom: 2px solid var(--ol-gray-200);
}

.settings-users-table td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--ol-gray-100);
  vertical-align: middle;
}

.settings-you-badge {
  display: inline-block;
  margin-left: 0.5rem;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--ol-accent);
  background: var(--ol-primary);
  padding: 1px 6px;
  border-radius: 99px;
}

.settings-toggle-row {
  display: flex !important;
  align-items: flex-start;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}

.settings-toggle-row input[type="checkbox"] {
  margin-top: 0.2rem;
  flex-shrink: 0;
}

.settings-toggle-row div {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.settings-toggle-row strong {
  font-size: 0.95rem;
}

.settings-toggle-row small {
  font-size: 0.8rem;
  color: var(--ol-gray-500);
}

/* Switch toggle rows: only the switch itself flips state. The whole row is a
   <label>, so clicking the title/description text used to toggle the switch —
   surprising for a card-sized hit area. Make the label box + text inert and let
   only the switch (and any nested link/button, e.g. a footnote) take clicks.
   Scoped to rows that contain a role="switch" input, so single-select RADIO rows
   (e.g. "Modo de envio de emails") keep their conventional full-row click. */
.settings-toggle-row:has(> input[role="switch"]) {
  cursor: default;
  pointer-events: none;
}
.settings-toggle-row:has(> input[role="switch"]) > input[role="switch"] {
  pointer-events: auto;
  cursor: pointer;
}
.settings-toggle-row:has(> input[role="switch"]) > input[role="switch"]:disabled {
  cursor: not-allowed;
}
.settings-toggle-row:has(> input[role="switch"]) :is(a, button) {
  pointer-events: auto;
}

/* Tertiary / disclosure buttons — subtle text-style actions that must NOT read
   as primary navy buttons (e.g. "Opções avançadas ▸", "Restaurar predefinições").
   Opted out of the global filled-button rule above. */
button.btn-link {
  width: auto;
  background: none;
  border: none;
  padding: 0.15rem 0.3rem;
  margin: 0;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--ol-primary);
  border-radius: 4px;
}
button.btn-link:hover {
  background: var(--ol-gray-100);
  color: var(--ol-primary-hover);
}

/* Alertas tab structure ------------------------------------------------------
   Master switch: a top-level header row, label flush-left and the switch on the
   right (no left indentation from a leading checkbox). */
.settings-master-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1rem;
  margin-bottom: 1.5rem;
  background: var(--ol-gray-100);
  border: 1px solid var(--ol-gray-200);
  border-radius: 8px;
}
.settings-master-toggle > div {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.settings-master-toggle strong { font-size: 1rem; }
.settings-master-toggle small {
  font-size: 0.8rem;
  color: var(--ol-gray-500);
}
.settings-master-toggle .switch-only {
  margin: 0;
  flex-shrink: 0;
  cursor: pointer;
}
.settings-master-toggle .switch-only input[role="switch"] { cursor: pointer; }

/* Section heading that groups related alerts under one label. */
.settings-section-heading {
  margin: 0 0 0.25rem;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ol-gray-500);
}
.settings-alert-group { margin-bottom: 1.75rem; }
.settings-section-help {
  margin: 0 0 0.75rem;
  font-size: 0.8rem;
  color: var(--ol-gray-500);
}
.settings-sendmode-fieldset {
  border: none;
  padding: 0;
  margin: 0 0 0.5rem;
}
.settings-sendmode-row { margin-bottom: 0.5rem !important; }

/* AlertCard body: send-mode + advanced options, indented under the alert's
   toggle so the "Opções avançadas" disclosure clearly belongs to the card. */
.alert-card__body {
  margin-top: 0.75rem;
  /* uipolish7 D-08 (CSS half): the alert title text sits past the leading
     role="switch" (~2.25em) + 0.75rem gap of `.settings-toggle-row`. The body
     block was left-outdented against that title column (rail hugging the card
     edge, content ~2rem left of the title). Shift the whole block — rail
     included — right so it lines up under the sub-alert text column. */
  margin-left: 1.85rem;
  padding-left: 1rem;
  border-left: 2px solid var(--ol-gray-200);
}

/* Segmented weekday toggle ("Dias da semana" in alert schedules). Previously
   reused the .badge dot+text component, which rendered a meaningless colour dot
   and — via the global button rule — a navy fill with no real pressed state.
   Real toggle: outline when off, filled navy when aria-pressed="true". */
button.day-toggle {
  width: auto;
  min-width: 2.1rem;
  padding: 0.2rem 0.4rem;
  font-size: 0.75rem;
  font-weight: 600;
  border: 1.5px solid var(--ol-gray-300);
  background: #fff;
  color: var(--ol-gray-600);
  border-radius: 6px;
  /* Unselected days are visibly dimmed so the on/off state reads at a glance,
     not just by fill colour (the selected rule below resets to 1). */
  opacity: 0.45;
}
button.day-toggle:hover {
  border-color: var(--ol-primary);
  color: var(--ol-primary);
  opacity: 1;
}
button.day-toggle[aria-pressed="true"] {
  background: var(--ol-primary);
  border-color: var(--ol-primary);
  color: #fff;
  opacity: 1;
}

/* Removable value chip (e.g. "dias a ignorar"). Replaces a misuse of .badge
   (status dot+text) whose inner × button inherited the navy filled-button rule. */
.settings-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  padding: 0.15rem 0.2rem 0.15rem 0.55rem;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--ol-gray-700);
  background: var(--ol-gray-100);
  border-radius: 99px;
}
.settings-chip button {
  width: auto;
  background: none;
  border: none;
  padding: 0 0.3rem;
  margin: 0;
  font-size: 1rem;
  line-height: 1;
  color: var(--ol-gray-500);
  border-radius: 99px;
}
.settings-chip button:hover:not(:disabled) {
  background: var(--ol-gray-200);
  color: var(--ol-red);
}
.settings-chip button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Editable-field affordance (P16): text-like inputs/selects/textareas across all
   Settings tabs get a resting border + hover/focus hint so they don't read as
   display-only. Positive type allow-list so non-text inputs (checkbox/radio/range/
   color/file) keep their native look; :not(:disabled) so disabled fields (e.g. the
   cadence inputs when the alerts master toggle is off) stay muted via Pico's default
   and don't gain a false editable affordance. App is light-only (body + cards are
   hard-coded light, no prefers-color-scheme), so #fff matches surrounding wells. */
.settings-panel input:is([type="text"], [type="email"], [type="number"], [type="date"], [type="time"], [type="tel"], [type="url"], [type="password"], [type="search"]):not(:disabled),
.settings-panel select:not(:disabled),
.settings-panel textarea:not(:disabled) {
  border: 1px solid var(--ol-gray-300);
  background-color: #fff;
}

.settings-panel input:is([type="text"], [type="email"], [type="number"], [type="date"], [type="time"], [type="tel"], [type="url"], [type="password"], [type="search"]):not(:disabled):hover,
.settings-panel select:not(:disabled):hover,
.settings-panel textarea:not(:disabled):hover {
  border-color: var(--ol-gray-400);
}

/* Keep the turquoise focus border (the resting rule above out-specifies the global
   input:focus, so re-assert the accent border at focus). */
.settings-panel input:is([type="text"], [type="email"], [type="number"], [type="date"], [type="time"], [type="tel"], [type="url"], [type="password"], [type="search"]):not(:disabled):focus,
.settings-panel select:not(:disabled):focus,
.settings-panel textarea:not(:disabled):focus {
  border-color: var(--ol-accent);
}

@media (max-width: 768px) {
  .settings-panel { max-width: 100%; }
  .settings-invite-row { flex-direction: column; align-items: stretch; }
  .settings-invite-row select { width: 100% !important; }
}

/* --- Activity feed --- */

.activity-feed {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.activity-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--ol-gray-100);
  font-size: 0.875rem;
}

.activity-row:last-child {
  border-bottom: none;
}

.activity-row-main {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
  min-width: 0;
}

.activity-row-meta {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  flex-shrink: 0;
  white-space: nowrap;
}

.activity-action {
  font-weight: 600;
  font-size: 0.8rem;
  padding: 1px 6px;
  border-radius: 4px;
}

.activity-action--uploaded { color: var(--ol-primary); background: rgba(3, 5, 49, 0.06); }
.activity-action--verified { color: var(--ol-green); background: var(--ol-green-bg); }
.activity-action--rejected { color: var(--ol-red); background: var(--ol-red-bg); }
.activity-action--created { color: var(--ol-blue); background: var(--ol-blue-bg); }
.activity-action--updated { color: var(--ol-purple); background: var(--ol-purple-bg); }
.activity-action--merged { color: var(--ol-amber); background: var(--ol-amber-bg); }
.activity-action--assigned { color: var(--ol-green); background: var(--ol-green-50); }
.activity-action--unassigned { color: var(--ol-gray-500); background: var(--ol-gray-100); }

.activity-entity {
  font-weight: 500;
  color: var(--ol-gray-800);
}

a.activity-entity {
  text-decoration: none;
}

a.activity-entity:hover {
  text-decoration: underline;
  color: var(--ol-primary);
}

.activity-detail {
  color: var(--ol-gray-500);
  font-size: 0.8rem;
}

.activity-user {
  color: var(--ol-gray-500);
  font-size: 0.8rem;
}

.activity-time {
  color: var(--ol-gray-400);
  font-size: 0.75rem;
  min-width: 2.5rem;
  text-align: right;
}

/* --- Progress steps --- */

.progress-steps {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.progress-step {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.8rem;
  color: var(--ol-gray-400);
  font-weight: 500;
}

.progress-step--active {
  color: var(--ol-primary);
  font-weight: 600;
}

.progress-step--done {
  color: var(--ol-green);
}

.progress-step__connector {
  width: 1.5rem;
  height: 2px;
  background: var(--ol-gray-200);
}

.progress-step__connector--done {
  background: var(--ol-green);
}

/* --- Inline form validation --- */

.field-error {
  color: var(--ol-red);
  font-size: 0.8rem;
  margin-top: 0.25rem;
}

/* --- Pagination controls --- */

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 0;
  font-size: 0.85rem;
  color: var(--ol-gray-500);
  flex-wrap: wrap;
}

.pagination button {
  padding: 0.3rem 0.75rem;
  font-size: 0.8rem;
  border-radius: 6px;
  line-height: 1.3;
  min-height: unset;
  height: auto;
}

.pagination span {
  font-size: 0.85rem;
  color: var(--ol-gray-500);
  white-space: nowrap;
  padding: 0 0.25rem;
}

/* combobox-unify: the page-size selector is now a <Combobox> (a button, not a
   <select>); keep it compact + auto-width inside the pagination row. */
.pagination .ol-combobox { width: auto; }
.pagination .ol-combobox__trigger {
  padding: 0.25rem 0.5rem;
  font-size: 0.8rem;
}
.pagination select {
  padding: 0.25rem 0.5rem;
  font-size: 0.8rem;
  border-radius: 6px;
  width: auto;
  min-height: unset;
  height: auto;
  margin: 0;
}

/* --- Sortable table headers --- */

th.sortable {
  cursor: pointer;
  user-select: none;
}

th.sortable:hover {
  color: var(--ol-gray-700);
}

th.sortable .sort-indicator {
  margin-left: 0.25rem;
  font-size: 0.7rem;
  opacity: 0.5;
}

th.sortable[aria-sort] .sort-indicator {
  opacity: 1;
}

/* --- Mobile filter count --- */

.mobile-filter-count {
  margin-left: auto;
  background: var(--ol-accent);
  color: var(--ol-primary);
  font-size: 0.7rem;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 99px;
}

/* --- Inline form alignment fixes (Pico reset) ---
   Pico adds margin-bottom to inputs, selects, buttons, and labels.
   These resets ensure proper vertical alignment in inline/flex layouts. */

/* Small buttons used inline with text */
.btn-sm {
  padding: 0.2rem 0.6rem;
  font-size: 0.8rem;
  line-height: 1.3;
  min-height: unset;
  height: auto;
  margin: 0;
  vertical-align: middle;
}

/* Inline select inside label (e.g. verification interval) */
label select[style*="width: auto"],
label select[style*="width:auto"] {
  margin-bottom: 0;
  min-height: unset;
  height: auto;
  vertical-align: middle;
}

/* Flex row with label + button at bottom (e.g. "Adicionar sub", "Atribuir equip").
   Both the inline-style form (legacy) and the utility-class form (.flex.items-end)
   need the same Pico-margin reset. */
div[style*="align-items: flex-end"] > label,
.flex.items-end > label,
.flex.flex-wrap.items-end > label {
  margin-bottom: 0;
}

div[style*="align-items: flex-end"] > label input,
div[style*="align-items: flex-end"] > label select,
.flex.items-end > label input,
.flex.items-end > label select,
.flex.flex-wrap.items-end > label input,
.flex.flex-wrap.items-end > label select {
  margin-bottom: 0;
}

div[style*="align-items: flex-end"] > button,
div[style*="align-items: flex-end"] > div > button,
.flex.items-end > button,
.flex.items-end > div > button,
.flex.flex-wrap.items-end > button,
.flex.flex-wrap.items-end > div > button {
  margin-bottom: 0;
  min-height: unset;
  height: auto;
}

/* --- Email detail panel --- */

.email-detail {
  background-color: var(--ol-gray-50);
  border-left: 4px solid var(--ol-accent);
  animation: expandRow 0.2s ease-out;
}

.email-detail__header {
  padding: 0.5rem 1rem;
  font-size: 0.85rem;
  color: var(--ol-gray-500);
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  align-items: center;
}

.email-detail__contact {
  font-size: 0.85rem;
  color: var(--ol-gray-500);
}

.email-detail__inbound-meta {
  font-size: 0.85rem;
  color: var(--ol-gray-500);
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

.email-detail__body {
  padding: 0 1rem 1rem;
  max-height: 60vh;
  overflow: auto;
}

.email-detail__body iframe {
  border-radius: 4px;
  background: #fff;
}

/* --- Compact document count --- */

.doc-count {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ol-gray-500);
}

.doc-count--complete {
  color: var(--ol-green);
}

.doc-count--empty {
  color: var(--ol-gray-400);
}

/* Column-header legend for the three-number "Documentos" pill
   (válidos / ficheiros / obrigatórios) — small, muted, on its own line. */
.th-legend {
  display: block;
  font-size: 0.68rem;
  font-weight: 500;
  color: var(--ol-gray-500);
  text-transform: none;
  letter-spacing: 0;
  margin-top: 0.1rem;
}

/* --- Reply status (email log) --- */

.reply-status { font-size: 0.8rem; font-weight: 500; }
.reply-status--replied { color: var(--ol-green); }
.reply-status--early { color: var(--ol-gray-500); }
.reply-status--warning { color: var(--ol-yellow); }
.reply-status--late { color: var(--ol-red); }

/* ============================================================
   Utility classes — prefer these over ad-hoc inline style={{}}.
   See UI_UX_FINDINGS.md §S1.
   ============================================================ */

/* --- Layout --- */
.flex { display: flex; }
.flex-col { display: flex; flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }
.flex-1 { flex: 1; min-width: 0; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }
.items-baseline { align-items: baseline; }
.justify-start { justify-content: flex-start; }
.justify-between { justify-content: space-between; }
.justify-end { justify-content: flex-end; }
.justify-center { justify-content: center; }
.justify-around { justify-content: space-around; }
/* Display utilities (U1 iter 8) */
.inline-flex { display: inline-flex; }
.grid { display: grid; }
.inline-block { display: inline-block; }
.block { display: block; }
.hidden { display: none; }
.gap-1 { gap: var(--sp-1); }
.gap-2 { gap: var(--sp-2); }
.gap-3 { gap: var(--sp-3); }
.gap-4 { gap: var(--sp-4); }

/* --- Spacing ---
 * Source order matters: with equal specificity, later wins.
 * Order from broad → narrow so directional classes win when combined
 * with shorthand (Tailwind convention). E.g. `m-0 mb-2` produces
 * `margin: 0; margin-bottom: 0.5rem;` (U1 iter 13 fix; previously the
 * shorthand was source-after-directional and silently clobbered mb-*).
 */
/* 4-sides shorthand */
.m-0 { margin: 0; }
.m-1 { margin: var(--sp-1); }
.m-2 { margin: var(--sp-2); }
.m-3 { margin: var(--sp-3); }
.m-4 { margin: var(--sp-4); }
.m-5 { margin: var(--sp-5); }
.m-6 { margin: var(--sp-6); }

/* Axis-pair shorthand */
.my-1 { margin-top: var(--sp-1); margin-bottom: var(--sp-1); }
.my-2 { margin-top: var(--sp-2); margin-bottom: var(--sp-2); }
.my-3 { margin-top: var(--sp-3); margin-bottom: var(--sp-3); }
.my-4 { margin-top: var(--sp-4); margin-bottom: var(--sp-4); }
.my-5 { margin-top: var(--sp-5); margin-bottom: var(--sp-5); }
.my-6 { margin-top: var(--sp-6); margin-bottom: var(--sp-6); }
.mx-1 { margin-left: var(--sp-1); margin-right: var(--sp-1); }
.mx-2 { margin-left: var(--sp-2); margin-right: var(--sp-2); }
.mx-3 { margin-left: var(--sp-3); margin-right: var(--sp-3); }
.mx-4 { margin-left: var(--sp-4); margin-right: var(--sp-4); }
.mx-5 { margin-left: var(--sp-5); margin-right: var(--sp-5); }
.mx-6 { margin-left: var(--sp-6); margin-right: var(--sp-6); }
.mx-auto { margin-left: auto; margin-right: auto; }

/* Single-side */
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: var(--sp-1); }
.mt-2 { margin-top: var(--sp-2); }
.mt-3 { margin-top: var(--sp-3); }
.mt-4 { margin-top: var(--sp-4); }
.mt-5 { margin-top: var(--sp-5); }
.mt-6 { margin-top: var(--sp-6); }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: var(--sp-1); }
.mb-2 { margin-bottom: var(--sp-2); }
.mb-3 { margin-bottom: var(--sp-3); }
.mb-4 { margin-bottom: var(--sp-4); }
.mb-5 { margin-bottom: var(--sp-5); }
.mb-6 { margin-bottom: var(--sp-6); }
.ml-0 { margin-left: 0; }
.ml-1 { margin-left: var(--sp-1); }
.ml-2 { margin-left: var(--sp-2); }
.ml-3 { margin-left: var(--sp-3); }
.ml-4 { margin-left: var(--sp-4); }
.ml-5 { margin-left: var(--sp-5); }
.ml-6 { margin-left: var(--sp-6); }
.mr-0 { margin-right: 0; }
.mr-1 { margin-right: var(--sp-1); }
.mr-2 { margin-right: var(--sp-2); }
.mr-3 { margin-right: var(--sp-3); }
.mr-4 { margin-right: var(--sp-4); }
.mr-5 { margin-right: var(--sp-5); }
.mr-6 { margin-right: var(--sp-6); }

/* Gap scale completion */
.gap-5 { gap: var(--sp-5); }
.gap-6 { gap: var(--sp-6); }

/* Padding utilities */
.p-0 { padding: 0; }
.p-1 { padding: var(--sp-1); }
.p-2 { padding: var(--sp-2); }
.p-3 { padding: var(--sp-3); }
.p-4 { padding: var(--sp-4); }
.p-5 { padding: var(--sp-5); }
.p-6 { padding: var(--sp-6); }
.px-0 { padding-left: 0; padding-right: 0; }
.px-1 { padding-left: var(--sp-1); padding-right: var(--sp-1); }
.px-2 { padding-left: var(--sp-2); padding-right: var(--sp-2); }
.px-3 { padding-left: var(--sp-3); padding-right: var(--sp-3); }
.px-4 { padding-left: var(--sp-4); padding-right: var(--sp-4); }
.px-5 { padding-left: var(--sp-5); padding-right: var(--sp-5); }
.px-6 { padding-left: var(--sp-6); padding-right: var(--sp-6); }
.py-0 { padding-top: 0; padding-bottom: 0; }
.py-1 { padding-top: var(--sp-1); padding-bottom: var(--sp-1); }
.py-2 { padding-top: var(--sp-2); padding-bottom: var(--sp-2); }
.py-3 { padding-top: var(--sp-3); padding-bottom: var(--sp-3); }
.py-4 { padding-top: var(--sp-4); padding-bottom: var(--sp-4); }
.py-5 { padding-top: var(--sp-5); padding-bottom: var(--sp-5); }
.py-6 { padding-top: var(--sp-6); padding-bottom: var(--sp-6); }
/* Single-side padding — must come AFTER .p-/.px-/.py- so it wins on equal specificity. */
.pt-0 { padding-top: 0; }
.pt-1 { padding-top: var(--sp-1); }
.pt-2 { padding-top: var(--sp-2); }
.pt-3 { padding-top: var(--sp-3); }
.pt-4 { padding-top: var(--sp-4); }
.pt-5 { padding-top: var(--sp-5); }
.pt-6 { padding-top: var(--sp-6); }
.pb-0 { padding-bottom: 0; }
.pb-1 { padding-bottom: var(--sp-1); }
.pb-2 { padding-bottom: var(--sp-2); }
.pb-3 { padding-bottom: var(--sp-3); }
.pb-4 { padding-bottom: var(--sp-4); }
.pb-5 { padding-bottom: var(--sp-5); }
.pb-6 { padding-bottom: var(--sp-6); }
.pl-0 { padding-left: 0; }
.pl-1 { padding-left: var(--sp-1); }
.pl-2 { padding-left: var(--sp-2); }
.pl-3 { padding-left: var(--sp-3); }
.pl-4 { padding-left: var(--sp-4); }
.pl-5 { padding-left: var(--sp-5); }
.pl-6 { padding-left: var(--sp-6); }
.pr-0 { padding-right: 0; }
.pr-1 { padding-right: var(--sp-1); }
.pr-2 { padding-right: var(--sp-2); }
.pr-3 { padding-right: var(--sp-3); }
.pr-4 { padding-right: var(--sp-4); }
.pr-5 { padding-right: var(--sp-5); }
.pr-6 { padding-right: var(--sp-6); }

/* Font style */
.font-italic { font-style: italic; }

/* --- Typography --- */
.text-xs { font-size: 0.75rem; }
.text-sm { font-size: 0.85rem; }
.text-base { font-size: 0.9rem; }
.text-lg { font-size: 1.1rem; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-left { text-align: left; }
.text-success { color: var(--ol-green); }
.text-warning { color: var(--ol-yellow); }
.text-danger { color: var(--ol-red); }
.nowrap { white-space: nowrap; }
.font-mono { font-family: monospace; }

/* Width + flex sizing */
.w-full { width: 100%; }
.flex-shrink-0 { flex-shrink: 0; }

/* Grid columns (U1 iter 9) */
.grid-cols-2 { grid-template-columns: 1fr 1fr; }
.grid-cols-3 { grid-template-columns: 1fr 1fr 1fr; }

/* The validity pair in the validation editor ("Data de Início" /
   "Data de validade efetiva"). The effective label is ~78% wider than its
   neighbour, so in a narrow column it wraps to two lines while "Data de Início"
   stays on one — which pushes its date input a full line down and leaves the two
   controls visibly stepped. Measured on the real stylesheet: a 19px offset at
   320/360/375px, and only ~4px of slack left at 390px (the commonest phone
   width), so the single-line case is one font fallback away from breaking too.
   Below ~27rem the side-by-side comparison is worth less than a straight row, so
   stack. Scoped to this pair rather than to `.grid-cols-2`, which is a shared
   utility used across the app. */
@media (max-width: 27rem) {
  .validity-dates-grid { grid-template-columns: 1fr; }
}

/* Cursor (U1 iter 9) */
.cursor-pointer { cursor: pointer; }
.cursor-default { cursor: default; }

/* Opacity (U1 iter 9) — for inactive-row dimming */
.opacity-50 { opacity: 0.5; }
.opacity-60 { opacity: 0.6; }
.opacity-100 { opacity: 1; }

/* Compact button variants (U1 iter 9) — recurring `padding/fontSize`
   inline-style patterns lifted into reusable classes. Literal px for
   exact parity with the converted `style={{ padding: 'Npx Mpx', ... }}`
   sites (Pico sets --pico-font-size: 15px so rem→px would shrink each
   side by 0.125–0.25px). Reset Pico's min-height/height/margin so they
   sit naturally inline with text (mirrors .btn-sm at line 1828). */
.btn-compact {
  padding: 4px 12px;
  font-size: 0.85rem;
  line-height: 1.3;
  min-height: unset;
  height: auto;
  margin: 0;
  vertical-align: middle;
}
.btn-compact-xs {
  padding: 4px 12px;
  font-size: 0.75rem;
  line-height: 1.3;
  min-height: unset;
  height: auto;
  margin: 0;
  vertical-align: middle;
}
.btn-micro {
  padding: 2px 8px;
  font-size: 0.75rem;
  line-height: 1.3;
  min-height: unset;
  height: auto;
  margin: 0;
  vertical-align: middle;
}

/* UA-A3 note: the existing .skip-to-main link in App.tsx is the canonical
   skip-to-content. <main tabIndex={-1}> (Layout.tsx) makes it programmatically
   focusable so activating the skip link actually moves focus there. */
.main-content:focus {
  outline: none;
}

/* --- Page header — standard top-of-page block --- */
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.page-header__titles {
  min-width: 0;
}

.page-header__titles h1,
.page-header__titles h2 {
  margin-bottom: 0.2rem;
}

.page-subtitle {
  margin: 0;
  font-size: 0.9rem;
  color: var(--ol-gray-500);
}

.page-header__actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-shrink: 0;
}

@media (max-width: 600px) {
  .page-header {
    flex-direction: column;
    gap: 0.75rem;
  }
}

/* --- Requires-JWT panel (UA-P0-1/2/3) --- */
.requires-jwt-panel {
  padding: 1.25rem 1.5rem;
  background: var(--ol-yellow-bg);
  border: 1px solid var(--ol-yellow-border);
  border-radius: 8px;
  max-width: 640px;
}
.requires-jwt-panel h3 {
  margin: 0 0 0.5rem;
  font-size: 1rem;
  color: var(--ol-yellow);
}
.requires-jwt-panel p {
  margin: 0 0 0.6rem;
  font-size: 0.9rem;
  line-height: 1.5;
}
.requires-jwt-panel__actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.5rem;
}

/* Sidebar nav item — JWT-only marker shown for legacy sessions */
.sidebar-nav a .nav-jwt-only {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  color: var(--ol-gray-400);
}

/* --- Deactivated-entity banner --- */
.deactivated-banner {
  padding: 0.6rem 1rem;
  background: var(--ol-red-bg);
  border: 1px solid var(--ol-red-border);
  border-radius: 8px;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ol-red);
  margin-bottom: 1rem;
}

/* --- Help text & info box --- */
.help-text {
  font-size: 0.82rem;
  color: var(--ol-gray-500);
  line-height: 1.45;
}

.info-box {
  padding: 0.75rem 1rem;
  background: var(--ol-gray-50);
  border: 1px solid var(--ol-gray-200);
  border-radius: 8px;
  font-size: 0.875rem;
}

.divider {
  border: none;
  border-top: 1px solid var(--ol-gray-200);
  margin: 1.5rem 0;
}

/* --- Login screen --- */
.login-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 1.5rem;
  background: linear-gradient(160deg, var(--ol-primary) 0%, #0a0d4a 55%, #050733 100%);
}

.login-card {
  background: #fff;
  padding: 2.25rem 2rem;
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
  width: 100%;
  max-width: 380px;
}

.login-card__brand {
  font-family: 'Clash Display', system-ui, sans-serif;
  font-size: 1.75rem;
  font-weight: 700;
  text-align: center;
  letter-spacing: -0.02em;
  color: var(--ol-gray-900);
  margin: 0;
}

.login-card__brand::after {
  content: '';
  display: block;
  width: 2.5rem;
  height: 3px;
  border-radius: 99px;
  background: var(--ol-accent);
  margin: 0.5rem auto 0;
}

.login-card__subtitle {
  text-align: center;
  font-size: 0.85rem;
  color: var(--ol-gray-500);
  margin: 0.75rem 0 1.75rem;
}

.login-field-label {
  display: block;
  margin-bottom: 0.35rem;
  font-weight: 500;
}

.login-error {
  color: var(--ol-red);
  font-size: 0.85rem;
  margin: 0 0 1rem;
  padding: 0.5rem 0.75rem;
  background: var(--ol-red-bg);
  border: 1px solid var(--ol-red-border);
  border-radius: 6px;
}

.login-switch {
  margin-top: 1.25rem;
  text-align: center;
}

.login-link-btn {
  background: none;
  border: none;
  color: var(--ol-gray-500);
  cursor: pointer;
  text-decoration: underline;
  font-size: 0.85rem;
  padding: 0.25rem;
}

.login-link-btn:hover {
  color: var(--ol-primary);
  background: none;
}

/* --- Full-page centered status (loading / error fallback) --- */
.page-status {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  gap: 0.5rem;
  color: var(--ol-gray-500);
  text-align: center;
}

/* --- Support widget (P6) --- */

.support-fab {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 900;
  width: 52px;
  height: 52px;
  padding: 0;
  border: none;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ol-primary);
  color: #fff;
  box-shadow: var(--shadow-lg);
  cursor: pointer;
  transition: transform 0.15s ease, background 0.15s ease, bottom 0.16s ease;
}

/* When a RowDetailModal is minimized into the bottom-right dock chip, lift the
   support FAB above it so the two don't overlap (RowDetailModal toggles this
   class on <body> while docked). */
body.has-row-detail-dock .support-fab {
  bottom: calc(1.25rem + 3.5rem);
}

.support-fab:hover {
  background: var(--ol-primary-hover);
  transform: translateY(-2px);
}

.support-fab:focus-visible {
  outline: 3px solid var(--ol-accent);
  outline-offset: 2px;
}

.support-form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.support-kind {
  display: flex;
  gap: 0.5rem;
}

.support-kind-btn {
  flex: 1;
  background: var(--ol-gray-100);
  color: var(--ol-gray-700);
  border: 1.5px solid var(--ol-gray-200);
}

.support-kind-btn.active {
  background: var(--ol-primary);
  color: #fff;
  border-color: var(--ol-primary);
}

.support-label {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ol-gray-700);
}

.support-textarea-wrap {
  position: relative;
}

.support-textarea {
  width: 100%;
  resize: vertical;
  font: inherit;
  font-size: 0.9rem;
  padding: 0.6rem 2.6rem 0.6rem 0.7rem;
  border: 1.5px solid var(--ol-gray-300);
  border-radius: 8px;
  box-sizing: border-box;
}

/* Takes the standard accent focus treatment: this rule (0,2,0) out-specifies the
   global textarea:focus-visible (0,1,1), so it has to re-assert the accent
   border itself rather than suppress the ring with `outline: none`. */
.support-textarea:focus {
  border-color: var(--ol-accent);
}

.support-mic {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 34px;
  height: 34px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1.5px solid var(--ol-gray-300);
  background: #fff;
  color: var(--ol-gray-600);
  cursor: pointer;
}

.support-mic:hover {
  border-color: var(--ol-primary);
  color: var(--ol-primary);
}

.support-mic.recording {
  background: var(--ol-red-bg);
  border-color: var(--ol-red);
  color: var(--ol-red);
  animation: support-mic-pulse 1.2s ease-in-out infinite;
}

@keyframes support-mic-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.4); }
  50% { box-shadow: 0 0 0 6px rgba(220, 38, 38, 0); }
}

.support-hint {
  font-size: 0.8rem;
  color: var(--ol-gray-500);
  margin: 0;
}

.support-hint-recording {
  color: var(--ol-red);
  font-weight: 500;
}

.support-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

/* ----------------------------------------------------------------------------
   validacao-ux-bundle (walkfix4 wave-5)
   T8 — "Motivo" flag treatment: a light coloured dot + text instead of a heavy
   filled pill, matching the Ficheiro-column confidence indicator (P10). Shared
   so future flag lists don't reinvent the inline style. yellow = generic /
   manual reason, red = a specific failed validation rule.
   T2 — reset the text colour on dropdown items rendered as role="button":
   Pico forces --pico-color (white) on any [role=button], which made worker
   names render white-on-white in the "Alterar trabalhador" / "Fundir
   trabalhadores" search results. Force a legible colour there.
   -------------------------------------------------------------------------- */
/* nowrap01-b — the family-wide default is now WRAP, not nowrap. Six scoped
   opt-outs (queue-fanout-flag, the queue Motivo cell, doc-list Estado/
   Validade, …) used to bolt `white-space: normal` back on one selector at a
   time because the base below force-nowrapped every flag; that is inverted
   here so the wrap is structural instead of re-derived per site. Genuinely
   chip-like content that must never break opts IN via `.flag-line--chip`
   (below) instead of every long flag opting out. See
   docs/bughunt/HANDOFF-nowrap-cell-overflow-2026-07-28.md item B.

   ⚠ `overflow-wrap: anywhere`, NOT `break-word`, at the base — measured, not
   assumed. Most `.flag-line` consumers stay `inline-flex` (the "flex iff
   single flag" rule two paragraphs below): several flags share one
   `flexWrap: wrap` row and each flag is a flex ITEM with the browser default
   `min-width: auto`, so its layout box cannot shrink below its own
   min-content size. Per the CSS Text spec, `break-word` is explicitly
   EXCLUDED from min-content sizing (it only affects where an already-fitted
   line breaks); `anywhere` is INCLUDED — it actually shrinks the min-content
   size. Built this as `break-word` first and measured a real regression:
   doc-list-table's Estado column (multi-flag, inline-flex, unchanged from
   before this fix) went from fitting (−7.8px) to overflowing its cell by
   +43.7px at 390px real Chromium, because "Apto Condicional" had no line
   edge to break against and `break-word` didn't shrink its own box to find
   one. `anywhere` fixes that (measured 0 regression after switching).
   Single-flag cells that are given `display: flex` (Motivo, doc-list
   Validade — see their own rules) DO get a real line edge from filling the
   cell, so THEY explicitly re-assert `overflow-wrap: break-word` to avoid
   `anywhere`'s per-character shattering of prose dates — the two properties
   are deliberately opposite per site, not a base/opt-out pair. */
.flag-line {
  display: inline-flex;
  align-items: flex-start;
  font-size: 0.75rem;
  line-height: 1.3;
  margin-right: 0.5rem;
  margin-bottom: 0.15rem;
  white-space: normal;
  overflow-wrap: anywhere;
}

.flag-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-right: 4px;
  flex: none;
  /* `.flag-line` uses `align-items: flex-start` (otherwise a 2-line flag
     floats its dot to the vertical middle of the whole block, visibly
     detached from the first line). The dot then has to be pushed down by the
     first line's half-leading to sit on that line's text. That offset is
     DERIVED, not chosen:
       (line-height − dot height) / 2 = (0.75rem × 1.3 − 7px) / 2
                                      = (14.63px − 7px) / 2 = 3.81px = 0.34em
     It used to be a hand-copied literal in each per-site wrap opt-out and had
     already drifted to two values (0.3em in three places, 0.36em in one) —
     both within a pixel, but the next copy would have invented a third. One
     variable, one source, now applied HERE unconditionally instead of at
     each former opt-out site. If `.flag-line`'s font-size or line-height
     changes, recompute HERE.
     ⚠ CONSUME IT ONLY ON `.flag-dot` ITSELF. It is declared here rather than on
     `:root` so it stays next to the metrics it is derived from — but that means
     (a) a rule that reads it on a PARENT — e.g. a `padding-top` declared on
     `.some-flag` instead of on `.flag-dot` itself — gets no value and silently computes to 0, with no error and no test
     failure; and (b) the unit is `em`, so it resolves against the consuming
     element's font-size — correct on a 0.75rem `.flag-dot`, but a `.stat-card`
     flag runs at 1.75rem and would get ~9.5px instead of 3.8px — `.stat-card
     .flag-line .flag-dot` below resets `margin-top` back to 0 for that
     reason (it never wraps in practice and the offset would be wrong there
     even if it did). */
  --flag-dot-first-line-offset: 0.34em;
  margin-top: var(--flag-dot-first-line-offset);
}

/* OPT-IN for the rare flag that must stay a single unbreakable chip — the
   base above is now WRAP by default. Restores the pre-inversion look: text
   never breaks, and the dot vertically centers on the one line (no
   first-line offset needed because there is only ever one line). */
.flag-line--chip {
  white-space: nowrap;
  align-items: center;
  overflow-wrap: normal;
}
.flag-line--chip .flag-dot {
  margin-top: 0;
}

.flag-line--yellow { color: var(--ol-yellow); }
.flag-line--yellow .flag-dot { background: var(--ol-yellow); }
.flag-line--red { color: var(--ol-red); }
.flag-line--red .flag-dot { background: var(--ol-red); }
/* Neutral / unknown-state flag (e.g. "Desconhecido" doc-type fallback).
   Same dot+text treatment as the others so all status flags stay consistent. */
.flag-line--gray { color: var(--ol-gray-500); }
.flag-line--gray .flag-dot { background: var(--ol-gray-400, var(--ol-gray-500)); }
.flag-line--green { color: var(--ol-green); }
.flag-line--green .flag-dot { background: var(--ol-green); }
.flag-line--blue { color: var(--ol-blue); }
.flag-line--blue .flag-dot { background: var(--ol-blue); }
.flag-line--orange { color: var(--ol-orange); }
.flag-line--orange .flag-dot { background: var(--ol-orange); }

/* credencial-mw-queue / draft-persist-approve-picks: `.queue-fanout-flag`
   (the "Associado a vários trabalhadores" fan-out flag) and `.flag-line--wrap`
   (StatusFlag's `wrap` prop hook — see StatusFlag.tsx and its test) used to be
   scoped opt-outs that re-added `white-space: normal; align-items: flex-start`
   one selector at a time, back when `.flag-line`'s default was nowrap.
   nowrap01-b inverted that default (above), so both selectors are now
   redundant with the base rule and were removed here. The classes themselves
   are UNCHANGED and still emitted by their callers (StatusFlag's `wrap` prop,
   Validation.tsx's fan-out flag) — that is deliberate, not an oversight: they
   are harmless no-op hooks now rather than something worth threading a
   className removal through every call site for. */

/* Duplicados tab — the action cell previously used `nowrap`, which clipped the
   2–3 buttons (Manter A / Manter B / Ignorar, or Ligar / Ignorar) on narrower
   viewports. Let them wrap into a flex grid with a consistent gap so every
   button stays fully visible. */
.dup-actions-cell {
  white-space: normal;
  vertical-align: middle;
}
.dup-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  align-items: center;
}

.search-result-item[role="button"],
.search-result-item[role="button"] strong {
  color: var(--ol-gray-900);
}

/* Pico v2 paints every [role=button] as a solid PRIMARY button (blue/teal
   background, inverse text). A search-result row is a list item, not a button —
   the blue fill made the muted NIF/filename sub-text unreadable. Reset it to a
   plain white row with a subtle hover/focus highlight. Pico's `[role=button]`
   is a single-class selector (0,1,0), so `.search-result-item[role=button]`
   (0,2,0) wins without !important. */
.search-result-item[role="button"] {
  background: #fff;
  border: none;
  box-shadow: none;
  font-weight: inherit;
  /* Pico centres [role=button] text; a list row reads left-to-right. Without
     this, multi-line rows (title + filename caption) centre and the caption's
     left indent becomes meaningless. */
  text-align: left;
}

.search-result-item[role="button"]:hover,
.search-result-item[role="button"]:focus-visible {
  background: var(--ol-gray-50, #f8fafc);
  color: var(--ol-gray-900);
}

/* P11 — slow-load hint: after a skeleton lingers >2s, SkeletonRows reveals this
   explicit spinner + "A carregar…". Uses its own ::before spinner rather than
   Pico's aria-busy spinner, which is intentionally suppressed in skeleton
   contexts above ([aria-busy="true"]:has(.skeleton, .skeleton-row)::before). */
.skeleton-slow-hint {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  color: var(--ol-gray-600);
}
.skeleton-slow-hint::before {
  content: "";
  flex: none;
  width: 0.85rem;
  height: 0.85rem;
  border: 2px solid var(--ol-gray-300);
  border-top-color: var(--ol-gray-600);
  border-radius: 50%;
  animation: skeleton-slow-spin 0.7s linear infinite;
}
@keyframes skeleton-slow-spin {
  to { transform: rotate(360deg); }
}

/* VV-1: validation document viewer — larger inline box + fullscreen overlay.
   Appended (not refactored) so it composes with the wave-3 styles above. */
.zoomable-image {
  position: relative;
  width: 100%;
  height: calc(82vh - 2rem);
  overflow: hidden;
  border: 1px solid var(--ol-gray-200);
  border-radius: 8px;
  background: var(--ol-gray-50);
}
/* Fullscreen instance fills the dialog body instead of the inline 82vh box. */
.zoomable-image--fs {
  height: 100%;
  border: none;
  border-radius: 0;
  background: var(--ol-gray-100);
}

.validation-pdf-frame {
  width: 100%;
  height: calc(82vh - 2rem);
  border: 1px solid var(--ol-gray-200);
  border-radius: 8px;
}

/* Near-full-viewport overlay for the document viewer. Modal.tsx caps at 560px,
   so the viewer gets its own wide <dialog> (rendered by FullscreenViewer). */
.viewer-fullscreen {
  width: 96vw;
  max-width: 96vw;
  height: 94vh;
  max-height: 94vh;
  padding: 0;
  border: none;
  border-radius: 10px;
  background: #fff;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.viewer-fullscreen::backdrop {
  background: rgba(0, 0, 0, 0.6);
}
.viewer-fullscreen__inner {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.viewer-fullscreen__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--ol-gray-200);
}
.viewer-fullscreen__title {
  margin: 0;
  font-size: 0.95rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.viewer-fullscreen__body {
  flex: 1;
  min-height: 0;
  position: relative;
}
.viewer-fullscreen__frame {
  width: 100%;
  height: 100%;
  border: none;
}

/* --- VT-1: Validation queue table — fit-to-width + always-reachable actions ---
   The 7-column queue table could grow wider than the 1200px .main-content, which
   has overflow-x:hidden — so the last column ("Rever") was clipped off-screen with
   no way to scroll to it. Fix is three layers: a horizontal scroller (safety net at
   narrow widths), fixed table-layout so the table fits the screen at normal widths,
   and a sticky-right actions column so "Rever" stays reachable even mid-scroll.
   New classes only — earlier waves' table CSS is left untouched. */
.validation-queue-wrap {
  overflow-x: auto;
  border-radius: 10px;            /* re-establish the card rounding the scroller would clip */
}
/* loadperf01 T9 — server-side search/filter bar above the validation queue. */
.validation-queue-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.75rem;
  /* FIX-1 (historical): this bar used to be <div role="search">, which Pico styles
     as its input-`group` COMPONENT — one border-radius + box-shadow wrapping both
     controls (the "ring around the pair" bug). Neutralising the group vars below
     fixed that ring, but could never fix the ring Pico stamps on the SIBLING
     BUTTONS, because `[role=search]:has(input:focus) [type=button]` sets
     `--pico-button-box-shadow` on the button, not on any group var — so clicking
     the name search still outlined the Combobox trigger and "Limpar pesquisa".
     The bar is now a native <search> element (same landmark, no `[role=search]`
     attribute), so none of the group chrome applies and the vars below are inert.
     Kept only as belt-and-braces should the role ever be reinstated. */
  --pico-group-box-shadow: none;
  --pico-group-box-shadow-focus-with-button: none;
  --pico-group-box-shadow-focus-with-input: none;
  box-shadow: none;
  border-radius: 0;
}
/* FIX-1: keep the Combobox trigger filling its (auto/flex-sized) wrapper so it
   reads as an equal-width sibling of the name search, not a shrink-wrapped chip. */
.validation-queue-filters .ol-combobox__trigger { width: 100%; }
.validation-queue-filters > * { margin-bottom: 0; }
/* queue-filter-sidebyside — the sub selector (Combobox) and the worker-search
   input share ONE row, side by side (selector left, search right), grouped in a
   NON-wrapping `__primary` sub-row. Keeping them on one line at every width is a
   correctness requirement, not just cosmetics: if the input ever wrapped BELOW
   the Combobox, opening the Combobox would drop its fixed dropdown panel straight
   over the input, covering it — clicks would land on a dropdown option (the
   "combobox reacts when I click the search box" report) and typing would filter
   the sub list instead of the worker search ("search doesn't work"). nowrap +
   shrinkable children keeps the input out of the panel's drop path. */
.validation-queue-filters__primary {
  display: flex;
  flex-wrap: nowrap;           /* never stack the input beneath the Combobox */
  align-items: center;
  gap: 0.6rem;
  flex: 1 1 auto;
  min-width: 0;                /* allow the pair to shrink instead of overflowing */
}
/* Mirror the parent's Pico margin reset: the search <input> is no longer a direct
   child of .validation-queue-filters, so `.validation-queue-filters > *` no longer
   zeroes Pico's default `input { margin-bottom: 1rem }` — without this the input
   gains 1rem of dead space and rides ~0.5rem above the trigger in this centered
   row (the Combobox trigger already sets margin:0). */
.validation-queue-filters__primary > * { margin-bottom: 0; }
.validation-queue-filters__primary > .ol-combobox {
  width: auto;                 /* override the base .ol-combobox width:100% */
  flex: 1 1 0;
  min-width: 0;                /* shrink on narrow screens rather than wrap/stack */
  max-width: 24rem;
}
/* `input.` qualifier is load-bearing: Pico's padding rule is
   `input:not([type=checkbox],[type=radio],[type=range],[type=file])` (0,1,1), so a
   bare `.validation-queue-filter-name` (0,1,0) LOSES and its padding is silently
   dropped — which is why this one-off never actually matched the trigger. */
input.validation-queue-filter-name {
  flex: 1 1 0;
  min-width: 0;                /* shrink on narrow screens rather than wrap/stack */
  max-width: 24rem;
  /* Matches the Combobox trigger's box. The `height: auto` that lets padding
     win over Pico's pinned input height lives with the .filter-row rule; the
     focus treatment is now the global form-control :focus-visible rule near the
     top, so the local `outline: none` override — a third variant — is gone. */
  padding-block: 0.5rem;
  line-height: 1.4;
  border: 1px solid var(--ol-gray-300);
  border-radius: 6px;
  background: #fff;
  /* Pico's base `input:not([type=checkbox],[type=radio])` (0,1,1) adds
     `margin-bottom: var(--pico-spacing)`. The bar used to carry role="search", so
     Pico's own group reset (`[role=search] input:not(…) { margin-bottom: 0 }`, 0,2,1)
     cancelled it; the native <search> element does not match that selector, and the
     `.validation-queue-filters__primary > *` reset (0,1,0) is out-specified. Without
     this the input grows a 15px bottom margin and `align-items: center` pushes the
     Combobox trigger ~7.5px below it. */
  margin-bottom: 0;
}
/* The resting border above is `input.<class>` (0,1,1) and sits LATER in this file
   than the global `input:focus` accent border (also 0,1,1), so it wins the
   specificity tie and the border would stay grey inside the accent ring. Re-assert
   it at 0,2,1 — border only; the outline and halo stay in the single source of
   truth near the top of the file. */
input.validation-queue-filter-name:focus {
  border-color: var(--ol-accent);
}
.validation-queue-table {
  table-layout: fixed;
  width: 100%;
  min-width: 760px;               /* below this the wrapper scrolls instead of crushing cells */
  /* base `table` sets overflow:hidden for corner rounding, but that box would become
     the sticky scrollport and break the sticky-right cell. Hand rounding to the wrapper. */
  overflow: visible;
  box-shadow: none;               /* the wrapper carries the card chrome now */
}
/* Long unbroken filenames must wrap, not force horizontal overflow. `anywhere`
   only breaks tokens that can't otherwise fit — normal words still break at
   spaces, so worker/sub names don't break mid-word. */
.validation-queue-table td { overflow-wrap: anywhere; }

/* ─────────────────────────────────────────────────────────────────────────
   nowrap01 item C — WHY `--cols7` EXISTS. Read before adding an `nth-child`
   rule anywhere in this file's `.validation-queue-table` blocks.

   `.validation-queue-table` is worn by FIVE tables with DIFFERENT column
   counts (verified 2026-07-28):
     Validation.tsx  validation queue   7 cols   inside <TableScroll>
     Validation.tsx  Validados          7 cols   inside <TableScroll>
     LostDocsPanel   Documentos c/ erro 5 cols   .validation-queue-wrap
     EmailLog        Escalações         5 cols   .validation-queue-wrap
     EmailLog        Silenciados        3 cols   .validation-queue-wrap

   Every `nth-child(N)` width and per-column fix below was written for the
   7-column queue. Unscoped, they land on a DIFFERENT column in the other
   three: the 92px "Data" pin became a "Reprocessar"/"Ação" pin, the 13%
   "Trabalhador" width became the "Motivo" width, and the Motivo wrap opt-out
   protected a column LostDocs does not have. Measured consequences at 960px
   before this split: LostDocs' failure reason clipped mid-word and its whole
   "Recebido" date hidden under the opaque sticky action cell; Escalações'
   "A resolver…" button painted over the Motivo text; Silenciados' "Todos os
   alertas silenciados" badge cut to "● Todos".

   SO: `.validation-queue-table` is now CHROME ONLY — fixed layout, wrapping,
   the sticky-right action column, header treatment, row-state tints. Anything
   that names a COLUMN POSITION belongs on a per-table modifier.

   `--cols7` carries the 7-column geometry verbatim (queue + Validados render
   byte-identically to before the split). The modifier was added to EVERY
   column rule in the SAME selector position, so all pairwise specificity
   relationships between those rules are unchanged.
   The three shorter tables get their own explicit rules further down.
   ⚠ Never add a bare `.validation-queue-table td:nth-child(N)` rule again.
   ───────────────────────────────────────────────────────────────────────── */
/* 7 cols: Ficheiro | Tipo | Trabalhador | Subempreiteiro | Data | Motivo | Ações.
   Percentages sum <100; Motivo (no width set) absorbs the slack so the table always
   fills the wrapper without overflowing it. */
.validation-queue-table--cols7 th:nth-child(1), .validation-queue-table--cols7 td:nth-child(1) { width: 20%; }
.validation-queue-table--cols7 th:nth-child(2), .validation-queue-table--cols7 td:nth-child(2) { width: 11%; }
.validation-queue-table--cols7 th:nth-child(3), .validation-queue-table--cols7 td:nth-child(3) { width: 13%; }
.validation-queue-table--cols7 th:nth-child(4), .validation-queue-table--cols7 td:nth-child(4) { width: 13%; }
.validation-queue-table--cols7 th:nth-child(5), .validation-queue-table--cols7 td:nth-child(5) { width: 8%; }
.validation-queue-table--cols7 th:nth-child(7), .validation-queue-table--cols7 td:nth-child(7) { width: 112px; }

/* nowrap01 item C — the three SHORTER consumers, each stating its own geometry.
   Idiom: give a px width only to the "chrome" columns whose content has a known
   intrinsic size (a dd/mm/yyyy date, a button row); leave the prose columns
   width-less so `table-layout: fixed` splits the remainder between them. That
   way these tables degrade by narrowing TEXT, which wraps, instead of squeezing
   a nowrap date or an un-shrinkable button until it paints over its neighbour —
   which is exactly what the borrowed 7-column widths did to them.
   All figures below verified in Chromium (real Pico padding 11.25px/15px, ink
   measured with a DOM Range) at 1440/1280/1024/960/768; see the DECISIONS doc
   at fermiobra/docs/table-layout-spec/DECISIONS.md. */

/* The 760px floor on the shared class is sized for the SEVEN-column queue
   ("below this the wrapper scrolls instead of crushing cells"). Inherited by a
   5- or 3-column table it forces a horizontal scroll that is not needed, and
   because the last cell is sticky and opaque the columns it hides sit under it
   at rest — at a 960px viewport (≈625px of usable width) that silently ate
   LostDocs' "Recebido" and Escalações' "Criado" columns entirely. Give each
   short table a floor sized for ITS columns; below that they still scroll.
   ⚠ DO NOT LOWER THESE below ~760px. A UI review caught the trap: at a 768px
   viewport `.main-content` gives these tables 723px, so a 700px floor put them
   just ABOVE their floor — no scroll, and the columns crush instead. Measured at
   700px: the wrapper's scrollWidth equalled its clientWidth (723 = 723, i.e. no
   scroll at all), Ficheiro and Subempreiteiro fell to 102px each, and a normal
   filename shredded into an 11-line ribbon inside a 247px mostly-blank row. The
   3-column Silenciados survives at 730px only because 730 > 723 and it therefore
   still scrolls. The floor's job is to force the SCROLL, so it must stay ABOVE
   the narrowest content width these tables are ever handed.
   ⚠ These are the SAME specificity (0,1,0) as `.validation-queue-table
   { min-width: 760px }` above, so they win on SOURCE ORDER alone. Keep this
   block BELOW that rule — hoisting it silently restores the 7-column floor. */
.validation-queue-table--lost5 { min-width: 760px; }
.validation-queue-table--escalations5 { min-width: 760px; }
.validation-queue-table--silenced3 { min-width: 730px; }

/* LostDocsPanel — 5 cols: Ficheiro | Subempreiteiro | Motivo | Recebido | Ações.
   Motivo carries a `.badge` (`white-space: nowrap`, ~178px of ink) that the
   inherited 13% "Trabalhador" width crushed to a 98.7px column at 960px, where
   it ran 109px past its cell and was then CLIPPED mid-word by the opaque sticky
   action cell — the operator could read "● Process" and nothing else. 30% keeps
   it whole down to THIS table's 760px floor (below), where 32% is ~213px of
   content box against ~178px of badge ink. The extra margin over 30% is
   deliberate: `REASON_LABELS` maps only two reasons and LostDocsPanel falls
   back to the RAW `doc.reason` string for anything unmapped, which can be
   longer than either mapped label — and the clip that used to hide that is
   gone. Recebido/Ações are pinned so the date and the "A reprocessar…" button
   are never the thing that gives. */
.validation-queue-table--lost5 th:nth-child(3), .validation-queue-table--lost5 td:nth-child(3) { width: 32%; }
.validation-queue-table--lost5 th:nth-child(4), .validation-queue-table--lost5 td:nth-child(4) { width: 112px; }
.validation-queue-table--lost5 th:nth-child(5), .validation-queue-table--lost5 td:nth-child(5) { width: 175px; }

/* EmailLog Escalações — 5 cols: Subempreiteiro | Obra | Motivo | Criado | Ação.
   The 32% on Obra replaces the inline `style={{ width: '40%' }}` that used to
   live on the <th> (D-10's "give OBRA the flexible share"): a column width
   split between JSX and CSS is the same class of trap as this whole item. Obra
   went 40% -> 32%; the eight points it gives up buy the Ação column a cell its
   button actually fits in (the inherited 92px "Data" pin left "A resolver…"
   16.6px past its cell and over the Motivo text at 1280, 41.3px at 960) and
   keep Subempreiteiro/Motivo above ~109px at the 700px floor, which is the
   tightest point in this table. At 1440 Obra is still ~355px. */
.validation-queue-table--escalations5 th:nth-child(2), .validation-queue-table--escalations5 td:nth-child(2) { width: 32%; }
.validation-queue-table--escalations5 th:nth-child(4), .validation-queue-table--escalations5 td:nth-child(4) { width: 112px; }
.validation-queue-table--escalations5 th:nth-child(5), .validation-queue-table--escalations5 td:nth-child(5) { width: 145px; }
/* A date and a button label are single-line by nature. For Escalações this
   replaces the per-<th> inline `whiteSpace: 'nowrap'` (one source of truth) and
   also covers the body cells, which the inline version never did. The action
   columns are sized so their busiest label — the pending states "A
   reprocessar…" / "A resolver…", which are LONGER than the resting ones — fits
   on that one line; the resting label then has slack, which is the right way
   round. Without the nowrap the button box itself wrapped to three lines. */
.validation-queue-table--escalations5 th:nth-child(4),
.validation-queue-table--escalations5 td:nth-child(4),
.validation-queue-table--escalations5 th:nth-child(5),
.validation-queue-table--escalations5 td:nth-child(5),
.validation-queue-table--lost5 th:nth-child(4),
.validation-queue-table--lost5 td:nth-child(4),
.validation-queue-table--lost5 th:nth-child(5),
.validation-queue-table--lost5 td:nth-child(5) { white-space: nowrap; }

/* The shared `.validation-queue-table td { overflow-wrap: anywhere }` exists for
   column 1's unbroken FILENAMES; on ordinary Portuguese prose it fractures words
   mid-syllable ("Construçõe/s Silva", "respost/a ao pedido") — the same
   pathology B1-NEW-1 fixed for the queue's Motivo column with `break-word`,
   which unlike `anywhere` is not counted in min-content sizing and so still
   breaks a genuinely over-long token. Apply that same opt-out to every prose
   column of the three short tables. Only LostDocs col 1 carries filenames, so
   it is the one cell here that keeps `anywhere`. */
.validation-queue-table--lost5 td:nth-child(2),
.validation-queue-table--lost5 td:nth-child(3),
.validation-queue-table--escalations5 td:nth-child(1),
.validation-queue-table--escalations5 td:nth-child(2),
.validation-queue-table--escalations5 td:nth-child(3),
.validation-queue-table--silenced3 td:nth-child(1),
.validation-queue-table--silenced3 td:nth-child(2) {
  overflow-wrap: break-word;
  word-break: normal;
}

/* EmailLog Silenciados — 3 cols: Subempreiteiro | O que está silenciado | Ação.
   Only three columns, so the 7-column percentages left column 2 at 11% (189.8px
   at 960) and its "Todos os alertas silenciados" badge was cut to "● Todos" by
   the sticky action cell — the column whose entire job is to say WHAT is muted.
   Pin the two-button action cell; the two text columns split the rest. */
.validation-queue-table--silenced3 th:nth-child(3), .validation-queue-table--silenced3 td:nth-child(3) { width: 270px; }

/* Pin the actions column to the right edge so "Rever" is always visible, even
   before the operator scrolls the wrapper. Opaque backgrounds hide the row cells
   that scroll underneath; the hover tints mirror the row-hover rules above
   (.clickable-row:hover = rgba(33,242,192,0.06) ≈ #f2fefb composited on white). */
.validation-queue-table th:last-child,
.validation-queue-table td:last-child {
  position: sticky;
  right: 0;
  text-align: right;
}
.validation-queue-table thead th:last-child { background: var(--ol-gray-50); z-index: 6; }
/* Below the sticky header (z 5/6) so it scrolls under the header row, but its own
   sticky stacking context already floats it over the non-pinned cells in its row. */
/* The actions cell stays opaque white on hover — the row itself is not
   clickable, so there is no row-level hover tint to mirror (only the "Rever"
   button shows its own hover state). */
.validation-queue-table tbody td:last-child { background: #fff; z-index: 1; }

/* T1 (valpopup1): the Motivo column (col 6) holds reason badges (.flag-line).
   nowrap01-b made wrap the `.flag-line` DEFAULT (was: scoped here alone,
   because the shared rule used to force nowrap and overflow the cell under
   the opaque, sticky Ações column). `display: flex` is still cell-specific:
   it puts each reason badge on its own line (one flag per Motivo cell)
   instead of shrink-to-fitting to max-content, which is what gives this cell
   a real line edge to break against — see the "flex iff single flag" rule at
   `.flag-dot`'s declaration. Because it has that edge, `overflow-wrap:
   break-word` is re-asserted here (the base `.flag-line` now uses `anywhere`
   for the shrink-to-fit multi-flag case — see the base rule's comment): a
   Motivo reason is prose with spaces, and `anywhere` would shatter it
   character-by-character the same way it shatters a date, which is exactly
   what `break-word` exists to avoid. The maxWidth:320 wrapper div + per-rule
   `title` tooltips are preserved. Dot first-line alignment is now baked into
   the base `.flag-dot` rule. */
.validation-queue-table--cols7 td:nth-child(6) .flag-line { display: flex; overflow-wrap: break-word; }

/* name_imp identity group-header band (Part C2) — header-tweak pass.
   #1 THINNER: tighter vertical padding than a normal body cell (0.75rem) so the
      band reads as a slim divider strip, not a chunky row.
   #2 NEAR-WHITE: a very subtle grey (gray-50, #f9fafb) instead of the heavier
      table-head grey (#f1f5f9) — just enough to set the band apart.
   #3 NAME = BODY TEXT: the band's <td> keeps the inherited tbody font-size
      (0.9rem) / weight (normal), so the most-complete name in its leading <span>
      matches a regular cell; the muted identifier/count chips style themselves. */
.validation-queue-table tbody tr.validation-queue-row--identity-header td {
  padding: 0.3rem 1rem;
  background: var(--ol-gray-50, #f9fafb);
  border-bottom: 1px solid var(--ol-gray-100);
  text-align: left;
}
/* Left-aligned flat band; real horizontal spacing between segments via gap
   (inline margins read as cramped). align-items:baseline so the body-size name
   and the smaller muted chips share a text baseline. */
.identity-header-band {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: flex-start;
  gap: 0.1rem 0.6rem;
}
/* nowrap01-b follow-up (coordinator finding, wave 1 UI review): the label was
   scrolling out of view horizontally while the sticky actions column
   (td:last-child) stayed pinned on the right — a band that reads "6
   documentos" is meaningless once its own label has scrolled off. Every band
   type shares `.identity-header-band` (fan-out / identity / relationship), so
   pin it here once rather than per band. `left` matches the td's own
   1rem horizontal padding (above) so the label doesn't touch the scroller's
   edge; the nearest `overflow-x: auto` ancestor is `.table-scroll__viewport`,
   which is what `position: sticky` measures `left` against. */
.identity-header-band > span:first-child {
  position: sticky;
  left: 1rem;
}

/* WS4 relationship band (D-XDU-1/2) — deliberately the OUTERMOST band: a
   fan-out or identity band can still be drawn INSIDE the same run. Before
   this rule it shared `.validation-queue-row--identity-header td` above with
   every OTHER band and rendered pixel-identical to the bands nested inside
   it — a UI reviewer measured a "6 documentos" relationship band looking
   like it presided over a single row, because nothing distinguished
   "container" from "content". A blue accent (border + tint, same family as
   the app's other informational bands — see `.cp-panel` above) reads as a
   distinct, larger-scope container; the top+bottom border (vs. the plain
   band's bottom-only) frames it as something that OPENS a section rather
   than a slim inline divider. Same (0,2,3) specificity as the shared rule
   above and placed after it, so this wins on source order without needing
   `!important`. */
.validation-queue-table tbody tr.validation-queue-row--relationship-band td {
  background: var(--ol-blue-bg, #eff6ff);
  border-top: 2px solid var(--ol-blue-border, #bfdbfe);
  border-bottom: 1px solid var(--ol-blue-border, #bfdbfe);
  padding-top: 0.45rem;
  padding-bottom: 0.45rem;
}
/* Same reasoning applied to the LABEL text itself, so the whole band —
   text included — reads in the accent colour, not just its background. */
.validation-queue-row--relationship-band .identity-header-band .text-muted {
  color: var(--ol-blue-700, #1d4ed8);
}

/* The row that CLOSES a relationship run used to carry the exact same 2px
   gray `--cluster-divider` border as leaving an ordinary identity cluster —
   also flagged by the wave-1 review as indistinguishable, even though a
   relationship run is the OUTER container and closing it is the bigger
   boundary. `relRunDividerBefore` (Validation.tsx) is a subset of
   `dividerBefore`'s three OR'd causes (new cluster / left a fan-out run /
   left a relationship run), so BOTH `--cluster-divider` and
   `--rel-run-divider` land on the row whenever the relationship-run cause
   fires. Rather than rely on source order to break the tie (fragile — the
   next edit that reorders these two rules silently flips which one wins),
   this selector requires BOTH classes, which raises its specificity above
   `--cluster-divider` alone (0,3,3 vs 0,2,3) and wins unconditionally. */
.validation-queue-table tbody tr.validation-queue-row--cluster-divider.validation-queue-row--rel-run-divider td {
  border-top: 3px solid var(--ol-blue-border, #bfdbfe);
}
/* "Identidade agrupada" review-pane panel (ClusterProvenancePanel).
   Lives in the NARROW right-hand review column (.validation-right, ~1fr of the
   1.15fr/1fr .validation-shell split), so every text run — worker names, doc-type
   names, NIF/NISS digits — must be able to break inside the box rather than spill
   out of it. All styling is here rather than inline so the whole card can be read
   (and adjusted) in one place. Light-theme only (Pico overridden).
   Renders inline in the review-pane fieldset — NOT inside the overflow:hidden
   queue table — so no portal/fixed positioning is needed. */
.cp-panel {
  max-width: 100%;
  box-sizing: border-box;
  overflow-wrap: anywhere;
  word-break: break-word;
  background: var(--ol-blue-bg, #eff6ff);
  border: 1px solid var(--ol-blue-border, #bfdbfe);
  border-radius: 8px;
  margin-bottom: 0.75rem;
  font-size: 0.85rem;
  line-height: 1.45;
}

/* Header row: chevron + title, the whole strip clickable. `min-width:0` on the
   button AND the title is what actually lets the title wrap — without it the flex
   item takes its max-content width and overflows the card. */
.cp-toggle {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  padding: 0.5rem 0.6rem;
  margin: 0;
  background: none;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font: inherit;
  text-align: left;
  color: inherit;
}
.cp-toggle:hover { background: var(--ol-blue-100, #dbeafe); }
.cp-toggle:focus-visible { outline: 2px solid var(--ol-blue, #2563eb); outline-offset: -2px; }

/* CSS-drawn triangle (no emoji glyph) — rotates to point down when open. */
.cp-chevron {
  display: inline-block;
  flex: 0 0 auto;
  width: 0;
  height: 0;
  /* Pin to the FIRST line of the title. A long merged name wraps the title to 2-3
     lines; centring the chevron against the whole block parked it beside line 2. */
  align-self: flex-start;
  margin-top: 0.4rem;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  border-left: 5px solid var(--ol-blue-hover, #1d4ed8);
  transition: transform 0.15s ease;
}
.cp-chevron--open { transform: rotate(90deg); }

.cp-title { min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
/* The merged name is supporting detail, not a second heading — de-emphasise it so
   the count reads first and a long name doesn't shout. */
.cp-title-name { font-weight: 400; color: var(--ol-gray-600, #4b5563); }

.cp-detail {
  padding: 0 0.6rem 0.55rem;
  border-top: 1px solid var(--ol-blue-border, #bfdbfe);
  margin-top: 0.1rem;
  padding-top: 0.5rem;
}

/* Shared small-caps label for the two sections ("associado a:", "Campos usados:")
   so the card has one obvious hierarchy instead of two ad-hoc muted lines. */
.cp-section-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ol-gray-500, #6b7280);
  margin-bottom: 0.35rem;
}

.cp-siblings {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
/* Each sibling is its own card: the doc link + badge on line 1, the reason it
   grouped on line 2. Two lines beats one wrapped line here — in a column this
   narrow the badge and the "— reason" tail used to wrap into a ragged blob. */
.cp-sibling {
  min-width: 0;
  overflow-wrap: anywhere;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.35rem 0.45rem;
  border: 1px solid var(--ol-blue-border, #bfdbfe);
  border-radius: 6px;
  background: #fff;
}
.cp-sibling-main {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  min-width: 0;
}
/* `.link-button` carries no rules of its own — these used to be inline styles, and
   inline beat Pico's `button` defaults for free. As a class they must undo them
   explicitly, `width:auto` above all: Pico stretches a bare button to 100%, which
   would knock the "validado" badge onto its own line. */
.cp-sibling-link {
  min-width: 0;
  width: auto;
  padding: 0;
  margin: 0;
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  font-weight: 500;
  line-height: inherit;
  text-align: left;
  overflow-wrap: anywhere;
  color: var(--ol-blue-hover, #1d4ed8);
  text-decoration: underline;
}
.cp-sibling-name { min-width: 0; font-weight: 500; overflow-wrap: anywhere; }
/* The "why it grouped" reason is supporting evidence — it must sit UNDER the doc
   name in the visual hierarchy, not compete with it. */
.cp-sibling-detail { font-size: 0.78rem; color: var(--ol-gray-500, #6b7280); }
.cp-sibling-detail strong { font-weight: 500; color: var(--ol-gray-600, #4b5563); }
/* The badge must not be squeezed by a long doc label beside it. */
.cluster-sibling-validado { flex: 0 0 auto; font-size: 0.7rem; white-space: nowrap; }

/* Separate the "Campos usados" block from the siblings list with a divider +
   breathing room, giving the panel a clear two-section hierarchy. */
.cp-fields {
  margin-top: 0.6rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--ol-blue-border, #bfdbfe);
}
.cp-fields-list { list-style: none; margin: 0; padding: 0; }
/* label + value on one line, provenance on its own muted line beneath: the source
   doc name is the longest string in the card and must never crowd the value. */
.cp-field {
  min-width: 0;
  overflow-wrap: anywhere;
  padding: 0.25rem 0;
}
.cp-field + .cp-field { border-top: 1px dashed var(--ol-blue-border, #bfdbfe); }
.cp-field-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem;
  min-width: 0;
}
.cp-field-label { flex: 0 0 auto; font-weight: 600; color: var(--ol-gray-700, #374151); }
.cp-field-value { min-width: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.cp-field-src { display: block; font-size: 0.78rem; color: var(--ol-gray-500, #6b7280); }
.cp-field-src strong { font-weight: 500; color: var(--ol-gray-600, #4b5563); }

/* #3 NAME MATCHES THE BAND: the most-complete name carries the SAME
   `text-xs text-muted` styling as the identifier/DOB/count chips (David: same
   font, size, AND colour as the rest of the band — it must not read bigger).
   The `.identity-header-name` class is kept only as a stable test/render hook
   and intentionally sets NO font of its own, so the utility classes win. */

/* #4 DIVISION after a cluster: a row that starts a new (solo or other) group
   right after a multi-member cluster gets a top border, closing the cluster off
   visually so the rows don't blur together. Flat — no indentation. The sticky
   actions cell (td:last-child) carries its own opaque background, so the border
   is applied to every cell of the row to read across the full width. */
.validation-queue-table tbody tr.validation-queue-row--cluster-divider td {
  border-top: 2px solid var(--ol-gray-200);
}

/* name_imp Part C3: a row the operator detached via "Separar do grupo" stays in
   place (it no longer teleports to its own recency slot). Mark it as pulled aside
   — a faint tint + a left accent bar — so it reads as "spun out of the group
   above" rather than vanishing, with the reversible "Voltar a agrupar" control on
   the row. The tint is applied to every cell so it reads across the sticky actions
   cell; the accent bar rides the first cell (inset shadow, so it never widens the
   fixed-layout column). */
.validation-queue-table tbody tr.validation-queue-row--detached td {
  background: var(--ol-gray-50, #f9fafb);
}
.validation-queue-table tbody tr.validation-queue-row--detached td:first-child {
  box-shadow: inset 3px 0 0 var(--ol-gray-400, #9ca3af);
}

/* fix-queue-layout (bughunt0707 B1-P1-1 / B1-P2) — scoped to the Validation
   queue + Validados tables. These used to lean on `.table-scroll` ANCESTRY to
   mean "the 7-column tables"; since nowrap01 item C they also carry the
   explicit `--cols7` modifier, so the scoping states what it means and a
   future <TableScroll> around a 5-column sibling cannot silently inherit
   7-column geometry. The `.table-scroll` prefix is retained on every one of
   them so the specificity ordering against the later `--cols7` overrides
   (the 92px col-5 / 14% col-4 block) is exactly what it was. */
/* B1-P1-1: the Data / "Verificado em" column was 8% — ≈61px at the 760px table
   min-width, while a nowrap "06/07/2026" needs ~78px + 2rem cell padding, so
   the date painted over the Motivo cell (text-on-text). Fixed px width sized
   for the full date + padding (mirrors the 112px Ações column). */
.table-scroll .validation-queue-table--cols7 th:nth-child(5),
.table-scroll .validation-queue-table--cols7 td:nth-child(5) {
  width: 112px;
}
/* similarbugs-nowrap-cell-overflow §2.1 — the SAME cell also carries the
   validator's email, on a second line, when an admin unchecks "Mostrar apenas
   as validações deste utilizador" (Validation.tsx, `scope === 'all'`). That
   line is not a date and does not fit: `david.matias@fermi.pt` measures ~146px
   in this column's 82px content box, and it inherited the cell's `nowrap` —
   which is here for the DATE, not for it. So it painted ~64px past the cell and
   over the neighbouring "Estado" flag: two strings on top of each other, in the
   one column whose job is to say who verified the document. Width-independent,
   because the column is pinned in px.
   Undo the inherited nowrap for this line only — the date above it keeps its
   nowrap and its 112px pin.

   `white-space: normal` is the load-bearing half. The `overflow-wrap` line
   RE-ASSERTS a value this cell already inherits from
   `.validation-queue-table td { overflow-wrap: anywhere }` (above), so removing
   it changes nothing today — it is defence-in-depth against that inherited rule
   being narrowed later, and it documents at the point of use that this value
   MUST be able to break mid-token. Keep the two declarations together.
   ⚠ Do not read this as contradicting the doc-list Validade rule, which
   deliberately overrides the same inherited `anywhere` DOWN to `break-word`.
   The two cells want opposite things and both are right: the Validade label is
   ordinary prose whose words must stay whole (`anywhere` shattered a date into
   one character per line), whereas an email address is a single unbreakable
   token — per-character breaking is the only way it can fit at all, and there
   is nothing to preserve by refusing to split it.
   The element also carries a `title` with the full address, so a wrapped value
   stays hoverable and copyable. */
.table-scroll .validation-queue-table--cols7 td:nth-child(5) .verified-by-email {
  white-space: normal;
  overflow-wrap: anywhere;
}
/* B1-P2-2: header words no longer fracture mid-word at narrow widths
   ("SUBEMPREITEI/RO", "TRABALH/ADOR"). Wrapping at spaces stays allowed; an
   unbreakable-too-long word ellipsizes on its line instead (the th carries a
   `title` with the full text).
   nowrap01 item C: this used to be scoped `.table-scroll …`, so it only ever
   reached the queue + Validados; the three unwrapped consumers kept fracturing
   ("SUBEM/PREITE/IRO" on LostDocs at 960px, measured). Mid-word fracture in a
   table head is wrong on all five, and this rule names no column, so it is
   chrome — promoted to the shared class. */
.validation-queue-table thead th {
  overflow-wrap: normal;
  word-break: normal;
  hyphens: none;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* B1-P2-3: while columns are still hidden beneath the OPAQUE sticky Ações
   cell (TableScroll toggles `--more` off once fully scrolled), cast a soft
   left-edge inset shadow on it so Motivo text disappearing under the pinned
   column reads as "scroll for more", not broken truncation. This replaces the
   right-edge fade cue (cue={false}) which would wash the pinned column out. */
.table-scroll--more .validation-queue-table thead th:last-child,
.table-scroll--more .validation-queue-table tbody td:last-child {
  box-shadow: inset 12px 0 10px -10px rgba(3, 5, 49, 0.3);
}

/* B1-NEW-1 (bughunt0713, HIGH): at the old 760px table floor the fixed columns
   (20+11+13+14% + 112px Data + 112px Ações) consumed 665px, leaving Motivo —
   the width-less slack absorber — exactly 95px. `overflow-wrap: anywhere` then
   shredded the triage reason into a 9–15-line tower with mid-word fractures
   ("Tipo / descon / hecido"), whose height set every row's height: ~3 giant,
   mostly-blank rows per laptop screen. Two-part fix, scoped via `.table-scroll`
   ancestry (queue + Validados only — EmailLog reuses `.validation-queue-table`
   UNWRAPPED with different columns and must keep the 760px base floor):
   1) raise the floor so Motivo is guaranteed ≥ ~179px (0.42×960 − 224). The
      wrapper h-scrolls sooner on laptops, but the sticky Ações column + the
      B1-P2-3 inset-shadow cue are the designed affordance for that — readable
      after a scroll beats unreadable always. */
.table-scroll .validation-queue-table--cols7 {
  min-width: 960px;
}
/* 2) Motivo wraps at word boundaries — normal spaced Portuguese reasons never
   fracture mid-word (the pathology B1-P2-2 fixed for headers, reintroduced in
   body cells by the global `td { overflow-wrap: anywhere }`, which stays for
   col 1's unbroken filenames). `break-word` (NOT `normal`): a genuinely
   unbreakable token longer than the ~179px column — a long filename/code
   embedded in a reason — still breaks to a new line rather than being CLIPPED
   by the pre-existing `td:nth-child(6){overflow:hidden;text-overflow:ellipsis}`
   (styles.css ~5321, which can't ellipsize wrapped multi-line flag content).
   Unlike `anywhere`, `break-word` breaks are NOT counted in min-content sizing,
   so normal-length words keep wrapping only at spaces. overflow-wrap inherits,
   so the .flag-line badges follow. */
.table-scroll .validation-queue-table--cols7 td:nth-child(6) {
  overflow-wrap: break-word;
  word-break: normal;
}
/* B1-NEW-7 (bughunt0713): the uppercase letter-spaced "SUBEMPREITEIRO" header
   needs ~130px of content box; at 14% it ellipsized ("SUBEMPREITEI…") even on
   a full 1440 desktop. Grant col 4 two more percent where the table is wide
   enough to spare it (Motivo keeps ≥ ~240px there); below 1200px the ellipsis
   + the existing th `title` tooltip remain the intended degradation. */
@media (min-width: 1200px) {
  .table-scroll .validation-queue-table--cols7 th:nth-child(4),
  .table-scroll .validation-queue-table--cols7 td:nth-child(4) {
    width: 16%;
  }
}

/* Forecast "Previsão" (projected) table: ONE single read-only table that uses
   the app's plain base <table> look (same as the main email log) — NOT the
   validation-queue 7-col fixed/sticky layout, which squashed columns and pinned
   the wrong cell when reused here. Rows from different days share one table,
   separated by a full-width day header row. */
.forecast-table { width: 100%; }
.forecast-table .nowrap { white-space: nowrap; }
/* Full-width day separator inside the single forecast table. */
.forecast-day-row td {
  background: var(--ol-gray-50, #f9fafb);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--ol-gray-600, #4b5563);
  border-top: 1px solid var(--ol-gray-200, #e5e7eb);
}

/* ========================================================================
   Modo demonstração (demo / privacy mode)
   ------------------------------------------------------------------------
   Privacy in demo mode is now enforced ENTIRELY SERVER-SIDE: when the mode is
   on, the frontend sends `X-Demo-Mode: 1` (src/api.ts) and the backend
   `DemoMaskMiddleware` (backend/app/demo_mask.py) redacts personal data out of
   every JSON response before it reaches the browser. There is deliberately NO
   client-side CSS censoring anymore — no grey redaction bars over table values
   / names / NIFs / emails / form fields, and no blur on document images. The
   data simply arrives already masked, so nothing needs to be hidden visually.

   The only thing left here is the floating "modo demonstração ativo" badge so
   the operator can see at a glance that the mode is on (and click to turn it
   off). `src/lib/demoMode.ts` still toggles the `demo-censor` class on <html>
   as a state marker, but no rule targets it.
   ======================================================================== */

/* Floating "modo demonstração ativo" badge — always legible (.demo-keep),
   above all content, and a one-click off switch. */
.demo-mode-badge {
  position: fixed;
  left: 1rem;
  bottom: 1rem;
  z-index: 9999;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.8rem;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1;
  color: #fff;
  background: #b91c1c;
  border: 1px solid #fca5a5;
  border-radius: 999px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
  cursor: pointer;
  width: auto;
  margin: 0;
}
.demo-mode-badge:hover { background: #991b1b; }

/* ===========================================================================
   Unified email table ("Caixa de emails") — email-unified-table-redesign Layer 1
   One filterable table with three time buckets (Previsão / Agora / Histórico).
   =========================================================================== */

/* Filter area above the table — row 1 holds the dropdown selectors, rows 2-3
   hold the checkbox groups full-width so a growing dynamic Tipo list wraps
   downward and never overflows to the right (#4). */
.email-filter-bar {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  margin-bottom: 0.85rem;
  padding: 0.75rem 0.9rem;
  background: var(--ol-gray-50, #f9fafb);
  border: 1px solid var(--ol-gray-200, #e5e7eb);
  border-radius: 10px;
}
.email-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.25rem;
  align-items: flex-start;
}
/* Checkbox groups stack one per line, each full-width, so their inner checkboxes
   wrap within the row instead of pushing past the right edge. */
.email-filter-row--checks {
  flex-direction: column;
  gap: 0.55rem;
  border-top: 1px solid var(--ol-gray-200, #e5e7eb);
  padding-top: 0.6rem;
}
.email-filter-group {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}
.email-filter-group--wide { width: 100%; }
.email-filter-clear {
  margin-left: 0.4rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ol-blue-600, #2563eb);
  font-size: 0.7rem;
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
  text-decoration: underline;
}
/* Held-email batch-approve toolbar (the "Aprovar todos / selecionados" row). */
.email-batch-row > td {
  background: var(--ol-gray-50, #f9fafb);
  border-bottom: 1px solid var(--ol-gray-200, #e5e7eb);
  padding: 0.5rem 0.75rem;
}
.email-batch-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.email-batch-hint {
  font-size: 0.72rem;
}
.email-held-check {
  vertical-align: middle;
  cursor: pointer;
}
.email-filter-group > .email-filter-label {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ol-gray-500);
}
.email-filter-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.85rem;
}
.email-filter-check {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.85rem;
  white-space: nowrap;
  cursor: pointer;
}
.email-filter-check input { margin: 0; cursor: pointer; }
/* emails-active-alert-star: "* = alerta ativo" legend under the Tipo checks.
   Small top margin so it hugs the Tipo group it describes rather than floating
   midway to the Estado group. */
.email-filter-legend {
  margin: 0.2rem 0 0;
  font-size: 0.75rem;
}
.email-filter-group select { max-width: 16rem; }
/* combobox-unify: the email filter selects are now <Combobox> controls. */
.email-filter-group .ol-combobox { max-width: 16rem; }

/* "Alertas ativos" summary panel (#5). */
.alertas-ativos {
  margin-bottom: 0.85rem;
  padding: 0.55rem 0.9rem;
  background: var(--ol-gray-50, #f9fafb);
  border: 1px solid var(--ol-gray-200, #e5e7eb);
  border-radius: 10px;
  font-size: 0.85rem;
}
.alertas-ativos > summary {
  cursor: pointer;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
.alertas-ativos-count {
  font-size: 0.75rem;
  color: var(--ol-gray-500);
}
.alertas-ativos-body { margin-top: 0.6rem; }
.alertas-ativos-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 0.6rem;
}
.alertas-ativos-inactive { margin-top: 0.5rem; }
.alertas-ativos-hint { margin: 0.55rem 0 0; font-size: 0.78rem; }

/* Involved workers/documents shown above a projection's preview body (#6). */
.email-preview-entities {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.5rem;
  margin-bottom: 0.55rem;
  font-size: 0.82rem;
}
.email-preview-chip {
  display: inline-block;
  max-width: 100%;
  padding: 0.1rem 0.5rem;
  background: var(--ol-indigo-50, #eef2ff);
  border: 1px solid var(--ol-gray-200, #e5e7eb);
  border-radius: 999px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* An obra/sub with no (or invalid) recipient — red so the gap is obvious (#6a). */
.email-preview-chip--missing {
  background: var(--ol-red-bg, #fef2f2);
  border-color: var(--ol-red, #dc2626);
  color: var(--ol-red, #dc2626);
}

/* Bucket separator rows inside the unified table. */
.email-bucket-row td {
  background: var(--ol-gray-100, #f3f4f6);
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ol-gray-600);
  padding: 0.4rem 0.75rem;
}
.email-bucket-row--previsao td { background: var(--ol-blue-50, #eff6ff); }
.email-bucket-row .email-bucket-count {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ol-gray-500);
  margin-left: 0.4rem;
}

/* Projection rows read as a lighter "preview". */
.email-row--projection td { color: var(--ol-gray-600); }
.email-row--projection .email-cell-when { font-style: italic; }

/* EMAIL Layer 3 — a projection the operator cancelled (suppressed). Struck
   through + dimmed so it reads as "won't send" while staying visible + reversible
   (Repor). The action cell is left untouched so the Repor button stays legible. */
.email-row--suppressed td:not(:last-child) {
  text-decoration: line-through;
  opacity: 0.55;
}

/* Secondary recipient line under a sub name. */
.email-cell-recipient {
  display: block;
  font-size: 0.78rem;
  color: var(--ol-gray-500);
  margin-top: 0.1rem;
}

/* folha-ponto #19 — forecast recipient-count badges. They carry a full
   sentence, and yellow needs the darker text token for AA contrast at 12px.
   A suppressed row's line-through doesn't propagate into the badge's
   inline-flex box — set it explicitly so a cancelled row's badge reads
   cancelled too.
   nowrap01-b: the white-space:normal + line-height:1.3 this rule used to
   restore against a nowrap base are now what `.badge` sets by default —
   removed here as redundant, not as a behavior change. */
.badge--recipient-count.badge--yellow {
  color: var(--ol-yellow-text);
}
.email-row--suppressed .badge--recipient-count {
  text-decoration: line-through;
}

/* Pager footer for the Histórico bucket. */
.email-pager {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.75rem;
  margin-top: 0.6rem;
  font-size: 0.85rem;
  color: var(--ol-gray-600);
}

/* Unified email table — horizontal scroll on narrow viewports so the dense
   6-column layout never clips columns off-screen (mirrors .validation-queue-wrap).
   min-width keeps the columns readable; the wrapper scrolls instead. */
.email-table-wrap { overflow-x: auto; }
.email-table { min-width: 760px; }

/* ══════════════════════════════════════════════════════════════════════════
   uipolish7 css-modals-tables-misc — FIX-5 table sizing / sticky actions.
   Appended here (after the base validation-queue + email table rules) so these
   same-specificity overrides win by source order. Owns the MODAL/TABLE region;
   does NOT touch the form-control region (css-form-controls' lane).
   ════════════════════════════════════════════════════════════════════════ */

/* FIX-5 · E-01 / B-06 — validation queue: give the Data column a firm width so
   it stops bleeding onto the Motivo badge (illegible overlap at ~768px).
   col order: Ficheiro|Tipo|Trabalhador|Subempreiteiro|Data|Motivo|Ações
   ⚠ This 92px pin is the single clearest illustration of handoff item C: it was
   keyed on the bare class, so it also pinned LostDocsPanel's "Recebido" column,
   Escalações' "Ação" BUTTON column and Silenciados' "Ação" column to 92px. The
   `--cols7` modifier is what stops that. The Motivo-clipping half of E-01/B-06
   is gone; see the comment where it used to be, just below. */
.validation-queue-table--cols7 th:nth-child(5),
.validation-queue-table--cols7 td:nth-child(5) {
  width: 92px;
  white-space: nowrap;
}
/* nowrap01 item C — `overflow: hidden; text-overflow: ellipsis` REMOVED from
   this cell (was FIX-5 E-01/B-06's "clip an over-long Motivo instead of letting
   it push under the sticky Ações column").
   Four reasons, in order of weight:
   1. It never ellipsized anything. `text-overflow` applies to a SINGLE-LINE
      overflow; this cell holds a wrapped multi-line `.flag-line` block, so the
      declaration only ever produced a SILENT clip with no affordance. The
      B1-NEW-1 comment above already treats it as a hazard to design around
      ("still breaks to a new line rather than being CLIPPED by the pre-existing
      td:nth-child(6){overflow:hidden}"), not as a feature.
   2. It contradicts the decision on record for this family — these cells WRAP,
      they do not truncate. A UI reviewer measured the alternative for the
      sibling "Verificado em" cell: an ellipsis at 82px shows `david.matia…`,
      strictly less information than a two-line wrap. Same argument here, where
      the clipped string is the operator's triage reason.
   3. It is redundant for its stated purpose. The Ações cell to its right is
      `position: sticky` with an opaque background and its own z-index, so it
      already paints over any rightward spill, and `.table-scroll__viewport`
      clips whatever passes the table edge.
   4. It made the column structurally UNMEASURABLE: e2e/layout-overflow.spec.ts
      skips a clipped anchor whole (clipped ink cannot overprint), so the wrap
      opt-out two rules above could never be discriminated by the detector — the
      spec stayed green with the opt-out deleted and the label 582px past its
      box. Removing the clip converts a blind site into a measured one.
   Measured no-op today, at the fixture's long Motivo reason: ink is −15.6px
   (1440) / −10.1px (1280) / −20.1px (1024, 960, 768) with the clip and without
   it, because the `break-word` rule above already guarantees the fit. It only
   changes what happens when that guarantee BREAKS — which is precisely when it
   must be visible. Column 6 of Validados ("Estado") wore the same clip and is
   covered by the same reasoning.
   ⚠ ACCEPTED, MEASURED COST — do NOT "fix" it by restoring the clip. `break-word`
   breaks are not counted in min-content sizing, and the label is an anonymous
   flex item (of the `display: flex` on `.flag-line`) whose automatic minimum
   size is therefore its longest unbreakable token. So a Motivo reason carrying
   a ≥~45-char unbreakable token — reachable via `validationErrorLabel`'s
   fallback to the full interpolated rule message, or `manualReasonLabel`'s
   verbatim passthrough — cannot shrink, and now spills past the table's rounded
   right edge onto the page background instead of being clipped at the cell
   (measured at a 1024px viewport: the scrollport's scrollWidth goes 960 -> 1001,
   so ~41px of tail floats outside the card and `--more` keeps its "scroll for
   more" shadow lit). Not reachable at 1440/1280. This is a deliberate trade:
   the old behaviour DESTROYED that text silently with no ellipsis (the content
   is multi-line, so `text-overflow` never fired), whereas now it is ugly but
   readable and scrollable — and the detector reports it. The clean fix belongs
   to handoff item B, which reworks `.flag-line` itself: give the label a real
   flex item with `min-width: 0` (or drop the flex layout) and the token breaks
   instead of either spilling or being clipped.
   The Data-column half of FIX-5 E-01/B-06 (the 92px pin below) is unaffected. */
/* E-03 — the "Subempreiteiro" header is a long single word; keep it on one line
   (with a hair more column) so it stops wrapping to two rows in the head. */
.validation-queue-table--cols7 th:nth-child(4),
.validation-queue-table--cols7 td:nth-child(4) { width: 14%; }
.validation-queue-table--cols7 th:nth-child(4) { white-space: nowrap; }

/* B-13 — the last queue row's "Rever" sat under the floating chat FAB. Clear it
   with bottom padding on the scroll wrapper (padding, so it stays inside the
   rounded scroller and doesn't add an outer gap).
   NB since nowrap01 item C this also applies to LostDocsPanel, which gained
   `.validation-queue-wrap`. Deliberate, not collateral: the SupportWidget FAB is
   global, LostDocs' last row carries a "Reprocessar" button, and the EmailLog
   tables that already wore this class have had the same 72px for as long. A
   bespoke opt-out would be a third behaviour to keep in sync for no gain. */
.validation-queue-wrap { padding-bottom: 72px; }

/* D-01 — "Caixa de emails" table: pin the Ações column to the right edge exactly
   like the validation queue, so the row actions stay visible instead of being
   cut mid-button (1440px) or hidden entirely (1280px). The two full-width
   colSpan rows (bucket separators, batch-approve toolbar) keep their own
   background — exclude them so the sticky white/grey fill doesn't paint over
   them. */
.email-table { overflow: visible; }       /* base `table` sets overflow:hidden (corner clip) which would break the sticky cell; the wrapper is the scrollport */
.email-table-wrap { border-radius: 10px; }
.email-table thead th:last-child,
.email-table tbody tr:not(.email-bucket-row):not(.email-batch-row) td:last-child {
  position: sticky;
  right: 0;
  text-align: right;
  /* scroll-affordance: a soft left-edge shadow so the pinned Ações column reads
     as floating over content that scrolls beneath it (the D-01 affordance). */
  box-shadow: -6px 0 6px -6px rgba(15, 23, 42, 0.18);
}
.email-table thead th:last-child { background: var(--ol-gray-50); z-index: 6; }
.email-table tbody tr:not(.email-bucket-row):not(.email-batch-row) td:last-child {
  background: #fff;
  z-index: 1;
}

/* D-09 — doc-types table (Settings): the Validade cell held long validity-kind
   labels that wrapped to 2–3 lines, bouncing row height 34–100px. Keep it on
   one line. Scoped via `:has(td code)` so it hits ONLY the doc-types table (the
   sole `.settings-users-table` whose first column renders a <code> element) —
   the shared-class users/samples tables are untouched. */
.settings-users-table:has(td code) td:nth-child(4) { white-space: nowrap; }

/* --- Folha de Ponto results: mobile card layout (P3 #18) ---
   The results table is a grouped table (entry header row + one row per
   candidate + optional expanded-docs row), so it can't reuse
   table.responsive-cards verbatim — the group rows must stay full-width
   labels while only candidate rows become cards. Mirrors the
   responsive-cards card chrome (border, radius, shadow). Likely used on a
   phone at the obra gate. */
@media (max-width: 768px) {
  table.roster-results,
  table.roster-results tbody {
    display: block;
  }

  table.roster-results thead {
    display: none;
  }

  /* Entry group header — a plain full-width label between cards. */
  table.roster-results tbody tr.roster-group-row,
  table.roster-results tbody tr.roster-note-row {
    display: block;
  }
  table.roster-results tbody tr.roster-group-row td,
  table.roster-results tbody tr.roster-note-row td {
    display: block;
    background: transparent !important;
    border: none !important;
    padding: 0.6rem 0.25rem 0.25rem;
  }

  /* Candidate rows become cards: worker info on top, estado bottom-left,
     ação top-right. */
  table.roster-results tbody tr:not(.roster-group-row):not(.roster-note-row):not(.roster-expanded-row) {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.25rem 0.75rem;
    padding: 0.75rem;
    margin-bottom: 0.5rem;
    border: 1px solid var(--ol-gray-200);
    border-radius: 8px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  }
  table.roster-results tbody tr:not(.roster-group-row):not(.roster-note-row):not(.roster-expanded-row) td {
    padding: 0;
    border: none;
  }
  /* Pico sets list-style on the LI, so the warnings <ul>'s inline
     listStyle:none doesn't reach it — with td padding zeroed the square
     markers would float in the gutter outside the card border. */
  table.roster-results tbody td ul,
  table.roster-results tbody td ul li {
    list-style: none;
  }
  table.roster-results tbody tr:not(.roster-group-row):not(.roster-note-row):not(.roster-expanded-row) td:nth-child(1) {
    grid-column: 1;
    grid-row: 1 / 3;
  }
  table.roster-results tbody tr:not(.roster-group-row):not(.roster-note-row):not(.roster-expanded-row) td:nth-child(2) {
    grid-column: 1 / -1;
    grid-row: 3;
    font-size: 0.85rem;
  }
  table.roster-results tbody tr:not(.roster-group-row):not(.roster-note-row):not(.roster-expanded-row) td:nth-child(3) {
    grid-column: 2;
    grid-row: 1;
    text-align: right;
  }

  /* Expanded docs row — full-width panel attached under its card. */
  table.roster-results tbody tr.roster-expanded-row {
    display: block;
    margin: -0.5rem 0 0.5rem;
    border: 1px solid var(--ol-gray-200);
    border-top: none;
    border-radius: 0 0 8px 8px;
    background: #fff;
  }
  table.roster-results tbody tr.roster-expanded-row td {
    display: block;
    border: none;
  }
}

/* =========================================================================
   imp_plan1 Point 1 P1a — global entity popup (nav-stack breadcrumb + body).
   Disjoint region appended at EOF so it never collides with concurrent
   styles.css edits (valpopup-affordances / imp-p3-css). Uses the existing
   .row-detail-back button style; only adds the breadcrumb trail layout + the
   placeholder body (P1b–P1f replace the body with real panels). Responsive:
   the breadcrumb wraps on narrow viewports rather than overflowing.
   ========================================================================= */
/* imp_plan3 T7 — canonical back/breadcrumb chrome shared by BOTH nav-stack
   pop-up hosts (the global entity popup AND the validador). Promoted from the
   former .entity-popup-breadcrumb* rules and now the SINGLE stylesheet behind
   <FrameNavHeader>; the validador's old inline breadcrumb styles are migrated
   onto these classes so the two hosts no longer drift. */
.frame-nav-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2, 0.5rem);
  font-size: 0.85rem;
  margin-bottom: 0.85rem;
}
.frame-nav-header__trail {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.15rem;
  min-width: 0;
  color: var(--ol-gray-600, #475569);
}
.frame-nav-header__crumb {
  display: inline-flex;
  align-items: center;
}
/* The "›" separator lives inside the FOLLOWING crumb (before its label); the
   surrounding JSX whitespace collapses in HTML, so space it explicitly on both
   sides instead of relying on the trail `gap` (which only spaces crumb wrappers,
   leaving the chevron flush against its own label). */
.frame-nav-header__crumb [aria-hidden='true'] {
  margin: 0 0.25rem;
}
.frame-nav-header__trail button {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--ol-accent-ink, #0b7a63); /* a11y: raw accent text ~1.45:1 on white */
  cursor: pointer;
  text-decoration: underline;
}
.frame-nav-header__trail button:disabled {
  cursor: default;
  opacity: 0.6;
  text-decoration: none;
}
.frame-nav-header__trail [aria-current='page'] {
  font-weight: 600;
  color: var(--ol-primary, #030531);
}
.frame-nav-header__reset {
  margin-left: auto;
}
.entity-popup-placeholder {
  padding: var(--sp-4, 1rem);
  color: var(--ol-gray-700, #334155);
}
.entity-popup-placeholder code {
  font-size: 0.85em;
  word-break: break-all;
}
.entity-popup-placeholder__note {
  color: var(--ol-gray-500, #64748b);
  font-size: 0.85rem;
}

/* ── Combobox (searchable selector) ───────────────────────────────────────
   Reusable searchable dropdown replacing native <select>. The trigger looks
   like a native select at rest; the panel is portalled to <body> with fixed
   positioning so it is never clipped inside scrolling containers / modals.
   The search field inside the panel renders only when the option count is at
   or above the threshold (see Combobox.tsx `searchThreshold`, default 7). */
.ol-combobox {
  position: relative;
  display: inline-block;
  width: 100%;
}
.ol-combobox__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: 0.9rem;
  line-height: 1.4;
  text-align: left;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--ol-gray-300);
  border-radius: 6px;
  background: #fff;
  color: inherit;
  cursor: pointer;
  margin: 0;
}
.ol-combobox__trigger:disabled {
  background: var(--ol-gray-100);
  cursor: not-allowed;
  opacity: 0.8;
}
.ol-combobox__trigger:focus-visible {
  /* Single source of truth with the text-like form-control :focus-visible rule
     near the top of this file — keep these four declarations identical, or an
     <input> and the Combobox beside it glow two different ways. */
  outline: 2px solid var(--ol-accent, #21f2c0);
  outline-offset: 1px;
  border-color: var(--ol-accent);
  box-shadow: 0 0 0 3px rgba(33, 242, 192, 0.25);
}
/* C-06: a POINTER click leaves :focus set but not :focus-visible — Pico then
   paints a stray blue focus halo under the mouse. Suppress the ring for
   pointer focus while keeping the keyboard a11y ring (:focus-visible above). */
.ol-combobox__trigger:focus:not(:focus-visible) {
  box-shadow: none;
  outline: none;
}
.ol-combobox__trigger[aria-expanded="true"] {
  border-color: var(--ol-accent);
}
.ol-combobox__value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ol-combobox__value--placeholder {
  color: var(--ol-gray-500);
}
/* The live search query mirrored into the trigger while the user types (there is
   no visible search box). A leading magnifier + accent tint distinguishes "what
   I'm typing" from "what's currently selected". */
.ol-combobox__value--query {
  color: var(--ol-primary, #111827);
}
.ol-combobox__value--query::before {
  content: '⌕ ';
  color: var(--ol-gray-500);
}
.ol-combobox__chevron {
  flex: 0 0 auto;
  color: var(--ol-gray-500);
  font-size: 0.7rem;
  line-height: 1;
}
.ol-combobox__panel {
  position: fixed;
  z-index: 2200; /* above modal-overlay (1000) */
  background: #fff;
  border: 1px solid var(--ol-gray-200);
  border-radius: 8px;
  box-shadow: var(--shadow-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  max-height: 18rem;
}
/* No visible search box: the typing surface is clipped offscreen but stays
   focusable (NOT display:none) so it still summons the mobile soft keyboard and
   is reachable by assistive tech. The query is shown by mirroring it into the
   trigger (.ol-combobox__value--query). */
.ol-combobox__search-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}
.ol-combobox__list {
  list-style: none;
  margin: 0;
  padding: 0.25rem;
  overflow-y: auto;
  overflow-x: hidden;
}
.ol-combobox__option {
  padding: 0.45rem 0.6rem;
  border-radius: 5px;
  cursor: pointer;
  font-size: 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  white-space: normal;
  word-break: break-word;
}
.ol-combobox__option[aria-disabled="true"] {
  color: var(--ol-gray-500);
  cursor: default;
}
.ol-combobox__option--active {
  background: var(--ol-gray-100);
  box-shadow: inset 3px 0 0 var(--ol-accent);
}
.ol-combobox__option--selected {
  font-weight: 600;
}
.ol-combobox__check {
  flex: 0 0 auto;
  color: var(--ol-primary);
  width: 1rem;
}
.ol-combobox__empty,
.ol-combobox__status {
  padding: 0.5rem 0.6rem;
  font-size: 0.85rem;
  color: var(--ol-gray-500);
}
.ol-combobox__status--error {
  color: var(--ol-red, #dc2626);
}
/* sub-create-shortcut: sticky "+ Adicionar" footer row. flex:0 0 auto keeps it
   pinned below the scrollable list (the panel is a flex column); the top border
   separates it from the options. */
.ol-combobox__create {
  flex: 0 0 auto;
  width: 100%;
  text-align: left;
  padding: 0.55rem 0.6rem;
  border: none;
  border-top: 1px solid var(--ol-gray-200);
  background: #fff;
  color: var(--ol-primary);
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
}
.ol-combobox__create:hover,
.ol-combobox__create:focus-visible {
  background: var(--ol-gray-100);
}

/* ── Top-layer toast host (audit-toast-toplayer) ───────────────────────────
   #toast-host wraps react-hot-toast's <Toaster> in a `popover="manual"` element
   (see ToastHost.tsx) so toasts paint in the browser TOP LAYER — above any open
   native <dialog>.showModal() (the validador RowDetailModal etc.), which no
   z-index can beat. The UA stylesheet styles `[popover]` as a centred, bordered,
   padded box with `inset: 0; margin: auto`; neutralise ALL of that so the host
   is a zero-footprint transparent passthrough and the Toaster keeps its OWN
   fixed top-right positioning. */
#toast-host,
#toast-host:popover-open {
  inset: auto;
  margin: 0;
  border: 0;
  padding: 0;
  background: transparent;
  overflow: visible;
  max-width: none;
  max-height: none;
  width: auto;
  height: auto;
  color: inherit;
}

/* --- 404 catch-all (uxfollow01 not-found-route) --- */
/* A garbage URL can be one long unbroken token; let the echoed path wrap
   instead of overflowing the centered .page-status column. */
.page-status code {
  overflow-wrap: anywhere;
  max-width: 100%;
}
/* upload-progress-bars: slim per-file XHR upload progress bar (DashboardUpload) */
progress.file-card__progress {
  display: block;
  width: 100%;
  height: 0.4rem;
  margin: 0.35rem 0 0;
  /* Brand-blue fill instead of Pico's default azure. accent-color drives the
     native bar in modern browsers; the pseudo-elements cover WebKit/Gecko. */
  accent-color: var(--ol-blue);
}
progress.file-card__progress::-webkit-progress-value { background: var(--ol-blue); }
progress.file-card__progress::-moz-progress-bar { background: var(--ol-blue); }
/* Tabular figures so the "N%" label keeps a stable width as digits change,
   which keeps the bars' right edge from jittering across rows. */
.file-card__status { font-variant-numeric: tabular-nums; }
/* ── ⌘K discoverability pill (cmdk-discoverability) ─────────────────────────
   Visible affordance for the global command palette, portaled by
   CommandPalette.tsx into a `.cmdk-hint-slot` container it inserts as the FIRST
   child of Layout's `.sidebar-nav` (the app has no topbar). Being first in the
   DOM means it also reads first for keyboard Tab / screen readers — visual and
   focus order agree (no `order` hack). React only positions its own link nodes,
   never this foreign container, so a leading sibling is reconciler-safe.
   Styled to read as sidebar chrome (muted-on-primary like the nav links), with
   a bordered pill outline so it reads as clickable rather than as a 12th nav
   destination. Left padding carries an extra 2px so the search glyph aligns with
   the nav-link icon column (links have a 3px transparent border-left; the pill
   has a 1px border all round). */
.cmdk-hint-slot {
  display: block;
  width: 100%;
}

.cmdk-hint {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 0.6rem;
  padding: 0.5rem 0.75rem 0.5rem calc(0.75rem + 2px);
  width: 100%;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.8rem;
  font-weight: 500;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
}

.cmdk-hint:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.1);
}

.cmdk-hint:focus-visible {
  outline: 3px solid var(--ol-accent);
  outline-offset: 2px;
}

.cmdk-hint kbd {
  margin-left: auto;
  font-family: inherit;
  font-size: 0.68rem;
  padding: 0.08rem 0.35rem;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.75);
  white-space: nowrap;
}

/* On phones the sidebar is a touch drawer — a keyboard-shortcut badge is
   noise there; the pill itself stays (first tappable palette entry point). */
@media (max-width: 768px) {
  .cmdk-hint kbd {
    display: none;
  }
}
/* ── Email send-health strip (uxfollow01 email-send-health-strip) ──────────
   Compact always-visible glance row at the top of the Emails page: send mode
   + held-for-review + failed-in-view counts (EmailSendHealthStrip.tsx).
   Counting mirrors the estado derivation — is_dry_run wins over outbox_status,
   so Simulado rows never read as real sends/failures. */
.email-health-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 1.25rem;
  padding: 0.45rem 0.75rem;
  margin-bottom: 0.6rem;
  border: 1px solid var(--ol-gray-200);
  border-radius: 8px;
  background: var(--ol-gray-50);
  font-size: 0.82rem;
  line-height: 1.3;
}
.email-health-strip__item {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  white-space: nowrap;
}
.email-health-strip__label {
  color: var(--ol-gray-600);
  font-weight: 500;
}
.email-health-strip__count {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* Subtle warning treatment when any visible send failed — amber, not red:
   it's a "look at this", not an alarm (the row itself carries the red badge). */
.email-health-strip__item--warn .email-health-strip__label,
.email-health-strip__item--warn .email-health-strip__count {
  color: var(--ol-amber-text);
}
.email-health-strip__item--warn {
  background: var(--ol-amber-bg);
  border: 1px solid var(--ol-amber-border);
  border-radius: 6px;
  padding: 0.1rem 0.45rem;
  margin: -0.1rem 0;
}

/* ── css-a11y-pass (uxfollow01 wave 2) — WCAG pass, CSS-only ─────────────────
   Four fixes appended together so the cascade order is explicit (these are
   deliberate tablet/mobile overrides of earlier base rules):
   1. wide unwrapped tables → horizontal scroll at tablet widths;
   2. ≥44px tap targets on mobile (WCAG 2.5.5);
   3. request-cart group header wraps at 375px instead of clipping;
   4. expanded hit-area for the tiny inline InfoHint icon on mobile. */

/* 1 — Wide tables: .main-content clips horizontal overflow (overflow-x:hidden),
   so any table wider than a tablet viewport was simply CUT OFF with no way to
   reach the hidden columns — only the validation queue ever got a scroll
   wrapper (.validation-queue-wrap). CSS-only equivalent for the unwrapped
   siblings: turn the <table> element itself into its own scroller.
   `width: fit-content` keeps the card chrome hugging the rows (a plain
   display:block table would paint a full-width card behind shrink-to-fit
   rows); `max-width: 100%` caps it at the viewport so wide content scrolls.
   Exclusions: .validation-queue-table + .email-table already live inside
   overflow-x:auto wrappers (and the queue's sticky-right actions column needs
   the wrapper to be the scrollport). ⚠ That claim was FALSE for one of the five
   `.validation-queue-table` consumers until nowrap01 item C: LostDocsPanel had
   no wrapper, so this exclusion left its 760px-min-width table clipped dead by
   `.main-content { overflow-x: hidden }` (130px of it unreachable at a 960px
   viewport, taking the whole "Recebido" column with it). It is wrapped in
   `.validation-queue-wrap` now, which is what makes this exclusion honest —
   re-check that before adding a sixth consumer of the class; .responsive-cards / .roster-results
   re-lay themselves out as cards at ≤768px; tables with an inline width
   (SubPortal) keep their own handling. Popover safety: base `table` already
   sets overflow:hidden (rounded corners), so any in-table dropdown must
   already be portalled (position:fixed at body) — this rule clips nothing
   that wasn't clipped before, and portalled menus still escape.
   Known, accepted trade-off (same one .validation-queue-wrap made): inside a
   scrolling table the sticky `thead th { top: 0 }` sticks to the table's own
   scrollport, so headers stop pinning on page scroll — at tablet widths only. */
@media (max-width: 1024px) {
  .main-content table:not(.validation-queue-table):not(.email-table):not(.responsive-cards):not(.roster-results):not(.doc-list-table):not([style*="width"]) {
    display: block;
    width: fit-content;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* 2 — Tap targets ≥44px on mobile (WCAG 2.5.5). Only controls whose growth
   can't break a layout: tab strips just get taller, pagination/nav rows are
   single-purpose rows. Desktop densities are untouched. */
/* NB units are px, NOT rem: Pico sets `--pico-font-size: 15px` (see :root), so
   the root font is 15px and 2.75rem would be only 41.25px — under the 44px
   WCAG 2.5.5 target. Hard px keeps it a true 44px regardless of root scaling. */
@media (max-width: 768px) {
  .modal-content .modal-close {
    width: 44px;   /* was 2rem/32px */
    height: 44px;
  }
  .tabs__tab,
  .tab-bar button,
  .settings-tab,
  .pagination button,
  .pagination .ol-combobox__trigger,
  .request-cart-pill {
    min-height: 44px;
  }
  .sidebar-nav a {
    min-height: 44px; /* drawer nav rows were ~38px */
  }
  .sidebar-logout-btn {
    min-width: 44px;
    min-height: 44px;
  }
  .switch {
    min-height: 44px; /* the 18px track alone is far under target size */
  }
  /* Inline per-item "×" removals in the request cart set their padding inline
     (0 6px) — raise the box via min sizes, which the inline style doesn't pin.
     36px, not 44: a 44px bordered square beside 0.85rem list text balloons
     every row — the "where it doesn't break layout" carve-out. */
  .request-cart-drawer .modal-body button.outline {
    min-width: 36px;
    min-height: 36px;
  }
}

/* 3 — Request-cart GROUP header (RequestCartDrawer <section><header>): the
   header row lays out name · subtitle · spacer · count · "Remover grupo" with
   inline `display:flex` and no wrap, so at 375px the button clipped off the
   card edge. The inline style doesn't set flex-wrap, so plain CSS can supply
   it; long sub names get a break opportunity instead of forcing overflow. */
.request-cart-drawer .modal-body section > header {
  flex-wrap: wrap;
  row-gap: 0.35rem;
}
.request-cart-drawer .modal-body section > header > strong {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* (No InfoHint hit-area expansion: an inline `::after { inset:-8px }` overlay
   would extend the icon's onClick dead-zone — the hint calls
   preventDefault+stopPropagation — 8px downward over the input/checkbox that
   often sits directly below it in a Settings label, swallowing edge taps. On
   touch the icon's only affordance is a native `title` tooltip, which doesn't
   render on tap anyway, so the enlarged target buys nothing. WCAG 2.5.8's
   inline-target exception applies; left as-is deliberately.) */

/* ── P-R1 (bughunt0707) — shared <TableScroll> wide-table scroll wrapper ─────
   `.main-content { overflow-x: hidden }` (~line 398, kept: page layout depends
   on it) hard-clips any table wider than the content column — whole columns,
   including their action buttons, become unreachable (B1-P1 queue, B3-H1
   settings, B4-2 accuracy, B2-6 emails). The fix children should apply: wrap
   the wide table in <TableScroll> (components/TableScroll.tsx) — a
   self-contained overflow-x:auto scroller that works regardless of ancestor
   clipping, with a right-edge fade + "→" cue while columns remain off-screen
   and keyboard scrolling (focusable region, Arrow/Home/End). These classes are
   the shared primitive; per-page adoption is the child tasks' job. Append-only
   block; light theme only. */
.table-scroll {
  position: relative;
  max-width: 100%;
}
.table-scroll__viewport {
  overflow-x: auto;
  max-width: 100%;
  -webkit-overflow-scrolling: touch;
  /* Re-establish the card rounding the scroller would otherwise clip — same
     trade .validation-queue-wrap already makes. */
  border-radius: 10px;
}
/* Keyboard-focus ring on the scroll region (it's tabbable when scrollable). */
.table-scroll__viewport:focus-visible {
  outline: 2px solid var(--ol-primary, #2563eb);
  outline-offset: 2px;
}
/* Right-edge affordance: white fade + "→", shown only while more columns sit
   off-screen to the right (.table-scroll--more toggled by the component).
   pointer-events:none — purely visual, never intercepts row clicks. Fade is
   white-based because tables are white cards on the light-only theme. */
.table-scroll__cue {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 44px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 0.4rem;
  pointer-events: none;
  border-radius: 0 10px 10px 0;
  background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.9));
  color: var(--ol-gray-600, #4b5563);
  font-size: 1.05rem;
  font-weight: 700;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.table-scroll--more .table-scroll__cue {
  opacity: 1;
}
/* Pico gives <table> a bottom margin; inside the scroller that margin would
   put daylight between the rows and the horizontal scrollbar. Also neutralise
   the css-a11y-pass `@media (max-width:1024px)` rule above, which turns any
   bare `.main-content table` into its OWN scroller (display:block;
   width:fit-content; overflow-x:auto). A table wrapped in <TableScroll> must
   NOT self-scroll — the wrapper's viewport has to be the scrollport so its
   overflow drives the "→" cue + keyboard scrolling. Without this, a wrapped
   table on a tablet/phone (≤1024px — the exact widths this whole change
   targets) would scroll inside itself, leave the viewport non-overflowing, and
   the wrapper would silently do nothing. `!important` is required only because
   that rule's long `:not()` chain out-specifies a plain descendant selector;
   the selector is scoped to wrapped tables so it's inert everywhere else.
   NOTE for child tasks: like every overflow scroller (incl. the pre-existing
   .validation-queue-wrap), a sticky `thead th { top:0 }` pins to the viewport,
   not the page — so a wrapped table stops pinning its header on PAGE scroll.
   Prefer wrapping tables where reaching hidden columns matters more than a
   sticky header, or that have none. */
.table-scroll__viewport > table {
  margin-bottom: 0;
  display: table !important;
  max-width: none !important;
  overflow: visible !important;
}
/* --- crossdoc-redesign: "Alertas entre documentos" tab (CrossDocAlerts.tsx) ---
   Group headers previously carried a coloured .flag-line/.flag-dot bullet; per
   operator feedback the dots were removed (the JSX no longer renders them) and
   the title font is overridden off the global Clash Display heading face back to
   the body sans, so these read as plain section labels rather than display
   titles (e.g. "Conflito de identidade · Nome"). */
.crossdoc-group { margin-bottom: 1.5rem; }
.crossdoc-group__title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.5rem;
  font-family: var(--pico-font-family, system-ui, sans-serif);
  word-spacing: normal;
}
.crossdoc-group__count { font-weight: 400; font-size: 0.8rem; color: var(--ol-gray-500); }
.crossdoc-identity { margin-top: 2rem; }
/* doclist-column-overflow: the Estado column (doc-list-table td:nth-child(3))
   wraps its StatusFlag pills via an inline-style `flexWrap:wrap` container.
   Originally this needed a scoped opt-out because the shared `.flag-line`
   forced nowrap on every consumer; nowrap01-b inverted that default (see the
   base `.flag-line`/`.flag-dot` rules above), so col 3 needs NO dedicated
   rule any more — every `.flag-line`, here included, wraps and pins its dot
   to the first line by default. Nothing to see: this comment stays only so a
   reader searching for "Estado column overflow" lands somewhere that
   explains why there's no rule below it.

   The Validade column (td:nth-child(4)) is different: it is only 14% wide
   and its single flag carries the longest labels in the table — `Válido até
   21/03/2027`, `Expirado há 137 dias`, and the periodic_monthly `Fora de
   prazo (Mar 2026)` (validityInfo() in DocumentList.tsx). `display: flex`
   makes that ONE flag fill the cell instead of shrink-to-fitting to its
   max-content — without it the flag box itself grows past the cell, so a
   long token never sees a line edge and `break-word` never triggers (a
   38-char token still overflowed by 178px at 768). This is the one
   genuinely column-specific declaration left: col 3 holds SEVERAL flags
   side by side in a `flexWrap:wrap` row and must stay `inline-flex` (the
   base default), or `flex` would stack them one per line instead. Same
   "flex iff single flag" rule as the validation-queue Motivo cell above.
   `overflow-wrap: break-word` DOES still need re-asserting here — the base
   `.flag-line` rule now defaults to `anywhere` (measured: `break-word` alone
   let col 3's shrink-to-fit multi-flag min-content sizing overflow, see the
   base rule's comment), and `anywhere` on a prose date is exactly the
   shattering this override exists to prevent (`21/03/2027` → one character
   per line). The `display: flex` fill is what GIVES this cell a real line
   edge to break `break-word` against in the first place — without it the
   flag box itself grows past the cell, so a long token never sees an edge
   and break-word never triggers (a 38-char token still overflowed by 178px
   at 768). This is the one genuinely column-specific declaration left: col 3
   holds SEVERAL flags side by side in a `flexWrap:wrap` row and must stay
   `inline-flex` (the base default, which needs `anywhere`'s min-content
   shrink precisely because it has no line edge), or `flex` would stack them
   one per line instead. Same "flex iff single flag" rule as the
   validation-queue Motivo cell above.

   Known residual, deliberately not chased here: below ~500px the column is
   only ~28-46px wide, so a single token (`21/03/2027`) still spills ~30-48px.
   `.doc-list-table` is excluded from the responsive block-scroll transform
   (see the `@media (max-width: 768px)` rule above), so a 6-column fixed table
   at phone width is a pre-existing layout gap — one this cell-level fix
   cannot close, and which `anywhere` would "solve" only by making the row
   300px tall. Fixing it properly means giving this table a narrow-width
   treatment, not tuning the flag. */
.doc-list-table td:nth-child(4) .flag-line {
  display: flex;
  overflow-wrap: break-word;
}
/* poptabs01 docdetail-hierarchy — DocumentDetailPanel type/containment
   hierarchy pass. The panel is a colSpan=6 <td> expansion rendered directly
   under its parent DocumentList row, but it read at the SAME visual weight
   as that row (tbody td is 0.9rem on white; the panel's own .text-sm was
   0.85rem on the same white, i.e. barely lighter) — the expansion looked
   like a sibling row rather than nested child content. Two changes, both
   scoped to this panel only:
   1. `.doc-detail-panel` — a step smaller body size, a muted body color,
      a soft grey fill (reusing the same --ol-gray-50 tint the table already
      uses for thead/hover, so it reads as "part of this system" rather than
      a new color), and a left accent border + extra left inset so the block
      visually hangs off the row above it instead of floating flush left.
   2. `.doc-detail-panel__section-label` — the plain, uncolored <strong>
      sub-headings ("Avisos", "Campos extraídos", "Declaração periódica",
      "Notas / Observações", "Histórico") get the same small-caps "eyebrow
      label" treatment as `thead th` (uppercase, letter-spacing, gray-500,
      600 weight) instead of full-strength bold body text — so they read as
      quiet section dividers, not headings competing with the row's own
      Tipo/Estado. Deliberately NOT applied to the colour-coded alert
      <strong> labels (Alertas/Avisos de validação, Apto Condicionalmente,
      Trabalhador menor, Alertas entre documentos, the workspecif banner) —
      those already carry their own severity-coded callout box and need to
      stay prominent; dimming them would hide compliance-relevant warnings. */
.doc-detail-panel {
  background: var(--ol-gray-50);
  border-left: 3px solid var(--ol-gray-300);
  padding-left: 1.25rem;
  color: var(--ol-gray-600);
  font-size: 0.8125rem;
}

.doc-detail-panel__section-label {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ol-gray-500);
}
/* ===========================================================================
   empresa-popup-tabs — CompanyDetailsPanel "Registo da Empresa" identity grid
   (appended block; shared file). Edit-in-place: the read grid and the edit grid
   share one container, so the inputs must sit flush inside each cell (Pico's
   default input margin would otherwise break the grid rhythm).
   =========================================================================== */
.empresa-identity__grid label {
  display: block;
}
.empresa-identity__grid--editing input,
.empresa-identity__grid--editing textarea {
  margin: 0.15rem 0 0;
  width: 100%;
}

/* =========================================================================
   empresa-panel-polish (batch poptabs01, wave 2) — CompanyDetailsPanel
   follow-up polish. Scoped, additive block; no existing rule is altered.
   ========================================================================= */

/* C8 — even vertical rhythm between the stacked sections inside each tab
   panel. The sections previously carried a mix of mb-4 / mb-5 / mb-6 / mt-6
   hooks, so gaps read unevenly. This normalizes the top/bottom margin of the
   direct <section> / .stats-grid children of a panel so the spacing is uniform
   regardless of which utility each section still carries. Higher specificity
   than the .mb-* / .mt-* utilities, so it wins without touching them. */
.empresa-panel-tab > section,
.empresa-panel-tab > .stats-grid {
  margin-top: 0;
  margin-bottom: 1.5rem;
}
.empresa-panel-tab > section:last-child,
.empresa-panel-tab > .stats-grid:last-child {
  margin-bottom: 0;
}

/* C5 — an Emails row must not force the narrow popup card to overflow when the
   address is long: the row wraps, and the address column takes min-width:0 so
   it can shrink, truncating with an ellipsis rather than pushing the switch /
   buttons off the card. */
.sub-email-row {
  flex-wrap: wrap;
}
.sub-email-row__addr {
  flex: 1 1 12rem;
  min-width: 0;
}
.sub-email-row__addr strong {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
/* ── Equipment Duplicados surface (EquipmentDuplicates.tsx) ───────────────────
   The machine analogue of the worker Duplicados list. Class-driven (was ~40
   inline style objects that couldn't express hover/focus and drifted from the
   palette). Radius unified to 8px; font sizes collapsed to 0.75/0.8/0.9rem;
   all colour from real --ol-* tokens. */
.eqdup-status {
  padding: 1rem;
}
.eqdup-status__error {
  color: var(--ol-red);
}
.eqdup-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.eqdup-card {
  border: 1px solid var(--ol-gray-200);
  border-radius: 8px;
  padding: 0.75rem;
  background: #fff;
}
/* Header meta: sub label + optional cross-sub badge on the left, the
   match kind + score on the right. Wraps + min-widths so a long
   "Sub A ↔ Sub B" never collides with the score on a narrow tab. */
.eqdup-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 0.35rem 0.75rem;
  margin-bottom: 0.5rem;
  font-size: 0.75rem;
  color: var(--ol-gray-600);
}
.eqdup-meta__subs {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  min-width: 12rem;
}
.eqdup-meta__score {
  white-space: nowrap;
}
/* The two candidate cards: side-by-side but wrap + min-width so they stack
   (instead of squishing) on a narrow tab. */
.eqdup-sides {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: stretch;
}
.eqdup-side {
  flex: 1 1 14rem;
  min-width: 12rem;
  text-align: left;
  padding: 0.6rem 0.75rem;
  border-radius: 8px;
  border: 1px solid var(--ol-gray-200);
  background: #fff;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.eqdup-side:hover {
  border-color: var(--ol-gray-400);
  background: var(--ol-gray-50);
}
.eqdup-side:focus-visible {
  outline: 2px solid var(--ol-blue);
  outline-offset: 2px;
}
/* Non-selectable (cross-sub) cards: no hover affordance, default cursor. */
.eqdup-side:disabled {
  cursor: default;
  opacity: 1;
}
.eqdup-side:disabled:hover {
  border-color: var(--ol-gray-200);
  background: #fff;
}
.eqdup-side--selected,
.eqdup-side--selected:hover {
  border: 2px solid var(--ol-blue);
  background: var(--ol-blue-50);
  padding: calc(0.6rem - 1px) calc(0.75rem - 1px); /* keep box size stable vs the 1px border */
}
.eqdup-side--drop,
.eqdup-side--drop:hover {
  background: var(--ol-gray-50);
}
/* Mute the dropped machine's name so the "will be eliminated" side reads as
   de-emphasised at a glance (the red hint carries the explicit wording). */
.eqdup-side--drop .eqdup-side__name {
  color: var(--ol-gray-500);
}
.eqdup-side__name {
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--ol-gray-900);
}
.eqdup-side__unnamed {
  color: var(--ol-gray-500);
  font-weight: 500;
}
.eqdup-side__inactive {
  margin-left: 6px;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--ol-gray-600);
}
.eqdup-side__meta {
  font-size: 0.8rem;
  color: var(--ol-gray-600);
  margin-top: 2px;
}
.eqdup-side__hint {
  font-size: 0.75rem;
  color: var(--ol-gray-600);
  margin-top: 2px;
}
.eqdup-side--selected .eqdup-side__hint {
  color: var(--ol-blue-700);
  font-weight: 600;
}
.eqdup-side--drop .eqdup-side__hint {
  color: var(--ol-red-600);
}
/* Error on its own full-width line above the action row (was inline before the
   button, where a long PT message reshaped/crowded the actions). */
.eqdup-error {
  margin-top: 0.5rem;
  font-size: 0.8rem;
  color: var(--ol-red);
}
.eqdup-actions {
  justify-content: flex-end;
  margin-top: 0.6rem;
}
/* Busy/disabled action buttons. Pico dims disabled buttons to opacity:.5, which
   drops the red-outline "A fundir…" label to ~2.3:1 (sub-AA). Restore full
   opacity so the busy label stays legible (Fundir red-on-white ~4.8:1, Associar
   white-on-navy ~19:1) and add a light-red busy fill on the destructive button
   as the non-clickable affordance. `aria-busy` + the "A …" label signal progress.
   Higher specificity than Pico's disabled rule; the primary (navy) button keeps
   its global fill, so only opacity is relaxed there. */
.eqdup-actions button:disabled {
  opacity: 1;
  cursor: default;
}
.eqdup-actions button.btn-danger:disabled {
  background: var(--ol-red-bg);
  border-color: var(--ol-red-border);
  color: var(--ol-red-600);
}

/* ════════════════════════════════════════════════════════════════════════
   fe-M4 residual (bughunt0713) — Pico [role=group] hijack neutraliser.
   Pico (loaded before this file) co-opts [role=group] as its fused
   input-group component: a whole-group focus ring via
   `[role=group]:has(…:focus) { --pico-group-box-shadow: … }` feeding the base
   rule's `box-shadow: var(--pico-group-box-shadow, …)`, plus inline-flex,
   width:100%, margin-bottom and 2rem side padding on child buttons. The app
   NEVER uses that component — the remaining role="group" carriers
   (AssignmentEditor / EquipmentAssignmentEditor obra checkbox lists) are plain
   stacked-label containers, so focusing ONE checkbox ringed the ENTIRE list
   (the "double-ring"). SupportWidget dropped the role instead (see
   supportWidgetKindToggle.test.tsx); removing it from the two editors is a
   sibling lane — this neutraliser makes the role harmless either way, for
   these carriers and any future accidental role="group".
   Same 0,1,0 specificity as Pico's base rule; this file loads later, so the
   cascade tie breaks our way (the :has() variants only set the custom
   property, never box-shadow itself). The carriers' inline styles already own
   their layout (display/flex-direction/margin), so resetting to div defaults
   here is inert for them and only strips Pico's group chrome. */
[role='group'] {
  display: block;
  width: auto;
  margin-bottom: 0;
  border-radius: 0;
  box-shadow: none;
  transition: none;
  vertical-align: baseline;
}
/* Pico inflates buttons inside a group to 2rem side padding (M4's "outsized"
   buttons). Restore Pico's own default form-element spacing. */
[role='group'] button,
[role='group'] [type='submit'],
[role='group'] [type='button'],
[role='group'] [role='button'] {
  --pico-form-element-spacing-horizontal: 1rem;
}

/* obra-popup-tabs (2026-07-28) — the PINNED ALERT ZONE in the entity panels.
   These blocks (cobertura provisória / validade em falta) describe the whole
   record and render above every tab, so the boundary between "always visible"
   and "this tab only" must be legible.

   Finding #4 (measured): the gap from the zone to the tab content was 11.25px —
   identical to the gap BETWEEN the alerts — so panel-wide state read as part of
   the first tab's section.
   Finding #5 (measured): the alerts were three unboxed coloured text lists
   sitting ABOVE an identity block that does get a bounded card, i.e. the most
   severe information carried the LEAST visual weight — while the obra popup's
   equivalent panel-wide state (DeactivatedBanner) is a bounded tinted box. Two
   treatments of one concept across the popups this change unifies. So the zone
   now gets the same bounded-block treatment, in a neutral amber-leaning grey so
   it frames both the amber (provisional) and red (blocker) children without
   fighting either one's own colour.

   Empty by construction when the record has no alerts (the children render
   nothing), so :empty keeps the rule from painting an empty box. */
.panel-pinned-alerts:not(:empty) {
  padding: 0.75rem 1rem;
  margin-bottom: 1.5rem;
  background: var(--ol-gray-50, #f9fafb);
  border: 1px solid var(--ol-gray-200, #e5e7eb);
  border-left: 3px solid var(--ol-yellow, #ca8a04);
  border-radius: 8px;
}

/* Finding #3 (measured): at 390px the pinned alerts ran to ~560px of a 784px
   card — 71% of the viewport, on EVERY tab, before any tab content. Pinning is
   what makes the position predictable (the point of the fix), but it must not
   push the tab content past the fold on a phone. Cap the zone and let it scroll
   INSIDE its own bounded box: the alerts stay always-visible and always in the
   same place, while the tab below stays reachable without a long scroll. */
@media (max-width: 768px) {
  .panel-pinned-alerts:not(:empty) {
    max-height: 38vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* obra-popup-tabs follow-up (2026-07-28) — aligned filter row.
   A row of filter controls aligned with `items-end` still reads as ragged when
   the controls have different intrinsic heights: Pico's native date input is
   ~54px, a Combobox ~40px and a `.btn-sm` ~34px, so bottom-aligning them puts
   their captions on three different lines. Normalising the control height gives
   the row one caption baseline and one control baseline. Applied to the obra
   popup's Histórico filters (finding #13); reusable for any similar row. */
.filter-row--aligned input,
.filter-row--aligned select,
.filter-row--aligned button {
  height: 2.5rem;
  margin-bottom: 0;
  padding-top: 0;
  padding-bottom: 0;
}

.filter-row--aligned label > span {
  /* One caption line-height for every column, so a caption can never sit lower
     than its neighbour because its control is taller. */
  line-height: 1.2;
}

/* sp-pending-affordance-ui (Defect 1) — "Criar novo" in WorkerMatcher stays a
   real, clickable button while the identity-cluster enrichment is pending (the
   guard's own toast still needs the click to fire), so `aria-disabled` — not the
   `disabled` attribute — is what marks it unavailable. Dim + `not-allowed` gives
   the operator the visual cue the enabled-looking button was missing; scoped to
   this one modifier class so no other button on the page is affected. */
.worker-create-btn--pending[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

/* sp-pending-affordance-ui (Defect 2) — the queue's identity-cluster pending/
   give-up notice (`queue-cluster-enrichment-notice`) sat in normal flow above a
   50-row page, so it scrolled out of view during triage — the exact moment an
   operator needs "agrupamento incompleto, confirme antes de aprovar" on screen.
   Pin it to the viewport top like the sidebar/table header treatments elsewhere
   in this file; opaque background so table rows don't show through underneath
   it, and a z-index below the sidebar (100) but above table content. */
.queue-cluster-enrichment-notice--sticky {
  position: sticky;
  top: 0;
  z-index: 20;
  background: #fff;
  padding: 0.15rem 0;
}

/* `.main-content { overflow-x: hidden }` makes the browser auto-resolve
   `overflow-y` to `auto`, which becomes the sticky reference frame instead of
   the viewport — and `.main-content` never itself scrolls (no fixed height),
   so plain `position: sticky` above renders as `position: static` in
   practice (verified in Chromium: the notice moved with the full scroll
   delta instead of pinning). Validation.tsx toggles this modifier once a
   sentinel confirms the notice has scrolled above the viewport top, switching
   it to a real `position: fixed` using its own captured geometry (`left`/
   `width`, set inline — necessarily dynamic, not hardcodable — since the
   sidebar width and `.main-content` padding both change across breakpoints). */
.queue-cluster-enrichment-notice--pinned {
  position: fixed;
  top: 0;
  z-index: 20;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}

/* sp-pending-affordance-ui (Defect 3) — the give-up copy ("Agrupamento por
   trabalhador incompleto — confirme os grupos antes de aprovar. Tentar
   novamente") wraps to a second line past the reserved `minHeight: 1.2em` slot
   at the 390px viewport (measured +16.00px table-position shift; 0.00px at
   768/1280/1440, where the sentence fits on one line). Reserve two lines' worth
   at narrow widths instead of one, so the slot is already tall enough before the
   text arrives and nothing shifts underneath it. */
@media (max-width: 480px) {
  .queue-cluster-enrichment-notice--sticky {
    min-height: 2.4em !important;
  }
}
