/* One box model for the whole client: a declared width or height is the
   border edge. Only <button> and <select> are border-box by default, which is
   how a <label> wearing .pcs-btn--sm stood 2px taller than the <button> beside
   it (BoxSizingTests). */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html, body {
    margin: 0;
    height: 100%;
    font-family: var(--pcs-font-ui, 'Inter', sans-serif);
    background: var(--pcs-bg-canvas, #f0f2f5);
    color: var(--pcs-fg-1, #293132);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Native controls do not inherit the page font; left alone they draw in the
   browser UI font. */
button, input, select, textarea, optgroup, option {
    font-family: var(--pcs-font-ui);
}

#app, .app-root {
    height: 100%;
}

/* Boot splash rendered inside #app until the Blazor WASM runtime starts and
   mounts the root component — and reused by App.razor's <Authorizing> state
   while the auth probe runs. Visible on first load and on every full reload
   (F5) — a WASM SPA re-bootstraps the whole runtime on a hard reload, so this
   window is unavoidable; styling it just makes it look intentional. */
.app-loading {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 20px;
    font-family: var(--pcs-font-ui, 'Inter', sans-serif);
    font-size: 14px;
    color: var(--pcs-fg-2, #8E9292);
    background: var(--pcs-bg-canvas, #f0f2f5);
}

.app-loading__logo {
    width: 220px;
    max-width: 60vw;
    height: auto;
}

/* Used by index.html's STATIC boot splash (shown before the WASM runtime is
   up, e.g. right after the auth redirect) — static HTML can't render
   PcsSpinner, so this one spinner stays global. The Blazor-rendered
   Authorizing splash uses <PcsSpinner Color="Brand"> with the same look. */
.app-loading__spinner {
    /* 0.7s to match PcsSpinner exactly, so the spinner doesn't jump when
       Blazor's Authorizing screen takes over from this static one. */
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 3px solid var(--pcs-light-grey-1, #DEDEDE);
    border-top-color: var(--pcs-atoll, #30597C);
    animation: app-loading-spin 0.7s linear infinite;
}

@keyframes app-loading-spin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .app-loading__spinner {
        animation: none;
    }
}

h1:focus {
    outline: none;
}

/* ── Focus restored after an overlay closed ────────────────────────
   Closing a dialog with Escape hands focus back to the button that opened it,
   which is right — focus must not fall to <body> — but the ring is not: the
   user dismissed something, they did not tab here. pcs.restoreFocus marks the
   element for that one restore and clears the mark on the next key, pointer
   press or blur, so Tab navigation keeps its ring.

   !important because component focus rings live in scoped CSS, whose generated
   [b-xxxxx] attribute outranks anything this file can say without it. The mark
   exists for a single interaction, so it cannot leave a control permanently
   unable to show focus. */
[data-pcs-focus-restored]:focus,
[data-pcs-focus-restored]:focus-visible {
    outline: none !important;
    box-shadow: none !important;
}

/* ── Key caps (design-system baseline, ADR-0044) ──────────────────
   <kbd> is the semantic element for "a key the user presses", and the
   shortcuts settings page prints it in two sizes: inline in a list row, and
   large in the detail pane that is about that one binding. Defined once here
   like .pcs-input, because it is the element's own look and not one feature's
   — the ribbon tooltips and the menus print key caps too. */
kbd {
    display: inline-block;
    padding: 2px 6px;
    border: 1px solid var(--pcs-border);
    border-bottom-width: 2px;
    border-radius: var(--pcs-radius-xs);
    background: var(--pcs-bg-3);
    font: normal normal 400 11px/1.2 var(--pcs-font-mono);
    color: var(--pcs-fg-1);
    white-space: nowrap;
}

kbd.kbd--lg {
    padding: 5px 11px;
    font-size: 14px;
}

/* ── PcsPressable (design-system baseline, ADR-0044) ────────────────
   The reset nine features were each writing before their own layout, plus the
   one focus ring they were each deriving or omitting. Everything else — size,
   spacing, background, selected-state colour — belongs to the caller's class,
   which is the point: this says "clickable", not "button".

   It is global rather than scoped to PcsPressable deliberately. A scoped
   `.pcs-pressable[b-x]` ties with the caller's `[b-y] .their-class` at (0,2,0)
   and then bundle order decides — which silently beat every caller whose file
   sorts before Shared/, so `font: inherit` won over their own type scale.
   Unscoped, it is (0,1,0) and every caller rule outranks it by construction. */
.pcs-pressable {
    appearance: none;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    text-align: inherit;
    cursor: pointer;
}

.pcs-pressable:focus-visible {
    outline: 2px solid var(--pcs-focus-color);
    outline-offset: 1px;
}

.pcs-pressable:disabled {
    cursor: default;
}

/* ── PcsInlineRename (design-system baseline, ADR-0044) ──────────────
   Borderless by design: the field stands where a label was, so it should not
   redraw the row. The focus ring is the only chrome, and it is the shared one —
   the three copies this replaced had a 30px cyan border, a 24px one, and none.
   Global for the same reason as .pcs-pressable above: scoped, it ties with the
   caller's rule and bundle order silently decides which type scale wins. */
.pcs-inline-rename {
    width: 100%;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
}

/* Field over a hidden copy of its own text, both in one grid cell: the copy
   sets the width, the field covers it. white-space: pre keeps trailing spaces
   counted, and the 2ch floor leaves somewhere to type after clearing it. */
.pcs-inline-rename__sizer {
    display: inline-grid;
    min-width: 2ch;
    max-width: 100%;
}

.pcs-inline-rename__sizer::after {
    content: attr(data-value);
    visibility: hidden;
    white-space: pre;
    grid-area: 1 / 1;
    font: inherit;
    padding: inherit;
}

.pcs-inline-rename__sizer > .pcs-inline-rename {
    grid-area: 1 / 1;
    width: 100%;
    min-width: 0;
}

.pcs-inline-rename:focus-visible {
    outline: 2px solid var(--pcs-focus-color);
    outline-offset: 1px;
}

/* ── Modal placement (design-system baseline, ADR-0044) ─────────────
   A card centred by flexbox lands on a half pixel whenever the space left
   over is odd, or the card is sized in vw — and everything inside it then
   renders a pixel off its twin elsewhere: the settings dialog's panel toggle
   sat at x.55 beside the project page's at a whole pixel. So a modal card is
   placed on whole pixels: it says how wide it wants to be (--pcs-modal-w)
   and, if it has a fixed height, how tall (--pcs-modal-h, with --sized).
   Global because every modal shell wears it, scrim included; the card's
   look stays the shell's. */
.pcs-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 600; /* above page chrome; below the snackbar (700) and menus/popovers (800) */
    background: rgba(0, 0, 0, 0.35);
    display: flex;
    align-items: center;
    justify-content: flex-start;
    --pcs-modal-inset: 24px;
    /* The height a fixed-height card may take: the viewport inside the inset. */
    --pcs-modal-room: calc(100vh - 2 * var(--pcs-modal-inset));
    padding: var(--pcs-modal-inset);
}

/* Each placement is declared twice: plainly, then on whole pixels. A browser
   without round() drops the second and still centres the card. */
.pcs-modal-card {
    width: min(var(--pcs-modal-w), 100%);
    width: round(down, min(var(--pcs-modal-w), 100%), 1px);
    margin-inline-start: calc((100% - min(var(--pcs-modal-w), 100%)) / 2);
    margin-inline-start: round(down, calc((100% - min(var(--pcs-modal-w), 100%)) / 2), 1px);
}

.pcs-modal-card--sized {
    align-self: flex-start;
    height: min(var(--pcs-modal-h), var(--pcs-modal-room));
    height: round(down, min(var(--pcs-modal-h), var(--pcs-modal-room)), 1px);
    margin-block-start: calc((var(--pcs-modal-room) - min(var(--pcs-modal-h), var(--pcs-modal-room))) / 2);
    margin-block-start: round(down, calc((var(--pcs-modal-room) - min(var(--pcs-modal-h), var(--pcs-modal-room))) / 2), 1px);
}

/* ── The navigation row (design-system baseline, ADR-0044) ───────────
   One row look for PcsSideNavItem and PcsTreeView: global because two
   primitives draw it, and a scoped copy in each is how they drifted apart.
   The reset is here too, so a <button> wearing it loses its UA chrome. */
.pcs-nav-row {
    display: flex;
    align-items: center;
    gap: 8px;
    border: 0;
    background: transparent;
    border-radius: var(--pcs-radius-control);
    color: var(--pcs-charcoal);
    font: normal normal 500 13px/32px var(--pcs-font-ui);
    text-align: left;
    cursor: pointer;
    transition: background-color var(--pcs-transition);
}

.pcs-nav-row:hover:not(.is-disabled) {
    background: var(--pcs-light-grey-2);
}

.pcs-nav-row.is-selected {
    background: var(--pcs-white);
}

/* The disclosure chevron: two borders rotated, so it scales with currentColor and
   turns rather than snaps. Right when closed, down when open. */
.pcs-nav-chevron {
    display: block;
    width: calc(7px + 1.5px);
    height: calc(7px + 1.5px);
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(-45deg);
    transition: transform var(--pcs-transition);
}

/* Turned down, the two drawn borders fill only the box's lower half, so the V
   is lifted by that half (a quarter of the diagonal) to sit on the row's centre. */
.pcs-nav-chevron.is-open {
    transform: translateY(-3px) rotate(45deg);
}

@media (prefers-reduced-motion: reduce) {
    .pcs-nav-chevron {
        transition: none;
    }
}

/* ── PcsCheckbox / PcsRadio (design-system baseline, ADR-0044) ────────
   Global for the same reason as .pcs-pressable below: scoped, the `font` here
   ties with a caller's own rule at (0,2,0) and bundle order picks the winner.
   Neither carries a form row's margin — both are used inline just as often, and
   the container that stacks them owns the rhythm (see .admin-form). */
.pcs-checkbox {
    display: flex;
    align-items: center;
    gap: 8px;
    font: normal normal 400 13px/18px var(--pcs-font-ui);
    color: var(--pcs-fg-1);
    cursor: pointer;
}

/* The box's own margins are the browser's asymmetric default (3px 3px 0 5px),
   which tips it off the centre line of any row it shares. */
.pcs-checkbox input,
.pcs-radio__input {
    margin: 0;
    accent-color: var(--pcs-atoll);
}

.pcs-radio__input {
    flex-shrink: 0;
}

/* PcsRadio's labelled shell; a caller's own class supplies the row layout. */
.pcs-radio {
    display: flex;
    align-items: center;
    gap: 8px;
    font: normal normal 400 13px/18px var(--pcs-font-ui);
    color: var(--pcs-fg-1);
    cursor: pointer;
}

.pcs-radio.is-disabled {
    cursor: default;
    opacity: 0.55;
}

/* ── The button (design-system baseline, ADR-0044) ──────────────────
   Global, like .pcs-input, because it is the control's own look and not one
   component's: PcsButton draws it on a <button> or an <a>, and
   PcsFilePickerButton draws it on the <label> that a file picker has to be —
   a browser opens a file dialog only from a real <input type=file>. Scoped to
   PcsButton, the second one could not wear it without restating it, which is
   how the symbols view's import came to be styled by its own feature CSS and
   then, when that CSS went, to stop looking like a button at all.

   Everything variable is still a parameter (Variant, Size, FullWidth) — this
   file holds what those parameters resolve to, nothing a feature picks. */
.pcs-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border-radius: var(--pcs-radius-control);
    /* One neutral edge for every variant: a border in the button's own fill read
       as a button 2px bigger than the field beside it. */
    border: 1px solid var(--pcs-border);
    font-family: var(--pcs-font-ui);
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color var(--pcs-transition), border-color var(--pcs-transition), color var(--pcs-transition);
}

.pcs-btn:focus-visible,
/* The file picker's own input is clipped, so the label carries its focus ring.
   :has(:focus-visible), not :focus-within: a focused element matches its own
   :focus-within, so that spelling ringed every button on a mouse click. */
.pcs-btn:has(:focus-visible) {
    outline: 2px solid var(--pcs-focus-color);
    outline-offset: 1px;
}

/* ─── Size (the shared 32/38/46 scale; Small and Medium match the two input
       heights so a button sits flush with the field beside it) ───────── */
.pcs-btn--sm {
    height: var(--pcs-control-h-sm);
    padding: 0 12px;
    font-size: 12px;
    line-height: 16px;
}

.pcs-btn--md {
    height: var(--pcs-control-h-md);
    padding: 0 18px;
    font-size: 13px;
    line-height: 16px;
}

.pcs-btn--lg {
    height: var(--pcs-control-h-lg);
    padding: 0 24px;
    font-size: 14px;
    line-height: 18px;
}

/* ─── Variant ────────────────────────────────────────────── */
.pcs-btn--primary {
    background: var(--pcs-atoll);
    color: var(--pcs-white);
}

.pcs-btn--primary:hover:not(:disabled):not(.pcs-btn--disabled) {
    background: var(--pcs-dark-navy);
}

.pcs-btn--secondary {
    background: var(--pcs-white);
    color: var(--pcs-charcoal);
}

.pcs-btn--secondary:hover:not(:disabled):not(.pcs-btn--disabled) {
    background: var(--pcs-light-grey-3);
}

.pcs-btn--danger {
    background: var(--pcs-danger);
    color: var(--pcs-white);
}

.pcs-btn--danger:hover:not(:disabled):not(.pcs-btn--disabled) {
    filter: brightness(0.92);
}

/* Borderless text action. Height stays on the scale so it lines up in a row
   with the buttons it sits beside; only the chrome goes away. */
.pcs-btn--link {
    background: transparent;
    border-color: transparent;
    color: var(--pcs-link);
    padding-left: 0;
    padding-right: 0;
}

.pcs-btn--link:hover:not(:disabled):not(.pcs-btn--disabled) {
    color: var(--pcs-link-hover);
    text-decoration: underline;
}

/* Two spellings of one state: <button disabled> where the element can carry it,
   and the modifier where it cannot — a <label> is never disabled, only the
   input inside it is, and that does not grey the label. */
.pcs-btn:disabled,
.pcs-btn--disabled {
    opacity: 0.5;
    cursor: default;
}

/* ─── Layout ─────────────────────────────────────────────── */
.pcs-btn--block {
    width: 100%;
}

.pcs-btn--align-start {
    justify-content: flex-start;
}

/* ─── Icon ──────────────────────────────────────
   The leading library icon is drawn Mono, i.e. as a mask filled with
   currentColor, so it is exactly the label's colour on every variant — white
   on the filled ones, charcoal on Secondary, atoll on Link. No filter, and
   nothing for a feature to override.
   Chrome glyphs are inline svg and inherit currentColor already. */
.pcs-btn .pcs-btn__glyph {
    flex-shrink: 0;
}

/* ── Standard form controls (design-system baseline, ADR-0044) ───────────
   One definition, two entry points: bare controls opt in with .pcs-input
   (plus .pcs-input--sm for dense toolbars/tables/panels), and anything
   slotted into a PcsFormField (.form-field__control) is styled
   automatically. Feature .razor.css files keep layout only — widths, gaps,
   alignment — and never restate these rules. */
.pcs-input,
.form-field__control :is(input, select, textarea) {
    height: var(--pcs-control-h-md, 38px);
    padding: 0 var(--pcs-control-pad-md, 14px);
    border: 1px solid var(--pcs-border, #DEDEDE);
    border-radius: var(--pcs-radius-control);
    background: var(--pcs-white, #ffffff);
    font: normal normal 500 14px/17px var(--pcs-font-ui, 'Inter', sans-serif);
    color: var(--pcs-charcoal, #293132);
    transition: border-color var(--pcs-transition, 150ms linear);
}

/* Bare .pcs-input controls size by their context; form-field slots fill. */
.form-field__control :is(input, select, textarea) {
    width: 100%;
}

.pcs-input:focus,
.form-field__control :is(input, select, textarea):focus {
    outline: none;
    border-color: var(--pcs-focus-color, #30597C);
}

.pcs-input::placeholder,
.form-field__control :is(input, textarea)::placeholder {
    color: var(--pcs-mid-grey, #8E9292);
}

/* The warning state (a field's Warning parameter): the value stands, but
   outside what is expected. The colour holds through focus, so the field
   keeps saying it while the user corrects it. */
.pcs-input--warning,
.pcs-input--warning:focus {
    border-color: var(--pcs-warning);
}

/* Text the field could not read (a number field given a paste that is not a
   number): it stays visible to be corrected, in the danger colour. */
.pcs-input[aria-invalid="true"],
.pcs-input[aria-invalid="true"]:focus {
    border-color: var(--pcs-danger);
}

/* Read-only state reads as read-only: greyed fill + text, no-entry cursor. */
.pcs-input:disabled,
.form-field__control :is(input, select, textarea):disabled {
    background: var(--pcs-light-grey-3, #F4F4F4);
    color: var(--pcs-mid-grey, #8E9292);
    cursor: not-allowed;
}

/* Stacked dialog form: the body's rhythm and one hint/error voice.
   Global rather than per-dialog .razor.css, because Blazor scopes a component
   stylesheet to that component alone — New Page shipped unstyled for exactly
   that reason, borrowing Page Setup's .page-setup rules it could never see,
   and .pcs-form-hint was referenced by the Duplicate dialog while defined
   nowhere at all.

   There is no .pcs-form-field here: a labelled field is PcsFormField, which
   every field primitive already renders from its Label. The hand-rolled
   <label><span> wrapper was a second spelling of it, with its own label type. */
.pcs-form-stack {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    min-inline-size: 20rem;
}

/* The stack owns the rhythm, so the fields in it drop their own row margin
   rather than adding a second gap on top of this one. */
.pcs-form-stack .form-field {
    margin-bottom: 0;
}

.pcs-form-hint {
    margin: 0;
    font-size: 11.5px;
    color: var(--pcs-fg-2);
    line-height: 1.5;
}

.pcs-form-error {
    margin: 0;
    font-size: 0.85rem;
    color: var(--pcs-danger, #c33);
}

/* Size, one vocabulary with the buttons (PcsControlSize: 32 / 38 / 46 px).
   Bare controls take the class; a PcsFormField passes Size and the modifier
   lands on the wrapper, so the slotted control follows without the caller
   naming a class at all. */
.pcs-input--sm,
.form-field--sm .form-field__control :is(input, select, textarea) {
    height: var(--pcs-control-h-sm, 32px);
    padding: 0 var(--pcs-control-pad-sm, 10px);
    font-size: 13px;
    line-height: 16px;
}

.pcs-input--lg,
.form-field--lg .form-field__control :is(input, select, textarea) {
    height: var(--pcs-control-h-lg, 46px);
    padding: 0 var(--pcs-control-pad-lg, 16px);
    font-size: 15px;
    line-height: 18px;
}

/* Typed over drawn content (a field's Overlay parameter): the box and the
   font are the drawing's, not a control's, and the drawing shows through. */
.pcs-input.pcs-input--overlay {
    height: 100%;
    padding: 0 2px;
    font: inherit;
    background: color-mix(in srgb, var(--pcs-bg-1) 92%, transparent);
    border-color: var(--pcs-focus-color);
}

/* Selects share the look plus a custom chevron. */
select.pcs-input,
.form-field__control select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-right: 36px;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23293132' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 14px center;
}

select.pcs-input--sm,
.form-field--sm .form-field__control select {
    padding-right: 28px;
    background-position: right 10px center;
}

select.pcs-input--lg,
.form-field--lg .form-field__control select {
    padding-right: 40px;
    background-position: right 16px center;
}

/* Native colour swatch, the same two entry points as .pcs-input: the bare
   .pcs-color class (plus --sm / --lg) for an inline swatch in a row or toolbar,
   and automatic styling when slotted into a PcsFormField. The control has no
   sizing of its own, so the design system gives it the control height as a
   square — four features had written their own 44x32 / 32x32 copy of this. */
.pcs-color,
.form-field__control input[type="color"] {
    /* A swatch is a square, so it never gives width up: as a flex item it
       carried the default flex-shrink and a toolbar one row too full squashed
       it to a 4px sliver that read as a divider. */
    flex: none;
    width: var(--pcs-control-h-md, 38px);
    height: var(--pcs-control-h-md, 38px);
    padding: 2px;
    cursor: pointer;
}

.pcs-color--sm,
.form-field--sm .form-field__control input[type="color"] {
    width: var(--pcs-control-h-sm, 32px);
    height: var(--pcs-control-h-sm, 32px);
}

.pcs-color--lg,
.form-field--lg .form-field__control input[type="color"] {
    width: var(--pcs-control-h-lg, 46px);
    height: var(--pcs-control-h-lg, 46px);
}

/* Multiline. */
textarea.pcs-input,
.form-field__control textarea,
.form-field--sm .form-field__control textarea,
.form-field--lg .form-field__control textarea {
    height: auto;
    min-height: 110px;
    padding: 12px 14px;
    line-height: 20px;
    resize: vertical;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

/* Blazor adds this class to every <ValidationMessage>; styled globally so all
   PcsTextField / PcsSelect / form validation reads the same. */
.validation-message {
    margin-top: 6px;
    font: normal normal 400 12px/16px var(--pcs-font-ui, 'Inter', sans-serif);
    color: var(--pcs-danger, #c0392b);
}

.dialog-warning {
    margin: 0;
    font: normal normal 400 13px/19px var(--pcs-font-ui, 'Inter', sans-serif);
    color: var(--pcs-charcoal, #293132);
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

.blazor-error-boundary::after {
    content: "An error has occurred."
}

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

/* Floorplan room name labels — real SVG <text> built via the render tree in
   FloorplanEditor, so its component-scoped CSS can't reach them (hence global).
   Constant on-screen size comes from the --fp-label-px var the editor's JS
   keeps in sync with zoom; the white halo (paint-order stroke) keeps the label
   legible over both white background and dark plan lines. */
.fp-room-label {
    fill: var(--pcs-charcoal, #293132);
    stroke: var(--pcs-white, #ffffff);
    stroke-width: 3;
    paint-order: stroke;
    vector-effect: non-scaling-stroke;
    text-anchor: middle;
    dominant-baseline: central;
    font-family: var(--pcs-font-ui, 'Inter', sans-serif);
    font-weight: 600;
    pointer-events: none;
    user-select: none;
}

/* The area line under a room name (item: Show area) — smaller and lighter. */
.fp-room-label__area {
    font-size: 0.82em;
    font-weight: 500;
}

/* Settings search target flash — the class is (re)applied by JS
   (pcs.settingsReveal), so component-scoped CSS can't own it (hence global,
   like .fp-room-label). The flash paints on an ::after box — inset from the
   content frame and rounded — because the settings rows own opaque white
   backgrounds that an element-level background can't reliably tint (and can't
   be inset). isolation + z-index slot the box between the element's own
   background and its content, so the opaque grey never covers the text.
   pcs.settingsReveal removes the class on animationend, so a hidden-and-
   reshown section doesn't replay old flashes. */
.pcs-settings-flash {
    position: relative;
    isolation: isolate;
}

.pcs-settings-flash::after {
    content: "";
    position: absolute;
    /* The default inset is sized for the 32px-padded top-level rows/sections;
       nested anchors (PcsSettingsAnchor) override the variable to hug their
       row instead. */
    inset: var(--pcs-settings-flash-inset, 6px 14px);
    z-index: -1;
    border-radius: var(--pcs-radius-md, 6px);
    background: var(--pcs-light-grey-3, #F4F4F4);
    pointer-events: none;
    animation: pcs-settings-flash 1.8s ease-out forwards;
}

@keyframes pcs-settings-flash {
    0%, 40% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ── Toolbar actions ───────────────────────────────────────────────────────
   The square icon button an editor toolbar offers beside its tools — undo,
   redo, delete, the toggles. Global, like .pcs-input, because it is the
   control's own look and not one toolbar's: the page editor's ribbon and the
   page-frame editor's strip both draw it, and a user should not be able to
   tell which one they are looking at. */
.ribbon__action {
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--pcs-radius-control);
    color: var(--pcs-fg-1);
    cursor: pointer;
    padding: 0;
    transition: background-color var(--pcs-transition), border-color var(--pcs-transition);
}

/* A run of actions that belong together. The cells already carry 10px of their
   own padding around a 16px icon, so 2px between them is a gap, not a squeeze —
   the 4px flex gap plus a 4px margin each was reading as a wide void. */
.ribbon__actions {
    display: inline-flex;
    align-items: center;
    gap: 2px;
}

/* Groups separate themselves: the rule belongs to the group that follows one,
   so no caller writes a divider and none has to know which group is last.
   Shorter and closer in than the tool divider, because these cells are 36px
   against the tool button's 46px. */
.ribbon__actions + .ribbon__actions::before {
    content: "";
    /* The width is the rule and takes the token; the height is how far it
       runs, which is a size like any other and stays whole pixels. */
    width: var(--pcs-hairline);
    height: 20px;
    margin: 0 7px;
    background: var(--pcs-border);
    flex: none;
}

.ribbon__action:hover:not(:disabled) {
    background: var(--pcs-light-grey-3);
    border-color: var(--pcs-border);
}

/* A pressed state for the toggle-shaped actions (the drawing tools' angle
   constraint) — same box as .ribbon__action, held down, inked in the accent
   every other "on" in the design system uses. */
.ribbon__action--active {
    background: var(--pcs-light-grey-3);
    border-color: var(--pcs-border);
    color: var(--pcs-accent);
}

.ribbon__action:disabled {
    pointer-events: none;
}

/* A tooltip host that wraps a control rather than a run of text: the span
   becomes the flex item its control would have been, so it lays out like one.
   Most controls carry data-pcs-tip themselves and need no host at all; this is
   for the ones that cannot — a select, or a disabled button, which emits no
   mouse events for the delegated host to see. It also carries the not-allowed
   cursor for a disabled action, which no longer receives pointer events. */
.ribbon__tip {
    display: inline-flex;
    align-items: center;
}

/* The one popup pcs.tips draws, for the whole document rather than one per
   tipped control. Global because that host appends it to document.body, where
   no component's scope id reaches it.

   Small dark rounded box, white text, brief fade. position:fixed with viewport
   coords from JS so overflow containers never clip it; translate centres it on
   the anchor x and lifts it
   above (or drops it below) the host. */
.pcs-tooltip {
    position: fixed;
    z-index: 900;
    transform: translate(-50%, -100%);
    /* max-content, or the box is shrink-to-fit against whatever room is left
       between it and the viewport edge — which near the right edge is a few
       characters, and "Scroll tabs left" came out three lines tall. It asks
       for its one line now, and the placement moves it to where that fits. */
    width: max-content;
    max-width: min(calc(320px + 20px), calc(100vw - 16px));
    padding: 5px 10px;
    border-radius: 4px;
    background: var(--pcs-dark-navy, #2E4B6A);
    color: var(--pcs-white, #fff);
    font: normal normal 400 12px/1.4 var(--pcs-font-ui, 'Inter', sans-serif);
    white-space: pre-line;
    overflow-wrap: break-word;
    text-align: center;
    pointer-events: none;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
    animation: pcs-tooltip-in 150ms ease 100ms both;
}

.pcs-tooltip--below {
    transform: translate(-50%, 0);
}

@keyframes pcs-tooltip-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

.ribbon__tip:has(> :disabled) {
    cursor: not-allowed;
}

/* A library icon carries its own colours, so the disabled state is a fade
   rather than a currentColor change. Global with the button it sits in: left
   behind in the ribbon's scoped sheet, a second toolbar's disabled actions
   stayed fully coloured and looked clickable. */
.ribbon__action:disabled .ribbon__action-icon {
    opacity: 0.35;
    filter: grayscale(1);
}

.ribbon__action-icon {
    width: 16px;
    height: 16px;
}

/* Every dropdown's chevron — the menu button's, the popover's, a chip's that
   opens a menu: the same gap after the text, and turned over while open.
   Global, because three primitives and their callers' triggers draw it and a
   scoped copy in each had already drifted (the chip's sat against its text
   and never turned). */
.pcs-dropdown-chevron {
    flex: none;
    margin-left: 6px;
    transition: transform var(--pcs-transition);
}

.pcs-dropdown-chevron.is-open {
    transform: rotate(180deg);
}
