﻿
html, body {
    background-color: var(--neutral-layer-2);
    color: var(--neutral-foreground-rest);
    font-family: var(--body-font);
    font-size: var(--type-ramp-base-font-size);
    line-height: var(--type-ramp-base-line-height);
    scroll-behavior: auto !important;
    margin: 0;
    padding: 0;
}

/* ----- Focus polish ------------------------------------------------------
   Blazor's <FocusOnNavigate Selector="h1" /> programmatically focuses each
   page's h1 after navigation (good for screen-reader announcements). It
   sets tabindex="-1" on the target so it can receive focus, but a -1
   element can ONLY be focused programmatically (users can't tab into it),
   so the focus ring is always cosmetic noise. Suppress it unconditionally.
   :focus-visible alone isn't enough because a keyboard refresh (F5/Ctrl-R)
   leaves the browser in "keyboard mode," which makes the next programmatic
   focus match :focus-visible.
   ------------------------------------------------------------------------ */
[tabindex="-1"]:focus {
    outline: none;
    box-shadow: none;
}

/* ----- Global anchor color ----------------------------------------------
   The browser's default link blue (#0000EE / #5555FF visited) is unreadable
   against our dark surface and clashes with the magenta accent in light
   mode. Route inline links through Fluent's accent-foreground ramp so they
   pick up the configured CustomColor and contrast properly in both themes.
   Components that opt out (e.g. .cs-list__row, breadcrumbs) already set
   `color: inherit` in their scoped CSS and continue to win on specificity.
   ------------------------------------------------------------------------ */
a {
    color: var(--accent-foreground-rest);
    text-decoration-color: var(--accent-foreground-rest);
}
a:hover { color: var(--accent-foreground-hover); }
a:active { color: var(--accent-foreground-active); }
a:visited { color: var(--accent-foreground-rest); }

/* Reading-content typography tokens. Apply to any surface where the
   user is doing sustained reading — long-form prose, lists of full
   sentences, form labels + help copy, dialogs that explain what they
   do. The Fluent --type-ramp-base-font-size default (14px) is too
   small for laptop reading distance per the Basecamp / Things 3
   references the design system anchors on; these tokens land in the
   17–19px range with comfortable line-height.

   When NOT to use: dense data tables, navigation, button labels,
   and other UI chrome. Those keep Fluent's default ramp so the
   visual density of the FluentUI components stays coherent. See
   docs/design-system.md §1 for the placement rule. */
:root {
    --cs-reading-font-size: 1.1875rem;     /* ~19px — primary reading body */
    --cs-reading-font-size-sm: 1.0625rem;  /* ~17px — secondary / aside reading */
    --cs-reading-line-height: 1.65;
}

/* Page title + subtitle — used by every top-level page. Lives here
   (not in a scoped .razor.css) so every page that uses these classes
   gets consistent styling. Move new cross-page patterns here, not into
   per-page scoped CSS. */
.cs-page-title {
    margin: 0 0 0.25rem 0;
}
.cs-page-subtitle {
    margin: 0 0 1.25rem 0;
    color: var(--neutral-foreground-hint, #6e6e6e);
}
/* Inline affordance under the /profile subtitle — link to the
   user's own /@{handle} page (or hint copy when no handle yet). */
.cs-profile__public-link {
    margin: -0.75rem 0 1.25rem 0;
    font-size: 0.95rem;
}
.cs-profile__public-link--hint {
    color: var(--neutral-foreground-hint, #6e6e6e);
}
/* Header meta row (e.g. "Cabinet · [Sharing pill]"). Flex so the inline
   FluentBadge sits on the same baseline as the kind label without the
   default <p> margin collapse. */
.cs-page-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin: 0 0 0.5rem 0;
    color: var(--neutral-foreground-hint, #6e6e6e);
}
.cs-page-meta__sep {
    color: var(--neutral-foreground-hint, #6e6e6e);
    opacity: 0.7;
}

/* Ease-of-entry — global Add wizard. The wizard itself is a FluentDialog;
   these rules size the choice tiles and tune the layout of the multi-
   step body. The primary surface decisions (modal frame, focus trap)
   are owned by FluentDialog defaults. */
.cs-add-wizard__intro {
    margin: 0 0 1rem 0;
    color: var(--neutral-foreground-rest, #1f1f1f);
}
.cs-add-wizard__choices {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.cs-add-wizard__choice {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    column-gap: 0.875rem;
    row-gap: 0.125rem;
    align-items: center;
    padding: 0.875rem 1rem;
    background: var(--neutral-layer-2, #f3f3f3);
    border: 1px solid var(--neutral-stroke-rest, #d1d1d1);
    border-radius: 8px;
    cursor: pointer;
    text-align: left;
    color: var(--neutral-foreground-rest, #1f1f1f);
}
.cs-add-wizard__choice:hover:not(:disabled) {
    background: var(--neutral-layer-3, #ebebeb);
}
.cs-add-wizard__choice:focus-visible {
    outline: 2px solid var(--accent-fill-rest, #0078d4);
    outline-offset: 2px;
}
.cs-add-wizard__choice fluent-icon {
    grid-row: 1 / span 2;
    color: var(--accent-fill-rest, #0078d4);
}
.cs-add-wizard__choice-label {
    font-weight: 600;
    grid-row: 1;
    grid-column: 2;
}
.cs-add-wizard__choice-hint {
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.875rem;
    grid-row: 2;
    grid-column: 2;
}
.cs-add-wizard__choice--disabled {
    opacity: 0.55;
    cursor: not-allowed;
}
.cs-add-wizard__success {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    padding: 1rem 0;
}
.cs-add-wizard__success p {
    margin: 0;
}
.cs-add-wizard__failure-list {
    margin: 0 0 0.875rem 0;
    padding-left: 1.25rem;
    color: var(--neutral-foreground-rest, #1f1f1f);
    font-size: 0.875rem;
}
.cs-add-wizard__failure-list li + li {
    margin-top: 0.25rem;
}

/* Spec 010 Phase 4 — list-of-items tabular bulk-entry editor. */
.cs-list-editor {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
}
.cs-list-editor__type-chosen {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    background: var(--neutral-layer-2, #f3f3f3);
    border: 1px solid var(--neutral-stroke-rest, #d1d1d1);
    border-radius: 6px;
}
.cs-list-editor__hint {
    margin: 0;
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.875rem;
}
.cs-list-editor__notice {
    margin: 0 0 0.875rem 0;
    color: var(--neutral-foreground-rest, #1f1f1f);
}
.cs-list-editor__table {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
}
.cs-list-editor__table th,
.cs-list-editor__table td {
    padding: 0.375rem 0.5rem;
    border-bottom: 1px solid var(--neutral-stroke-rest, #e5e5e5);
    text-align: left;
    vertical-align: middle;
}
.cs-list-editor__table th {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--neutral-foreground-hint, #6e6e6e);
}
.cs-list-editor__col-title { width: auto; }
.cs-list-editor__col-done { width: 4.5rem; text-align: center; }
.cs-list-editor__col-start { width: 11rem; }
.cs-list-editor__col-amount { width: 7rem; }
.cs-list-editor__col-actions { width: 2rem; text-align: right; }
.cs-list-editor__col-done input[type="checkbox"] {
    margin: 0;
}
.cs-list-editor__input {
    width: 100%;
    padding: 0.375rem 0.5rem;
    border: 1px solid var(--neutral-stroke-rest, #d1d1d1);
    border-radius: 4px;
    background: var(--neutral-layer-1, #ffffff);
    color: var(--neutral-foreground-rest, #1f1f1f);
    font: inherit;
    box-sizing: border-box;
}
.cs-list-editor__input:focus-visible {
    outline: 2px solid var(--accent-fill-rest, #0078d4);
    outline-offset: 1px;
    border-color: var(--accent-fill-rest, #0078d4);
}
.cs-list-editor__remove {
    border: none;
    background: transparent;
    color: var(--neutral-foreground-hint, #6e6e6e);
    cursor: pointer;
    font-size: 1.125rem;
    line-height: 1;
    padding: 0.125rem 0.375rem;
    border-radius: 4px;
}
.cs-list-editor__remove:hover:not(:disabled) {
    background: var(--neutral-layer-3, #ebebeb);
    color: var(--neutral-foreground-rest, #1f1f1f);
}
.cs-list-editor__field-error {
    margin: 0;
    color: #b32d2e;
    font-size: 0.875rem;
}

/* Spec 013 Phase 4 — Messaging surfaces. Inbox + Sent lists, the
   detail page, and the composer (used inside the AddWizard). Calm
   list rows with unread emphasis, no decorative chrome. */

.cs-inbox-list,
.cs-sent-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.cs-inbox-list__row,
.cs-sent-list__row {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto;
    column-gap: 0.5rem;
    padding: 0.75rem 0.875rem;
    background: var(--neutral-layer-1, #ffffff);
    border: 1px solid var(--neutral-stroke-rest, #e5e5e5);
    border-radius: 6px;
    text-decoration: none;
    color: var(--neutral-foreground-rest, #1f1f1f);
}
.cs-inbox-list__row:hover,
.cs-sent-list__row:hover {
    background: var(--neutral-layer-2, #f5f5f5);
}
.cs-inbox-list__row--unread {
    border-left: 3px solid var(--accent-fill-rest, #0078d4);
}
.cs-inbox-list__sender,
.cs-sent-list__head {
    grid-column: 1;
    grid-row: 1;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: baseline;
}
.cs-inbox-list__handle,
.cs-sent-list__edited {
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.875rem;
}
.cs-inbox-list__preview,
.cs-sent-list__preview {
    grid-column: 1;
    grid-row: 2;
    margin: 0;
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.9375rem;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
}
.cs-inbox-list__time,
.cs-sent-list__time {
    grid-column: 2;
    grid-row: 1;
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.8125rem;
    white-space: nowrap;
}
.cs-sent-list__recipient-count {
    font-weight: 600;
}

/* Detail page — a single-column read view; sender + recipient share
   the same chrome with conditional affordances (Edit/Delete vs.
   Archive). */
.cs-message-detail__time {
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.875rem;
}
.cs-message-detail__edited {
    color: var(--neutral-foreground-hint, #6e6e6e);
    margin-left: 0.5rem;
}
.cs-message-detail__body {
    margin: 1rem 0;
}
.cs-message-detail__text {
    margin: 0 0 1rem 0;
    white-space: pre-wrap;
    line-height: 1.5;
}
.cs-message-detail__editor {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.cs-message-detail__recipients {
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--neutral-stroke-rest, #e5e5e5);
}
.cs-message-detail__recipients h3 {
    margin: 0 0 0.5rem 0;
    font-size: 0.9375rem;
    color: var(--neutral-foreground-hint, #6e6e6e);
}
.cs-message-detail__recipients ul {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.cs-message-detail__recipient-state {
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.875rem;
}
.cs-message-detail__history {
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--neutral-stroke-rest, #e5e5e5);
}
.cs-message-detail__history-list {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0 0 0;
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
}
.cs-message-detail__history-time {
    display: block;
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.8125rem;
    font-family: monospace;
}

/* Composer — recipient picker + body field. Calm, scrollable at the
   common viewport sizes, no overwhelming list virtualization needed
   for v1 (most users won't have 100+ connections). */
.cs-message-composer {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
}
.cs-message-composer__heading {
    margin: 0 0 0.5rem 0;
    font-size: 0.9375rem;
    color: var(--neutral-foreground-hint, #6e6e6e);
}
.cs-message-composer__recipients {
    max-height: 12rem;
    overflow-y: auto;
    border: 1px solid var(--neutral-stroke-rest, #e5e5e5);
    border-radius: 6px;
    padding: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.cs-message-composer__recipient {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.375rem;
    border-radius: 4px;
    cursor: pointer;
}
.cs-message-composer__recipient:hover {
    background: var(--neutral-layer-2, #f3f3f3);
}
.cs-message-composer__recipient-display {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: baseline;
}
.cs-message-composer__recipient-handle {
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.875rem;
}
.cs-message-composer__error {
    margin: 0;
    color: #b32d2e;
    font-size: 0.875rem;
}

/* Inbox unread badge — small accent-fill pill on the nav slot. */
.cs-inbox-badge {
    margin-left: 0.25rem;
}

/* Location picker — drill-down used by the wizard's "Where?" step. */
.cs-location-picker {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
}
.cs-location-picker__crumbs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    align-items: center;
    font-size: 0.875rem;
}
.cs-location-picker__crumb {
    background: transparent;
    border: none;
    padding: 0.125rem 0.375rem;
    border-radius: 4px;
    color: var(--accent-foreground-rest, #0067b8);
    cursor: pointer;
    font: inherit;
}
.cs-location-picker__crumb:hover:not(:disabled) {
    background: var(--neutral-layer-2, #f3f3f3);
}
.cs-location-picker__crumb:disabled {
    cursor: default;
    color: var(--neutral-foreground-hint, #6e6e6e);
}
.cs-location-picker__crumb--current {
    color: var(--neutral-foreground-rest, #1f1f1f);
    font-weight: 600;
}
.cs-location-picker__sep {
    color: var(--neutral-foreground-hint, #6e6e6e);
    opacity: 0.6;
}
.cs-location-picker__list {
    list-style: none;
    margin: 0;
    padding: 0.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    max-height: 18rem;
    overflow-y: auto;
    border: 1px solid var(--neutral-stroke-rest, #d1d1d1);
    border-radius: 6px;
    background: var(--neutral-layer-1, #ffffff);
}
.cs-location-picker__row {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    width: 100%;
    padding: 0.5rem 0.625rem;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 4px;
    cursor: pointer;
    text-align: left;
    color: var(--neutral-foreground-rest, #1f1f1f);
    font: inherit;
}
.cs-location-picker__row:hover {
    background: var(--neutral-layer-2, #f3f3f3);
}
.cs-location-picker__row:focus-visible {
    outline: 2px solid var(--accent-fill-rest, #0078d4);
    outline-offset: -1px;
}
.cs-location-picker__chevron {
    margin-left: auto;
    color: var(--neutral-foreground-hint, #6e6e6e);
}
.cs-location-picker__empty {
    color: var(--neutral-foreground-hint, #6e6e6e);
    text-align: center;
    padding: 1rem;
    font-size: 0.9375rem;
}
.cs-location-picker__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: flex-end;
}

/* Add-button accent fill on the nav bars. The "Add" affordance is the
   page's primary write action, so it gets the accent treatment instead
   of the muted nav-link styling its siblings use. On mobile the accent
   wraps just the icon (a 36px circle inside the bar slot) — filling
   the whole slot would visually swallow neighboring slots; the desktop
   variant is icon-only so the whole button IS the accent circle. */
.cs-bottom-link--add {
    background: transparent;
}
.cs-bottom-link--add:hover {
    background: var(--neutral-fill-stealth-hover, rgba(0, 0, 0, 0.05));
}
.cs-bottom-link--add__circle {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--accent-fill-rest, #0078d4);
    color: var(--accent-foreground-rest, #ffffff);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 2px;
}
.cs-bottom-link--add__circle svg,
.cs-bottom-link--add__circle svg * {
    color: var(--accent-foreground-rest, #ffffff);
    fill: var(--accent-foreground-rest, #ffffff);
}
.cs-bottom-link--add:hover .cs-bottom-link--add__circle {
    background: var(--accent-fill-hover, #106ebe);
}
.cs-nav-link--add {
    background: var(--accent-fill-rest, #0078d4) !important;
    color: var(--accent-foreground-rest, #ffffff) !important;
    border-radius: 999px !important;
    padding: 0 !important;
    width: 36px !important;
    height: 36px !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    margin-right: 0.5rem;
    border: none;
    cursor: pointer;
}
.cs-nav-link--add fluent-icon,
.cs-nav-link--add fluent-icon svg,
.cs-nav-link--add fluent-icon svg * {
    color: var(--accent-foreground-rest, #ffffff) !important;
    fill: var(--accent-foreground-rest, #ffffff) !important;
}
.cs-nav-link--add:hover {
    background: var(--accent-fill-hover, #106ebe) !important;
}

/* Status message bar inset — used by both ShareDialog and SharingPanel.
   FluentMessageBar fills its parent edge-to-edge; without breathing room
   the alert background runs flush to the surface's edges and (worse) its
   internal layout overflows the right side, clipping the dismiss X.
   Lives globally because SharingPanel reuses the class outside of
   ShareDialog's scoped-CSS reach. */
.cs-share-dialog__status {
    margin: 0.75rem 1rem 0 1rem;
}
.cs-share-dialog__status fluent-message-bar {
    width: 100%;
    box-sizing: border-box;
}

/* Capability sub-form editors (ActionDataEditor, CalendarDataEditor,
   PurchaseDataEditor, ContactDataEditor; AttachmentDataEditor in spec
   009). All four share the same shell so the orchestrator can compose
   them without each rendering a different visual rhythm. Lives here
   because it's used by 4+ components — scoped CSS would silently miss
   most of them. */
.cs-cap-editor {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
}

.cs-cap-editor__hint {
    margin: 0;
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.9375rem;
    line-height: 1.45;
}

.cs-cap-editor__error {
    margin: 0;
    color: var(--error, #b3261e);
    font-size: 0.9375rem;
}

.cs-cap-editor__row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.cs-cap-editor__group {
    margin: 0;
    padding: 0;
    border: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.cs-cap-editor__group > legend {
    padding: 0;
    margin-block-end: 0.25rem;
    font-size: 0.8125rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--neutral-foreground-hint, #6e6e6e);
}

.flex {
    display: flex;
    gap: var(--gap, 1rem);
}

.flex-wrap {
    flex-wrap: wrap;
}

.flex-center {
    justify-content: center;
}

.flex-stretch {
    justify-content: stretch;
}

.nav-link {
    align-items: center;
}

.status-bar-safe-area {
    display: none;
}

.inline-block {
    display: inline-block;
}

.app-icon-normal {
    width: 2rem;
    height: 2rem;
    margin-right: 1rem;
}

@supports (-webkit-touch-callout: none) {
    .status-bar-safe-area {
        display: flex;
        position: sticky;
        top: 0;
        height: env(safe-area-inset-top);
        background-color: #f7f7f7;
        width: 100%;
        z-index: 1;
    }

    .flex-column, .navbar-brand {
        padding-left: env(safe-area-inset-left);
    }
}



/* ----- Views hub (/views) --------------------------------------------
   Spec 027 / T150 — six clickable cards in a responsive grid linking
   to the rollup pages. 3-col on desktop, 2-col on tablet, 1-col on
   mobile. */
.cs-views-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem;
    margin-top: 1.5rem;
}
@media (max-width: 900px) {
    .cs-views-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 540px) {
    .cs-views-grid { grid-template-columns: 1fr; }
}
.cs-views-card {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 1rem;
    border: 1px solid var(--neutral-stroke-divider-rest, #ebebeb);
    border-radius: var(--control-corner-radius, 6px);
    text-decoration: none;
    color: inherit;
    background: var(--neutral-layer-1, #ffffff);
    transition: background-color 0.15s ease;
}
.cs-views-card:hover,
.cs-views-card:focus-visible {
    background: var(--neutral-fill-secondary-rest, #fafafa);
    outline: none;
}
.cs-views-card__title {
    font-size: 1.05rem;
    font-weight: 600;
    margin: 0 0 0.25rem;
}
.cs-views-card__hint {
    margin: 0;
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.875rem;
}

/* ----- Calendar rollup agenda ----------------------------------------
   Spec 027 / T090 replaced the FullCalendar shell with a date-grouped
   list. The agenda groups events by day with a small heading per
   bucket. .cs-list__row is shared with every other list surface so
   we only own the date-header style here. */
.cs-agenda {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.cs-agenda__day {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--neutral-foreground-hint, #6e6e6e);
    margin: 0.75rem 0 0.25rem;
    padding-bottom: 0.25rem;
    border-bottom: 1px solid var(--neutral-stroke-divider-rest, #ebebeb);
}
.cs-agenda__day:first-child {
    margin-top: 0;
}

/* Error message color — dark-mode contrast bump. Both ItemCard and
   Cabinets define `.cs-dialog-error` / `.cs-action-error` in scoped
   CSS using the Fluent `--error-foreground-rest` token, which doesn't
   shift bright enough on dark. Override globally to red-400 (#f87171,
   ~6.4:1 against #1f1f1f) so server errors and field-validation alerts
   read clearly. !important is needed to beat the scoped (0,2,0) rules.
   Light mode keeps the default — Brett confirmed it's already fine.
   Spec 032 Phase 4 (T042) extension: `.cs-attachment-uploader__error`
   and `.cs-file-download__error` share the same dark-mode unreadable
   `--cs-warn-fg` (deep rust on dark bg); folded into the same rule. */
[data-theme="dark"] .cs-dialog-error,
[data-theme="dark"] .cs-action-error,
[data-theme="dark"] .cs-attachment-uploader__error,
[data-theme="dark"] .cs-file-download__error {
    color: #f87171 !important;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cs-dialog-error,
    :root:not([data-theme="light"]) .cs-action-error,
    :root:not([data-theme="light"]) .cs-attachment-uploader__error,
    :root:not([data-theme="light"]) .cs-file-download__error {
        color: #f87171 !important;
    }
}

/* Spec 005 polish — sharing status indicators on cabinet rows + child
   item rows. Originally rendered as pill-shaped FluentBadges with
   colored fills + borders; the pill chrome read as a button affordance
   even though clicking did nothing — a mini dark-pattern. Stripped to
   icon + colored label only. The Sharing tab on the cabinet detail
   page is the canonical edit surface for sharing; this is read-only
   status. Color + glyph encoding (icon stays) covers WCAG 1.4.1. */
.cs-sharing-pill {
    --pill-fg: var(--neutral-foreground-rest, #1f1f1f);
    background: transparent !important;
    border: none !important;
    padding: 0 !important;
    color: var(--pill-fg) !important;
    display: inline-flex !important;
    align-items: center;
    font-weight: 500;
}
/* FluentBadge's inner `control` part paints its own neutral fill +
   border via appearance="neutral". Keep it transparent so the host's
   stripped-down look isn't overridden inside the shadow DOM. */
.cs-sharing-pill::part(control) {
    background: transparent !important;
    border: none !important;
    color: inherit !important;
    padding: 0 !important;
}
/* Spacing between the slotted icon and the label. The host's flex `gap`
   doesn't reach into FluentBadge's named slot wrappers in shadow DOM,
   and `gap` on ::part(control) was inconsistent across icons (PeopleTeam
   has different inner padding than Globe / LockClosed). Targeting the
   light-DOM child by `[slot="start"]` is the robust path — margin
   travels with the slotted SVG into the shadow render tree regardless
   of how Fluent wraps its slots. */
/* Spacing between the slotted icon and the label. The previous direct-child
   selector (`.cs-sharing-pill > [slot="start"]`) didn't match — FluentBadge
   wraps slotted content, so the SVG isn't a direct child. Descendant
   combinator hits the actual SVG regardless of how deep Fluent wraps it.
   8px reads as a clear separator across all three icons (PeopleTeam in
   particular has a wide multi-figure glyph that extends near the right
   edge of its 16×16 box) without padding the pill out. */
.cs-sharing-pill svg[slot="start"],
.cs-sharing-pill [slot="start"] {
    margin-right: 8px !important;
}
/* Force the slotted icon glyph to render in the mode color (--pill-fg),
   matching the pill's label. The `inherit` / `currentColor` approach didn't
   stick — Fluent UI Blazor's FluentIcon renders the SVG with an explicit
   fill (presentation attribute on the <path>) and the icon system's default
   Color value sets its own color via inline style. We override at every
   level (svg + descendants + path + nested *) using `var(--pill-fg)`
   directly with !important so we beat both inline styles and presentation
   attributes regardless of the Fluent rendering shape. */
.cs-sharing-pill svg[slot="start"],
.cs-sharing-pill svg[slot="start"] *,
.cs-sharing-pill > [slot="start"],
.cs-sharing-pill > [slot="start"] * {
    color: var(--pill-fg) !important;
    fill: var(--pill-fg) !important;
}

/* Sharing color palettes — two semantic groups, both consumed below by
   .cs-sharing-pill--* and (for the permission group) by .cs-grant-select--*
   in PeoplePicker.razor.css. CSS custom properties inherit through the DOM,
   so scoped Blazor CSS reads them transparently.

   GROUP 1 — Sharing-mode palette (audience scope):
       public / shared / private. Foreground only — sharing-pills render on
       the page's neutral surface, no chip. Saturation bumped from default
       dark-on-white values so the three modes read as visually distinct
       hues at a glance, not "dark text" with subtle differences.

   GROUP 2 — Permission palette (access level), per design-system §2:
       none / view / edit / manage. Full fg + bg + border tokens because
       grant-select renders as a tinted chip. Single canonical hue per
       level — blue = read, orange = write, violet = admin, neutral = off.
       Sharing-pill view/edit/manage consume only the fg slot (no chip
       rendered there).

   Light values live at :root. Dark variants override under
   [data-theme="dark"] AND under :root:not([data-theme="light"]) inside
   @media (prefers-color-scheme: dark). The double-source covers the gap
   where the explicit theme attribute is sometimes absent on first paint
   after Blazor enhanced navigation; the OS-preference fallback keeps the
   palette in sync with Fluent UI's own token resolution (which reads
   prefers-color-scheme directly) during that brief window. Keep both
   dark branches in sync when changing values.

   Contrast (WCAG 1.4.3): light foregrounds verified ≥4.5:1 against their
   intended background — white for sharing-pills, the matching -bg tint
   for grant-select. Dark foregrounds verified ≥4.5:1 against the mixed
   page-dark + rgba(-bg) chip surface; rgba opacities tuned (0.16–0.18
   fill, 0.45 border) so the brighter -400 fg holds contrast on the chip
   without losing the chip's affordance. */
:root {
    --cs-color-share-public:  #15803d; /* green-700,  5.5:1 on white */
    --cs-color-share-shared:  #b45309; /* amber-700,  5.4:1 */
    --cs-color-share-private: #475569; /* slate-600,  7.5:1 */

    --cs-permission-none-fg:       var(--neutral-foreground-hint, #707070);
    --cs-permission-none-bg:       var(--neutral-layer-3, #ebebeb);
    --cs-permission-none-border:   var(--neutral-stroke-rest, #d1d1d1);

    --cs-permission-view-fg:       #1d4ed8; /* blue-700,   6.4:1 on view-bg   */
    --cs-permission-view-bg:       #eff6ff; /* blue-50   */
    --cs-permission-view-border:   #bfdbfe; /* blue-200  */

    --cs-permission-edit-fg:       #c2410c; /* orange-700, 4.9:1 on edit-bg   */
    --cs-permission-edit-bg:       #fff7ed; /* orange-50  */
    --cs-permission-edit-border:   #fed7aa; /* orange-200 */

    --cs-permission-manage-fg:     #6d28d9; /* violet-700, 6.5:1 on manage-bg */
    --cs-permission-manage-bg:     #f5f3ff; /* violet-50  */
    --cs-permission-manage-border: #ddd6fe; /* violet-200 */
}
[data-theme="dark"] {
    --cs-color-share-public:  #4ade80; /* green-400  */
    --cs-color-share-shared:  #fbbf24; /* amber-400  */
    --cs-color-share-private: #94a3b8; /* slate-400  */

    --cs-permission-view-fg:       #60a5fa; /* blue-400   */
    --cs-permission-view-bg:       rgba( 59, 130, 246, 0.16);
    --cs-permission-view-border:   rgba( 59, 130, 246, 0.45);

    --cs-permission-edit-fg:       #fb923c; /* orange-400 */
    --cs-permission-edit-bg:       rgba(249, 115,  22, 0.18);
    --cs-permission-edit-border:   rgba(249, 115,  22, 0.45);

    --cs-permission-manage-fg:     #a78bfa; /* violet-400 */
    --cs-permission-manage-bg:     rgba(139,  92, 246, 0.18);
    --cs-permission-manage-border: rgba(139,  92, 246, 0.45);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --cs-color-share-public:  #4ade80;
        --cs-color-share-shared:  #fbbf24;
        --cs-color-share-private: #94a3b8;

        --cs-permission-view-fg:       #60a5fa;
        --cs-permission-view-bg:       rgba( 59, 130, 246, 0.16);
        --cs-permission-view-border:   rgba( 59, 130, 246, 0.45);
        --cs-permission-edit-fg:       #fb923c;
        --cs-permission-edit-bg:       rgba(249, 115,  22, 0.18);
        --cs-permission-edit-border:   rgba(249, 115,  22, 0.45);
        --cs-permission-manage-fg:     #a78bfa;
        --cs-permission-manage-bg:     rgba(139,  92, 246, 0.18);
        --cs-permission-manage-border: rgba(139,  92, 246, 0.45);
    }
}

/* Pill mode → palette token. Light/dark resolution happens at the token
   layer above, so these mappings are theme-agnostic. */
.cs-sharing-pill--public  { --pill-fg: var(--cs-color-share-public);   }
.cs-sharing-pill--shared  { --pill-fg: var(--cs-color-share-shared);   }
.cs-sharing-pill--private { --pill-fg: var(--cs-color-share-private);  }
.cs-sharing-pill--view    { --pill-fg: var(--cs-permission-view-fg);   }
.cs-sharing-pill--edit    { --pill-fg: var(--cs-permission-edit-fg);   }
.cs-sharing-pill--manage  { --pill-fg: var(--cs-permission-manage-fg); }

/* Stealth-appearance buttons in row action groups (`cs-list__actions`)
   — the trash + edit + other inline icon buttons. Fluent's default
   stealth has a transparent rest state which doesn't read as
   interactive against the page surface. Add a very subtle resting
   tint so the button shape is visible without the buttons competing
   with the row's primary content or the page's Accent button. Hover
   bumps the tint to roughly Fluent's default hover weight. Scoped to
   row actions only so other stealth buttons elsewhere in the app are
   unaffected. */
/* Shared list-row layout — used by ItemCard's children list, the Inbox
   page, and any future "list of items with per-row actions" surface.
   Originally lived inside ItemCard.razor.css with ::deep; promoted here
   when spec 010 Phase 2 added the Inbox surface so it picks up the same
   layout without each page duplicating CSS. */
.cs-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.cs-list__row-wrapper {
    display: flex;
    align-items: stretch;
    gap: 0.5rem;
    border-radius: var(--control-corner-radius, 4px);
    min-width: 0;
}
.cs-list__row-wrapper > .cs-list__row {
    flex: 1 1 auto;
    min-width: 0;
}
.cs-list__row {
    display: block;
    color: inherit;
    text-decoration: none;
    border-radius: var(--control-corner-radius, 4px);
}
.cs-list__row:hover,
.cs-list__row:focus-visible {
    background: var(--neutral-fill-secondary-rest, #fafafa);
    outline: none;
}
.cs-list__actions {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    flex: 0 0 auto;
}

/* Empty-state block — used wherever a page has nothing to show
   (Inbox, ItemCard children list, error states). Same dashed-border
   "polite vacancy" treatment everywhere. */
.cs-empty {
    margin-top: 1rem;
    padding: 1.5rem;
    border: 1px dashed var(--neutral-stroke-rest, #d1d1d1);
    border-radius: var(--control-corner-radius, 4px);
    text-align: center;
}
.cs-empty__title {
    font-weight: 600;
    margin: 0 0 0.25rem 0;
}
.cs-empty__hint {
    margin: 0;
    color: var(--neutral-foreground-hint, #6e6e6e);
}

.cs-list__actions fluent-button[appearance="stealth"] {
    --stealth-rest: rgba(0, 0, 0, 0.05);
    --stealth-hover: rgba(0, 0, 0, 0.10);
}
[data-theme="dark"] .cs-list__actions fluent-button[appearance="stealth"] {
    --stealth-rest: rgba(255, 255, 255, 0.06);
    --stealth-hover: rgba(255, 255, 255, 0.12);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cs-list__actions fluent-button[appearance="stealth"] {
        --stealth-rest: rgba(255, 255, 255, 0.06);
        --stealth-hover: rgba(255, 255, 255, 0.12);
    }
}
.cs-list__actions fluent-button[appearance="stealth"]::part(control) {
    background: var(--stealth-rest) !important;
}
.cs-list__actions fluent-button[appearance="stealth"]:hover::part(control) {
    background: var(--stealth-hover) !important;
}

/* ───────────────────────────────────────────────────────────── */
/* Spec 014 Phase 4 / T071 — Publishing surface styles.          */
/* Mirrors the visual rhythm of the spec 013 messaging styles    */
/* (compact list rows, subtle handle, "edited X" affordance).    */
/* ───────────────────────────────────────────────────────────── */

/* Composer */
.cs-post-composer {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}
.cs-post-composer__heading {
    margin: 0 0 0.5rem 0;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--neutral-foreground-rest);
}
.cs-post-composer__audience-options {
    border: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.cs-post-composer__audience-option {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.5rem;
    border-radius: 4px;
    cursor: pointer;
}
.cs-post-composer__audience-option:hover {
    background: var(--neutral-fill-secondary-hover);
}
.cs-post-composer__audience-option input {
    margin-top: 0.25rem;
}
.cs-post-composer__audience-option > span {
    display: flex;
    flex-direction: column;
}
.cs-post-composer__audience-hint {
    font-size: 0.875rem;
    color: var(--neutral-foreground-hint);
    margin-top: 0.125rem;
}
.cs-post-composer__error {
    color: var(--error-foreground);
    margin: 0;
    font-size: 0.875rem;
}

/* Feed list */
.cs-feed-list,
.cs-drafts-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.cs-feed-list__row,
.cs-drafts-list__row {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 1rem;
    border: 1px solid var(--neutral-stroke-rest);
    border-radius: 6px;
    text-decoration: none;
    color: inherit;
    transition: background 0.1s ease;
}
.cs-feed-list__row:hover,
.cs-drafts-list__row:hover {
    background: var(--neutral-fill-secondary-hover);
}
.cs-feed-list__head,
.cs-drafts-list__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}
.cs-feed-list__author {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
}
.cs-feed-list__handle {
    color: var(--neutral-foreground-hint);
    font-size: 0.875rem;
}
.cs-feed-list__audience,
.cs-drafts-list__audience {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--neutral-foreground-hint);
    background: var(--neutral-fill-rest);
    padding: 0.125rem 0.5rem;
    border-radius: 999px;
}
.cs-feed-list__preview,
.cs-drafts-list__preview {
    margin: 0;
    color: var(--neutral-foreground-rest);
    line-height: 1.4;
}
.cs-feed-list__time,
.cs-drafts-list__time {
    font-size: 0.875rem;
    color: var(--neutral-foreground-hint);
}
.cs-feed-list__edited {
    color: var(--neutral-foreground-hint);
    font-style: italic;
}

/* Post detail */
.cs-post-detail__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: baseline;
}
.cs-post-detail__audience {
    font-weight: 600;
}
.cs-post-detail__edited {
    color: var(--neutral-foreground-hint);
    font-style: italic;
}
.cs-post-detail__body {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: 1rem 0;
}
.cs-post-detail__text {
    margin: 0;
    line-height: 1.6;
    white-space: pre-wrap;
}
.cs-post-detail__editor {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.cs-post-detail__audience-options {
    border: none;
    padding: 0;
    margin: 0;
    display: flex;
    gap: 1rem;
}
.cs-post-detail__audience-options label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
}
.cs-post-detail__history {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--neutral-stroke-rest);
}
.cs-post-detail__history-list {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0 0 0;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.cs-post-detail__history-list li {
    padding: 0.5rem;
    background: var(--neutral-fill-rest);
    border-radius: 4px;
}
.cs-post-detail__history-time {
    font-size: 0.75rem;
    color: var(--neutral-foreground-hint);
    display: block;
    margin-bottom: 0.25rem;
}

/* My posts hub (/posts). Same row shape as feed/drafts but with a
   prominent status pill (Draft = neutral, Published = accent). */
.cs-myposts-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.cs-myposts-list__row {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 1rem;
    border: 1px solid var(--neutral-stroke-rest);
    border-radius: 6px;
    text-decoration: none;
    color: inherit;
    transition: background 0.1s ease;
}
.cs-myposts-list__row:hover {
    background: var(--neutral-fill-secondary-hover);
}
.cs-myposts-list__head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.cs-myposts-list__status {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.125rem 0.5rem;
    border-radius: 999px;
}
.cs-myposts-list__status--draft {
    background: var(--neutral-fill-rest);
    color: var(--neutral-foreground-hint);
}
.cs-myposts-list__status--published {
    background: var(--accent-fill-rest);
    color: var(--accent-foreground-on-rest, white);
}
.cs-myposts-list__audience {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--neutral-foreground-hint);
    background: var(--neutral-fill-rest);
    padding: 0.125rem 0.5rem;
    border-radius: 999px;
}
.cs-myposts-list__preview {
    margin: 0;
    color: var(--neutral-foreground-rest);
    line-height: 1.4;
}
.cs-myposts-list__time {
    font-size: 0.875rem;
    color: var(--neutral-foreground-hint);
}

/* ───────────────────────────────────────────────────────────── */
/* Spec 018 / T024 + T025 — site-announcement banner.            */
/* Mounted in MainLayout BELOW the top nav (D1). Three severity  */
/* tiers (Info / Warning / Danger) with N1 dark-mode contrast    */
/* parity. Body uses pre-wrap so operator-typed newlines render. */
/* ───────────────────────────────────────────────────────────── */

.cs-announcements {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    /* Bottom padding gives the banner breathing room from the page
       header below. Without it the announcement crowds the h1. */
    padding: 0.5rem 1rem 1rem;
}
.cs-announcement {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.6rem 0.875rem;
    border-radius: var(--control-corner-radius, 6px);
    border: 1px solid var(--cs-ann-border, transparent);
    background: var(--cs-ann-bg, transparent);
    color: var(--cs-ann-fg, inherit);
}
.cs-announcement__body {
    flex: 1 1 auto;
    min-width: 0;
    /* T025 — preserve operator-typed newlines so the dev banner
       renders the way the operator wrote it. */
    white-space: pre-wrap;
    line-height: 1.45;
}
.cs-announcement__dismiss {
    flex: 0 0 auto;
    appearance: none;
    background: transparent;
    border: none;
    color: inherit;
    font-size: 1.25rem;
    line-height: 1;
    padding: 0 0.25rem;
    cursor: pointer;
    border-radius: 4px;
    opacity: 0.75;
}
.cs-announcement__dismiss:hover,
.cs-announcement__dismiss:focus-visible {
    opacity: 1;
    background: rgba(0, 0, 0, 0.08);
    outline: none;
}

/* Light-mode severity palettes. Backgrounds use 100-tier tints +
   300-tier borders + 900-tier foregrounds — all verified ≥7:1
   contrast against the tinted background. */
.cs-announcement--info {
    --cs-ann-bg:     #dbeafe; /* blue-100  */
    --cs-ann-border: #93c5fd; /* blue-300  */
    --cs-ann-fg:     #1e3a8a; /* blue-900  */
}
.cs-announcement--warning {
    --cs-ann-bg:     #fef3c7; /* amber-100 */
    --cs-ann-border: #fcd34d; /* amber-300 */
    --cs-ann-fg:     #78350f; /* amber-900 */
}
.cs-announcement--danger {
    --cs-ann-bg:     #fee2e2; /* red-100   */
    --cs-ann-border: #fca5a5; /* red-300   */
    --cs-ann-fg:     #7f1d1d; /* red-900   */
}

/* Dark-mode severity palettes (N1). Inverted ramp: 800-tier
   backgrounds (deep enough to read as a tinted block on the dark
   page surface, shallow enough not to look like an outage modal),
   500-tier borders for clear separation, 100-tier foregrounds for
   high-contrast text. Dual selectors mirror the spec 005 pill
   pattern so colors apply when EITHER the explicit data-theme
   attribute is dark OR the OS prefers dark and the user hasn't
   pinned light. */
[data-theme="dark"] .cs-announcement__dismiss:hover,
[data-theme="dark"] .cs-announcement__dismiss:focus-visible {
    background: rgba(255, 255, 255, 0.12);
}
[data-theme="dark"] .cs-announcement--info {
    --cs-ann-bg:     #1e40af; /* blue-800  */
    --cs-ann-border: #3b82f6; /* blue-500  */
    --cs-ann-fg:     #dbeafe; /* blue-100  */
}
[data-theme="dark"] .cs-announcement--warning {
    --cs-ann-bg:     #92400e; /* amber-800 */
    --cs-ann-border: #f59e0b; /* amber-500 */
    --cs-ann-fg:     #fef3c7; /* amber-100 */
}
[data-theme="dark"] .cs-announcement--danger {
    --cs-ann-bg:     #991b1b; /* red-800   */
    --cs-ann-border: #ef4444; /* red-500   */
    --cs-ann-fg:     #fee2e2; /* red-100   */
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cs-announcement__dismiss:hover,
    :root:not([data-theme="light"]) .cs-announcement__dismiss:focus-visible {
        background: rgba(255, 255, 255, 0.12);
    }
    :root:not([data-theme="light"]) .cs-announcement--info {
        --cs-ann-bg: #1e40af; --cs-ann-border: #3b82f6; --cs-ann-fg: #dbeafe;
    }
    :root:not([data-theme="light"]) .cs-announcement--warning {
        --cs-ann-bg: #92400e; --cs-ann-border: #f59e0b; --cs-ann-fg: #fef3c7;
    }
    :root:not([data-theme="light"]) .cs-announcement--danger {
        --cs-ann-bg: #991b1b; --cs-ann-border: #ef4444; --cs-ann-fg: #fee2e2;
    }
}

/* ───────────────────────────────────────────────────────────── */
/* Spec 019 / T014 — profile onboarding cues.                    */
/*   1. Avatar dot badge (persistent until profile complete)     */
/*   2. /home welcome banner (one-time, dismissible per user)    */
/* ───────────────────────────────────────────────────────────── */

/* Anchor for the absolute-positioned dot. Stamped on the
   <span> wrapping the FluentPersona / FluentIcon in AppNavBar +
   BottomMenu around the profile slot. */
.cs-profile-badge-host {
    position: relative;
    display: inline-flex;
    align-items: center;
}

/* Small filled accent-colored dot anchored at the top-right of
   the avatar. No animation per D2 — calm, gentle reminder, not
   nagging. */
.cs-profile-badge-dot {
    position: absolute;
    top: -2px;
    right: -2px;
    width: 10px;
    height: 10px;
    border-radius: 999px;
    background: var(--accent-fill-rest, #d13b8b);
    /* Outline against the page surface so the dot reads cleanly
       even when the avatar is the same hue (rare but possible). */
    box-shadow: 0 0 0 2px var(--neutral-layer-2, #fff);
    pointer-events: none;
}

/* Welcome banner on /home. Mirrors the spec 018 site-announcement
   rhythm (border-radius, padding, flex layout) but with a richer
   inner shape (title + copy + CTA + dismiss) since the welcome
   banner is more onboarding moment than ambient notice. */
.cs-profile-welcome-banner {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin: 0 0 1rem 0;
    padding: 0.875rem 1rem;
    border-radius: var(--control-corner-radius, 6px);
    border: 1px solid var(--cs-pwb-border, #93c5fd);
    background: var(--cs-pwb-bg, #dbeafe);
    color: var(--cs-pwb-fg, #1e3a8a);
}
.cs-profile-welcome-banner__body {
    flex: 1 1 auto;
    min-width: 0;
}
.cs-profile-welcome-banner__title {
    font-weight: 600;
    margin-bottom: 0.125rem;
}
.cs-profile-welcome-banner__copy {
    line-height: 1.45;
}
.cs-profile-welcome-banner__actions {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    flex: 0 0 auto;
}
.cs-profile-welcome-banner__cta {
    appearance: none;
    background: var(--accent-fill-rest, #d13b8b);
    color: #fff;
    border: none;
    border-radius: var(--control-corner-radius, 6px);
    padding: 0.4rem 0.875rem;
    font-weight: 500;
    cursor: pointer;
}
.cs-profile-welcome-banner__cta:hover,
.cs-profile-welcome-banner__cta:focus-visible {
    background: var(--accent-fill-hover, #b73378);
    outline: none;
}
.cs-profile-welcome-banner__dismiss {
    appearance: none;
    background: transparent;
    border: none;
    color: inherit;
    font-size: 1.25rem;
    line-height: 1;
    padding: 0 0.25rem;
    cursor: pointer;
    border-radius: 4px;
    opacity: 0.75;
}
.cs-profile-welcome-banner__dismiss:hover,
.cs-profile-welcome-banner__dismiss:focus-visible {
    opacity: 1;
    background: rgba(0, 0, 0, 0.08);
    outline: none;
}

/* Dark-mode palette for the welcome banner. Same dual-selector
   discipline as spec 018 — explicit [data-theme="dark"] and the
   prefers-color-scheme fallback for first-paint before our attr
   is written. */
[data-theme="dark"] .cs-profile-welcome-banner {
    --cs-pwb-bg:     #1e40af; /* blue-800 */
    --cs-pwb-border: #3b82f6; /* blue-500 */
    --cs-pwb-fg:     #dbeafe; /* blue-100 */
}
[data-theme="dark"] .cs-profile-welcome-banner__dismiss:hover,
[data-theme="dark"] .cs-profile-welcome-banner__dismiss:focus-visible {
    background: rgba(255, 255, 255, 0.12);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cs-profile-welcome-banner {
        --cs-pwb-bg: #1e40af; --cs-pwb-border: #3b82f6; --cs-pwb-fg: #dbeafe;
    }
    :root:not([data-theme="light"]) .cs-profile-welcome-banner__dismiss:hover,
    :root:not([data-theme="light"]) .cs-profile-welcome-banner__dismiss:focus-visible {
        background: rgba(255, 255, 255, 0.12);
    }
}

/* Spec 019 / T020 + T021 — JIT-gate explainer for the
   MessageComposer Send button + ConnectionFlowPanel send. Same
   visual rhythm as a Fluent message-bar but lighter — it sits
   ABOVE the disabled action so the user sees both the reason and
   the path to fix it (Set up profile link). */
.cs-profile-gate-explainer {
    margin: 0.25rem 0 0.5rem 0;
    padding: 0.6rem 0.875rem;
    border-radius: var(--control-corner-radius, 6px);
    border: 1px solid var(--cs-pwb-border, #93c5fd);
    background: var(--cs-pwb-bg, #dbeafe);
    color: var(--cs-pwb-fg, #1e3a8a);
    line-height: 1.45;
}
[data-theme="dark"] .cs-profile-gate-explainer {
    --cs-pwb-bg:     #1e40af;
    --cs-pwb-border: #3b82f6;
    --cs-pwb-fg:     #dbeafe;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cs-profile-gate-explainer {
        --cs-pwb-bg: #1e40af; --cs-pwb-border: #3b82f6; --cs-pwb-fg: #dbeafe;
    }
}
.cs-profile-gate-link {
    margin-left: 0.5rem;
    font-weight: 500;
}

/* ───────────────────────────────────────────────────────────── */
/* Spec 021 / T020 + T023 — Profile.razor toggle consequence-    */
/* framing copy + privacy-mode confirmation modal.                */
/* ───────────────────────────────────────────────────────────── */

/* Group wraps each FluentSwitch + its hint <p> so the hint
   visually clusters with its toggle without competing with the
   switch label. */
.cs-profile-toggle-group {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.cs-profile-toggle-hint {
    margin: 0 0 0 2.5rem;  /* roughly aligns under the toggle's label */
    font-size: 0.875rem;
    line-height: 1.4;
    color: var(--neutral-foreground-hint, #6e6e6e);
    max-width: 36rem;
}

/* Privacy-mode confirmation dialog content. Mirrors spec 019's
   PostComposer soft-warn rhythm — title + copy + horizontal CTA
   row, all wrapped in <FluentDialog>. */
.cs-privacy-mode-confirm {
    padding: 0.5rem 0.75rem;
    max-width: 32rem;
}
.cs-privacy-mode-confirm__title {
    margin: 0 0 0.5rem 0;
    font-size: 1.125rem;
    font-weight: 600;
}
.cs-privacy-mode-confirm__copy {
    margin: 0 0 1rem 0;
    line-height: 1.45;
}

/* Same shape as .cs-privacy-mode-confirm; reused by the
   ProfileImageEditor's clear-image confirmation modal. */
.cs-image-clear-confirm {
    padding: 0.5rem 0.75rem;
    max-width: 32rem;
}
.cs-image-clear-confirm__title {
    margin: 0 0 0.5rem 0;
    font-size: 1.125rem;
    font-weight: 600;
}
.cs-image-clear-confirm__copy {
    margin: 0 0 1rem 0;
    line-height: 1.45;
}

/* ───────────────────────────────────────────────────────────── */
/* Spec 020 / T022 — single avatar img class for <UserAvatar>.   */
/* Round, centre-cropped, no border. Width / height are inlined  */
/* by the component from its Size parameter so one CSS rule      */
/* covers every size variant.                                    */
/* ───────────────────────────────────────────────────────────── */
.cs-avatar {
    border-radius: 50%;
    object-fit: cover;
    object-position: center;
    display: inline-block;
    /* Vertical-align middle keeps the avatar baseline-aligned
       with adjacent text in inline contexts (nav rows etc.). */
    vertical-align: middle;
}

/* ───────────────────────────────────────────────────────────── */
/* Spec 020 / T042 — ProfileImageEditor on /profile.              */
/* Two instances: avatar (square 96px preview) + banner (~3:1     */
/* wide preview). Same visual rhythm as the form fields below.    */
/* ───────────────────────────────────────────────────────────── */

.cs-profile__images {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-bottom: 1.5rem;
}
.cs-profile__section-title {
    margin: 0.5rem 0 0.25rem 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--neutral-foreground-rest);
}

.cs-profile-image-editor {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
}
.cs-profile-image-editor--banner {
    flex-direction: column;
    gap: 0.5rem;
}
.cs-profile-image-editor__preview {
    flex: 0 0 auto;
}
.cs-profile-image-editor--banner .cs-profile-image-editor__preview {
    width: 100%;
    max-width: 36rem;
}
.cs-profile-image-editor__banner-img {
    width: 100%;
    max-width: 36rem;
    aspect-ratio: 3 / 1;
    object-fit: cover;
    border-radius: var(--control-corner-radius, 6px);
    display: block;
}
.cs-profile-image-editor__banner-placeholder {
    width: 100%;
    max-width: 36rem;
    aspect-ratio: 3 / 1;
    border-radius: var(--control-corner-radius, 6px);
    background: linear-gradient(135deg,
        var(--neutral-fill-rest, #f0f0f0) 0%,
        var(--neutral-fill-secondary-rest, #e6e6e6) 100%);
}
.cs-profile-image-editor__controls {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    flex: 1 1 auto;
    min-width: 0;
}
.cs-profile-image-editor__file {
    display: block;
}
.cs-profile-image-editor__error {
    margin: 0;
    color: var(--error-foreground-rest, #c53030);
    font-size: 0.875rem;
}
.cs-profile-image-editor__hint {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--neutral-foreground-hint, #6e6e6e);
}

/* ───────────────────────────────────────────────────────────── */
/* Spec 020 / T052 — /@{handle} public profile page.              */
/* Hero banner full-width, circular avatar overlapping the        */
/* banner's bottom-left (legacy reference shape per N1).          */
/* ───────────────────────────────────────────────────────────── */

.cs-public-profile {
    position: relative;
    /* Negative top margin pulls the banner up against the page
       container's edge — the legacy app's hero went full-width.
       Page padding still applies to the identity block below. */
    margin-top: -1rem;
}
.cs-public-profile__banner {
    width: 100%;
    aspect-ratio: 4 / 1;
    overflow: hidden;
    background: linear-gradient(135deg,
        var(--neutral-fill-rest, #f0f0f0) 0%,
        var(--neutral-fill-secondary-rest, #e6e6e6) 100%);
    border-radius: var(--control-corner-radius, 6px);
}
.cs-public-profile__banner-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.cs-public-profile__banner-placeholder {
    width: 100%;
    height: 100%;
}
.cs-public-profile__avatar-overlay {
    /* Negative top margin pulls the avatar UP onto the banner's
       bottom edge so it overlaps slightly (Mastodon-style). The
       white ring around the avatar (via box-shadow) separates it
       visually from the banner. */
    margin-top: -64px;
    margin-left: 1.5rem;
    width: 128px;
    height: 128px;
}
.cs-public-profile__avatar-overlay > .cs-avatar,
.cs-public-profile__avatar-overlay > div.fluent-persona {
    box-shadow: 0 0 0 4px var(--neutral-layer-1, #ffffff);
}
.cs-public-profile__identity {
    margin-top: 0.75rem;
    margin-left: 1.5rem;
}
.cs-public-profile__name {
    margin: 0 0 0.125rem 0;
    font-weight: 600;
}
.cs-public-profile__handle {
    margin: 0 0 1rem 0;
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: var(--cs-reading-font-size-sm, 1.0625rem);
}
.cs-public-profile__bio {
    margin: 0;
    font-size: var(--cs-reading-font-size, 1.1875rem);
    line-height: var(--cs-reading-line-height, 1.65);
    max-width: 40rem;
}


/* ─────────────────────────────────────────────────────────── */
/* Spec 009 / Phase 3 / T053 — AttachmentUploader. Multi-file
   uploader for attachments under a parent Item. Per-file row
   with name + size + progress bar + status + retry. */
.cs-attachment-uploader {
    margin: 1rem 0;
}
/* Spec 033 / Phase 4 — visually hide the native file input but keep it
   a real, focusable, screen-reader-reachable form control inside the
   styled label. (Not display:none — that would remove it from the
   accessibility tree and the tab order.) */
.cs-attachment-uploader__file {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* The brand-styled "Choose files…" affordance. Calm, neutral — not a
   loud accent button; uploading is a quiet utility action, not the
   page's primary CTA. */
.cs-attachment-uploader__choose-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    padding: 0.5rem 1rem;
    border: 1px solid var(--neutral-stroke-rest, #d0d0d0);
    border-radius: var(--control-corner-radius, 4px);
    background: var(--neutral-fill-rest, #f5f5f5);
    color: var(--neutral-foreground-rest);
    font-size: 0.9375rem;
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease;
}

.cs-attachment-uploader__choose-btn:hover {
    background: var(--neutral-fill-hover, #ebebeb);
    border-color: var(--neutral-foreground-hint);
}

/* focus-within so keyboard users tabbing to the hidden input still
   get a visible focus ring on the label that stands in for it. */
.cs-attachment-uploader__choose-btn:focus-within {
    outline: 2px solid var(--accent-fill-rest);
    outline-offset: 2px;
}
.cs-attachment-uploader__list {
    list-style: none;
    padding: 0;
    margin: 0;
}
.cs-attachment-uploader__item {
    border: 1px solid var(--neutral-stroke-rest, #d0d0d0);
    border-radius: 0.5rem;
    padding: 0.75rem;
    margin-bottom: 0.5rem;
}
.cs-attachment-uploader__row {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    align-items: baseline;
    font-size: 0.95rem;
}
.cs-attachment-uploader__name {
    font-weight: 500;
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.cs-attachment-uploader__size {
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-variant-numeric: tabular-nums;
}
.cs-attachment-uploader__status {
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.875rem;
}
.cs-attachment-uploader__progress {
    height: 4px;
    background: var(--neutral-fill-rest, #f0f0f0);
    border-radius: 2px;
    overflow: hidden;
    margin-top: 0.5rem;
}
.cs-attachment-uploader__progress-bar {
    height: 100%;
    background: var(--accent-fill-rest, #1d4ed8);
    transition: width 0.2s ease-out;
}
.cs-attachment-uploader__error {
    color: var(--cs-warn-fg, #9a3412);
    margin: 0.5rem 0 0 0;
    font-size: 0.875rem;
}


/* ─────────────────────────────────────────────────────────── */
/* Spec 009 / Phase 4 — attachment viewers (photo / video /
   pdf / file) inline-rendered on the IsAttachment Item detail
   page via ItemCard. */
.cs-item-card__attachment-preview {
    margin: 1rem 0;
}

.cs-photo-viewer {
    display: flex;
    justify-content: center;
    background: var(--neutral-fill-rest, #f5f5f5);
    border-radius: 0.5rem;
    overflow: hidden;
}
.cs-photo-viewer__img {
    max-width: 100%;
    max-height: 80vh;
    height: auto;
    display: block;
    /* Belt-and-suspenders for the fallback-to-original code path
       (when the normalized derivative is missing, the read URL
       serves the raw original which still has EXIF metadata).
       The server-side derivative generation applies orientation
       on its own, so this only matters for that fallback. */
    image-orientation: from-image;
}
.cs-photo-viewer--loading,
.cs-photo-viewer--error,
.cs-video-player--loading,
.cs-video-player--error,
.cs-pdf-viewer--loading,
.cs-pdf-viewer--error {
    padding: 2rem;
    text-align: center;
    color: var(--neutral-foreground-hint, #6e6e6e);
    background: var(--neutral-fill-rest, #f5f5f5);
    border-radius: 0.5rem;
}

.cs-video-player {
    width: 100%;
}
.cs-video-player__video {
    width: 100%;
    max-height: 80vh;
    background: #000;
    border-radius: 0.5rem;
}

.cs-pdf-viewer {
    width: 100%;
}
.cs-pdf-viewer__embed {
    width: 100%;
    height: 80vh;
    border: 1px solid var(--neutral-stroke-rest, #d0d0d0);
    border-radius: 0.5rem;
}
.cs-pdf-viewer__fallback {
    margin: 0.5rem 0 0 0;
    font-size: 0.875rem;
    color: var(--neutral-foreground-hint, #6e6e6e);
}

.cs-file-download {
    border: 1px solid var(--neutral-stroke-rest, #d0d0d0);
    border-radius: 0.5rem;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.cs-file-download__filename {
    font-weight: 500;
    font-size: 1rem;
}
.cs-file-download__meta {
    color: var(--neutral-foreground-hint, #6e6e6e);
    font-size: 0.875rem;
    font-variant-numeric: tabular-nums;
}
.cs-file-download__link {
    margin-top: 0.5rem;
    align-self: flex-start;
    background: var(--accent-fill-rest, #1d4ed8);
    color: var(--foreground-on-accent-rest, #fff);
    padding: 0.5rem 1rem;
    border-radius: 0.375rem;
    text-decoration: none;
}
.cs-file-download__link:hover {
    background: var(--accent-fill-hover, #1e40af);
}
.cs-file-download__error {
    margin: 0.25rem 0 0 0;
    color: var(--cs-warn-fg, #9a3412);
    font-size: 0.875rem;
}

/* Spec 027 / T050 — Media rollup responsive tile grid. 4-col on
   desktop, 2-col on narrow viewports. Tiles are square via aspect-
   ratio so missing/loading thumbnails don't reflow the layout. */
.cs-media-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.75rem;
    margin-top: 1rem;
}
@media (max-width: 640px) {
    .cs-media-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}
.cs-media-grid__tile {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    color: inherit;
    text-decoration: none;
    border-radius: var(--control-corner-radius, 4px);
    overflow: hidden;
}
.cs-media-grid__tile:hover,
.cs-media-grid__tile:focus-visible {
    background: var(--neutral-fill-secondary-rest, #fafafa);
    outline: none;
}
.cs-media-grid__img,
.cs-media-grid__poster {
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    background: var(--neutral-layer-2, #f6f6f6);
    border-radius: var(--control-corner-radius, 4px);
}
.cs-media-grid__poster {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--neutral-foreground-hint, #6e6e6e);
}

/* Spec 033 / Phase 4 — video tile play-icon overlay. Lives in the
   shared MediaGridThumbnail so the Media rollup AND the Attachments
   tab show a consistent play affordance over video poster frames.
   The wrap is position:relative; the video keeps the square
   aspect-ratio via .cs-media-grid__img. The overlay is centered and
   pointer-events:none so the consumer's wrapping <a> still handles
   the click. SVG fill is forced #fff with a drop-shadow — without
   the explicit fill, the theme-accent JS pins FluentIcon paths to
   the accent token (orange), which Brett flagged as both wrong and
   low-contrast over arbitrary poster frames. */
.cs-media-grid__video-wrap {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
}
.cs-media-grid__video-wrap .cs-media-grid__img {
    width: 100%;
    height: 100%;
}
.cs-media-grid__play-overlay {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    pointer-events: none;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.55));
}
.cs-media-grid__play-overlay svg {
    width: 44px;
    height: 44px;
    fill: #fff;
}
.cs-media-grid__caption {
    font-size: 0.875rem;
    color: var(--neutral-foreground-hint, #6e6e6e);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 0 0.25rem 0.25rem;
}

/* ─── Spec 032 / D14 — JavaScript-required fallback ─────────────────────────
   The inline script in App.razor adds `js-enabled` to <html> before <body>
   parses. When scripting is disabled the class never appears and the
   `html:not(.js-enabled)` selectors below take effect: the note becomes
   visible and the interactive affordances (top-right Log In, footer CTAs,
   AppNavBar, BottomMenu) hide since their underlying flows need JS to work.
   Footer links (Ethics, Contact) and the static prose stay visible. */
.cs-noscript-note {
    display: none;
    margin: 0;
    padding: 1rem 1.5rem;
    border-bottom: 1px solid #e0d8b8;
    background: #fff8e1;
    color: #2a2a2a;
    font-size: 0.9375rem;
    line-height: 1.5;
    text-align: center;
}

html:not(.js-enabled) .cs-noscript-note {
    display: block;
}

html:not(.js-enabled) .cs-topbar,
html:not(.js-enabled) .cs-bottombar,
html:not(.js-enabled) .cs-anon-chrome__login,
html:not(.js-enabled) .cs-landing__footer-cta {
    display: none !important;
}
