@layer components {
  cms-panel-switcher {
    display: inline-flex;
  }
  cms-panel-switcher .cms-panel-switcher-link {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
    width: 100%;
    color: inherit;
    text-decoration: none;
  }
  cms-list-search {
    display: block;
    min-width: 12rem;
  }
  cms-list-search .cms-list-search-hint {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
  cms-inplace-block-list {
    display: block;
  }
  cms-inplace-block-list > .cms-inplace-item {
    position: relative;
  }
  cms-inplace-block-list .cms-inplace-insert {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 0;
    margin-bottom: var(--pl-space-xs);
    border: none;
    background: transparent;
    color: var(--pl-color-text-muted);
    cursor: pointer;
    opacity: 0;
    transition: opacity var(--pl-transition-fast);
  }
  cms-inplace-block-list .cms-inplace-insert:hover, cms-inplace-block-list .cms-inplace-insert:focus-visible {
    opacity: 1;
  }
  cms-inplace-block-list > .cms-inplace-empty {
    font-family: var(--pl-font-family);
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
  }
  cms-inplace-editor {
    display: block;
  }
  cms-inplace-editor > .cms-inplace-header {
    display: flex;
    justify-content: flex-end;
    margin-bottom: var(--pl-space-md);
  }
  cms-inplace-editor .cms-inplace-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--pl-space-md);
  }
  cms-inplace-block {
    display: block;
    position: relative;
    border: 1px dashed var(--pl-color-border);
    border-radius: var(--pl-radius-md);
    margin-bottom: var(--pl-space-sm);
  }
  cms-inplace-block:hover, cms-inplace-block:focus-within {
    outline: 2px solid var(--pl-color-primary);
    outline-offset: 1px;
  }
  cms-inplace-block > .cms-inplace-toolbar {
    position: absolute;
    top: var(--pl-space-xs);
    right: var(--pl-space-xs);
    display: flex;
    gap: 2px;
    z-index: 2;
    background: var(--pl-color-surface-raised);
    border: 1px solid var(--pl-color-border);
    border-radius: var(--pl-radius-md);
    padding: 2px;
  }
  cms-inplace-block > .cms-inplace-toolbar {
    opacity: 0;
    transition: opacity var(--pl-transition-fast);
  }
  cms-inplace-block:hover > .cms-inplace-toolbar, cms-inplace-block:focus-within > .cms-inplace-toolbar {
    opacity: 1;
  }
  @media (hover: none) {
    cms-inplace-block > .cms-inplace-toolbar {
      opacity: 1;
    }
  }
  cms-inplace-block .cms-inplace-preview {
    padding: var(--pl-space-sm);
  }
  cms-inplace-block > .cms-inplace-children {
    display: block;
    margin: 0 var(--pl-space-sm) var(--pl-space-sm) var(--pl-space-xl);
  }
  cms-inplace-block > .cms-inplace-children > .cms-inplace-children-label {
    display: block;
    font-family: var(--pl-font-family);
    font-size: var(--pl-font-size-xs);
    font-weight: var(--pl-font-weight-medium);
    color: var(--pl-color-text-muted);
    margin-bottom: var(--pl-space-xs);
  }
  .cms-config-sheet-body {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-md);
    padding: 0 var(--pl-space-xl);
  }
  cms-inline-cell {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-2xs);
    max-width: 100%;
  }
  cms-inline-cell .cms-inline-edit {
    opacity: 0;
    transition: opacity var(--pl-transition-fast);
  }
  cms-inline-cell:hover .cms-inline-edit, cms-inline-cell:focus-within .cms-inline-edit {
    opacity: 1;
  }
  @media (hover: none) {
    cms-inline-cell .cms-inline-edit {
      opacity: 1;
    }
  }
  cms-inline-cell[data-editing] .cms-inline-edit, cms-inline-cell[data-draft] .cms-inline-edit {
    opacity: 1;
  }
  cms-inline-cell .cms-inline-popover {
    position: relative;
  }
  cms-inline-cell .cms-inline-draft-dot {
    position: absolute;
    top: 0;
    right: 0;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--pl-color-primary);
    pointer-events: none;
  }
  .cms-inline-pop {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-sm);
  }
  .cms-inline-pop-title {
    font-size: var(--pl-font-size-sm);
    font-weight: 600;
  }
  .cms-inline-pop-loading {
    height: 2rem;
  }
  .cms-inline-pop-footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--pl-space-2xs);
  }
  .cms-inline-pop.cms-inline-busy {
    opacity: 0.6;
  }
  .cms-inline-message {
    color: var(--pl-color-destructive);
    font-size: var(--pl-font-size-sm);
  }
  cms-feedback {
    display: contents;
  }
  cms-toaster {
    display: contents;
  }
  cms-flash-relay {
    display: none;
  }
  cms-media-upload {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-md);
    padding-top: var(--pl-space-md);
  }
  .cms-media-upload-pending {
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
  }
  .cms-media-upload-error {
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-destructive);
  }
  cms-quick-add {
    display: block;
  }
  cms-quick-add .cms-quick-add-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--pl-space-sm);
  }
  cms-quick-add .cms-quick-add-field {
    flex: 1 1 10rem;
    min-width: 8rem;
  }
  cms-quick-add .cms-quick-add-errors {
    flex-basis: 100%;
  }
  cms-quick-add .cms-quick-add-errors:not(:has(.cms-quick-add-error)) {
    display: none;
  }
  cms-quick-add .cms-quick-add-error {
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-destructive);
  }
  @media (max-width: 40rem) {
    cms-quick-add .cms-quick-add-row {
      flex-direction: column;
      align-items: stretch;
    }
  }
  cms-widget-tree-editor {
    display: block;
  }
  cms-widget-block-list {
    display: block;
  }
  cms-widget-block-list > pl-sortable > pl-sortable-item {
    margin-bottom: var(--pl-space-sm);
  }
  cms-widget-block-list > pl-sortable > pl-sortable-item > pl-collapsible {
    display: block;
    border: 1px solid var(--pl-color-border);
    border-radius: var(--pl-radius-md);
    padding: var(--pl-space-xs) var(--pl-space-sm);
    background: var(--pl-color-surface-raised);
  }
  cms-widget-block-list > pl-sortable > pl-sortable-item .cms-widget-block-header {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
  }
  cms-widget-block-list > pl-sortable > pl-sortable-item .cms-widget-block-header > pl-collapsible-trigger {
    flex: 1;
  }
  cms-widget-block-list > pl-sortable > pl-sortable-item .cms-widget-insert {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 0;
    margin-bottom: var(--pl-space-xs);
    border: none;
    background: transparent;
    color: var(--pl-color-text-muted);
    cursor: pointer;
    opacity: 0;
    transition: opacity var(--pl-transition-fast);
  }
  cms-widget-block-list > pl-sortable > pl-sortable-item .cms-widget-insert:hover, cms-widget-block-list > pl-sortable > pl-sortable-item .cms-widget-insert:focus-visible {
    opacity: 1;
  }
  cms-widget-block-list .cms-widget-children {
    display: block;
    margin-top: var(--pl-space-sm);
  }
  cms-widget-block-list .cms-widget-children-label {
    display: block;
    font-family: var(--pl-font-family);
    font-size: var(--pl-font-size-xs);
    font-weight: var(--pl-font-weight-medium);
    color: var(--pl-color-text-muted);
    margin-bottom: var(--pl-space-xs);
  }
  cms-widget-block-list > .cms-widget-tree-empty {
    font-family: var(--pl-font-family);
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
  }
  cms-widget-block-body {
    display: block;
    padding: var(--pl-space-sm) 0 var(--pl-space-sm) var(--pl-space-xl);
  }
  cms-widget-block-body[data-collapsed] {
    display: none;
  }
  cms-widget-picker {
    display: contents;
  }
  .cms-widget-picker-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .cms-widget-picker-description {
    font-size: var(--pl-font-size-xs);
    color: var(--pl-color-text-muted);
  }
  cms-command-palette {
    display: contents;
  }
  .cms-command-palette-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .cms-command-palette-description {
    font-size: var(--pl-font-size-xs);
    color: var(--pl-color-text-muted);
  }
  cms-action-sheet {
    display: contents;
  }
  cms-column-picker {
    display: block;
  }
  cms-secret-reveal {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--pl-space-2xs);
  }
  cms-secret-reveal .cms-secret-value {
    font-family: var(--pl-font-family-mono);
    font-size: var(--pl-font-size-sm);
    word-break: break-all;
  }
  cms-secret-reveal .cms-secret-error {
    color: var(--pl-color-destructive);
    font-size: var(--pl-font-size-sm);
  }
}
@layer patterns {
  /* ===== Shell chrome ===== */
  /* The brand is the header's ONLY elastic item: it may shrink (min-width: 0
     defeats the flex item's automatic content floor) and it ellipsizes rather
     than being clipped mid-glyph by the frame's overflow-x: clip. The anchor
     carries the full name as its title, so the trimmed word is still
     readable. */
  .cms-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-sm);
    min-width: 0;
    text-decoration: none;
    color: var(--pl-color-text);
  }
  .cms-brand-logo {
    display: block;
    height: 1.75rem;
    width: auto;
  }
  .cms-brand-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--pl-font-size-lg);
    font-weight: var(--pl-font-weight-bold);
    letter-spacing: -0.01em;
  }
  .cms-panel-title {
    color: var(--pl-color-text-muted);
    font-size: var(--pl-font-size-sm);
    padding-left: var(--pl-space-md);
    border-left: 1px solid var(--pl-color-border);
    white-space: nowrap;
  }
  .cms-topbar-start {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
  }
  .cms-topbar-end {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
    margin-left: auto;
    /* The header is a flex row: without this a select or a menu trigger
       gets squeezed into a stub once the row runs out of room. */
  }
  .cms-topbar-end > * {
    flex-shrink: 0;
  }
  /* ===== Narrow register (the frame's own breakpoint, see pl-app-shell) =====
     The chrome sheds what a phone has no room for: the panel title (the
     brand already names the panel). The band itself WRAPS at this width
     (pl-app-header), so the account menu and the theme control keep their
     full size on a line of their own instead of competing with the brand.
     Page widths follow the plumage frame, which already drops its padding
     and turns the sidebar into a drawer. Word-wrapping keeps a long record
     title from pushing the page wider than the viewport. */
  @media (max-width: 1023.98px) {
    .cms-panel-title {
      display: none;
    }
    /* break-word, NOT anywhere: `anywhere` also lowers the min-content
       width, which is what let the list table squeeze a hostname column to
       a few characters and fragment "starfleet.lifeboat" into
       "star / flee / t.li / fe". break-word breaks a word only as the last
       resort against overflow and leaves intrinsic sizing alone, so a table
       column still asks for its longest token and scrolls instead. */
    .cms-shell {
      min-width: 0;
      overflow-wrap: break-word;
    }
    .cms-shell h1,
    .cms-shell h2 {
      word-break: normal;
    }
  }
  /* Identifiers stay WHOLE inside the list table at every width: the scroll
     box is the overflow answer here, never a mid-token break. Unconditional,
     because the inherited value is the only thing that could reach in. */
  .cms-table-scroll {
    overflow-wrap: normal;
    word-break: normal;
  }
  .cms-error-page {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 50vh;
  }
  .cms-error-actions {
    display: flex;
    gap: var(--pl-space-sm);
    flex-wrap: wrap;
    justify-content: center;
    margin-top: var(--pl-space-md);
  }
  .cms-sidebar-nav {
    flex: 1;
    padding: var(--pl-space-md) var(--pl-space-sm);
  }
  /* AIDEV-NOTE: the section-heading register and the group-content indent used to be
     overridden here and NEVER applied -- these were `@layer patterns` rules against a
     plumage tag style in `@layer components`, and components outranks patterns, so the
     CMS sidebar rendered every section header as a plain nav item. The register now
     lives where the component owns it (plumage's m.nav-section-label); an override
     here could only ever be dead CSS again. */
  .cms-sidebar-header:empty,
  .cms-sidebar-footer:empty,
  .cms-topbar-start:empty {
    display: none;
  }
  .cms-sidebar-footer {
    padding: var(--pl-space-md);
    border-top: 1px solid var(--pl-color-border-muted);
  }
  .cms-shell {
    max-width: 72rem;
    margin: 0 auto;
  }
  /* Data-dense pages opt out of the reading-width cap by carrying
     data-cms-wide on their root (list pages do); the shell honors the
     contract without knowing page types. */
  .cms-shell:has([data-cms-wide]) {
    max-width: 100rem;
  }
  /* A surface-less page (account) framed in the shell: the module's own
     card styles still apply, the shell only bounds its width. */
  .cms-framed-page {
    max-width: 32rem;
    margin: 0 auto;
  }
  .cms-framed-page[data-wide] {
    max-width: 48rem;
  }
  /* ===== Breadcrumbs (child-resource record pages) ===== */
  .cms-breadcrumbs {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--pl-space-xs);
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
    margin-bottom: var(--pl-space-sm);
  }
  .cms-breadcrumbs a {
    color: inherit;
    text-decoration: none;
  }
  .cms-breadcrumbs a:hover {
    color: var(--pl-color-text);
    text-decoration: underline;
  }
  .cms-breadcrumbs [aria-current=page] {
    color: var(--pl-color-text);
    font-weight: var(--pl-font-weight-medium);
  }
  .cms-breadcrumbs pl-icon {
    color: var(--pl-color-text-muted);
  }
  /* ===== Shared page headers ===== */
  .cms-panel-index-header h1,
  .cms-resource-form-header h1,
  .cms-record-revisions-header h1,
  .cms-revision-diff-header h1,
  .cms-dashboard-header h1 {
    font-size: var(--pl-font-size-2xl);
    font-weight: var(--pl-font-weight-bold);
    letter-spacing: -0.02em;
    margin: 0;
  }
  .cms-panel-index-header,
  .cms-resource-form-header,
  .cms-record-revisions-header,
  .cms-revision-diff-header,
  .cms-dashboard-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--pl-space-md);
    margin-bottom: var(--pl-space-lg);
  }
  .cms-resource-list-actions,
  .cms-resource-form-actions {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
    margin-left: auto;
  }
  /* ===== Activity detail page ===== */
  .cms-activity-detail {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-lg);
  }
  .cms-activity-detail-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--pl-space-md);
  }
  .cms-activity-detail-heading {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--pl-space-sm);
  }
  .cms-activity-detail-heading h1 {
    font-size: var(--pl-font-size-2xl);
    font-weight: var(--pl-font-weight-bold);
    letter-spacing: -0.02em;
    margin: 0;
  }
  .cms-activity-detail-nav {
    margin-left: auto;
  }
  .cms-activity-meta {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--pl-space-xs) var(--pl-space-lg);
    margin: 0;
  }
  .cms-activity-meta-row {
    display: contents;
  }
  .cms-activity-meta dt {
    font-weight: var(--pl-font-weight-medium);
    color: var(--pl-color-text-muted);
  }
  .cms-activity-meta dd {
    margin: 0;
    overflow-wrap: anywhere;
  }
  /* First-column record link: reads as text, honest as a link on hover. */
  .cms-row-link {
    color: inherit;
    text-decoration: none;
    font-weight: var(--pl-font-weight-medium);
  }
  .cms-row-link:hover {
    color: var(--pl-color-primary);
    text-decoration: underline;
  }
  /* ===== Panel index (fallback landing) ===== */
  .cms-panel-index-group + .cms-panel-index-group {
    margin-top: var(--pl-space-xl);
  }
  .cms-panel-index-group-label {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
    margin: 0 0 var(--pl-space-md);
    font-size: var(--pl-font-size-xs);
    font-weight: var(--pl-font-weight-semibold);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--pl-color-text-muted);
  }
  .cms-panel-index-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
  }
  .cms-panel-index-description {
    font-size: var(--pl-font-size-xs);
    font-weight: var(--pl-font-weight-normal);
    color: var(--pl-color-text-muted);
  }
  .cms-panel-index-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
    gap: var(--pl-space-md);
  }
  .cms-panel-index-link {
    display: flex;
    align-items: center;
    gap: var(--pl-space-md);
    padding: var(--pl-space-lg);
    background: var(--pl-color-surface);
    border: 1px solid var(--pl-color-border);
    border-radius: var(--pl-radius-lg);
    color: var(--pl-color-text);
    text-decoration: none;
    font-weight: var(--pl-font-weight-medium);
    transition: border-color var(--pl-transition-fast), box-shadow var(--pl-transition-fast);
  }
  .cms-panel-index-link:hover {
    border-color: var(--pl-color-primary);
    box-shadow: var(--pl-shadow-sm);
  }
  .cms-panel-index-link pl-icon {
    color: var(--pl-color-primary);
  }
  /* ===== Record tabs (edit form + record-scoped subpages) ===== */
  .cms-record-tabs {
    width: 100%;
    margin-bottom: var(--pl-space-lg);
  }
  /* pl-scroll-nav's body owns the flex row, the scroller and the rule now.
     padding-block-end absorbs the anchors' -1px margin so the body does not gain a
     VERTICAL scrollbar; padding-inline keeps the 2rem arrows off the end tabs. */
  .cms-record-tabs pl-scroll-nav {
    --pl-scroll-nav-align: flex-end;
    --pl-scroll-nav-gap: var(--pl-space-xs);
    --pl-scroll-nav-padding-block-end: 1px;
    --pl-scroll-nav-padding-inline: 2.25rem;
    --pl-scroll-nav-border-bottom: 1px solid var(--pl-color-border);
  }
  /* The "More" trigger wears the tab shape: one rule, so a tab and the
     disclosure that holds the rest of the tabs are the same object visually. */
  .cms-record-tabs a,
  .cms-record-tabs .cms-record-tabs-more-trigger {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-xs);
    padding: var(--pl-space-sm) var(--pl-space-md);
    margin-bottom: -1px;
    border: 1px solid transparent;
    border-bottom: 2px solid transparent;
    border-radius: var(--pl-radius-md) var(--pl-radius-md) 0 0;
    color: var(--pl-color-text-muted);
    text-decoration: none;
    font-size: var(--pl-font-size-sm);
    font-weight: var(--pl-font-weight-medium);
    white-space: nowrap;
    /* No flex: 0 0 auto here -- pl-scroll-nav owns that rule (the collision fix),
       and it is pinned by a discriminating plumage test; a consumer copy would
       only mask a regression of the contract. */
    transition: color var(--pl-transition-fast);
  }
  .cms-record-tabs a:hover,
  .cms-record-tabs .cms-record-tabs-more-trigger:hover {
    color: var(--pl-color-text);
  }
  .cms-record-tabs a[data-active],
  .cms-record-tabs .cms-record-tabs-more-trigger[data-active] {
    color: var(--pl-color-primary);
    border-bottom-color: var(--pl-color-primary);
  }
  /* The trigger's native button sheds the UA chrome the tab rule does not cover. */
  .cms-record-tabs .cms-record-tabs-more-trigger {
    background: none;
    cursor: pointer;
    font-family: inherit;
  }
  /* ===== Resource form ===== */
  .cms-record-toolbar {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
  }
  .cms-form-layout {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-lg);
  }
  .cms-form-errors {
    list-style: none;
    margin: 0;
    padding: var(--pl-space-md) var(--pl-space-lg);
    background: var(--pl-color-destructive-tint);
    border: 1px solid var(--pl-color-destructive);
    border-radius: var(--pl-radius-md);
    color: var(--pl-color-destructive);
    font-size: var(--pl-font-size-sm);
  }
  .cms-form-actions {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
    padding-top: var(--pl-space-md);
    border-top: 1px solid var(--pl-color-border-muted);
  }
  .cms-delete-form {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: var(--pl-space-md);
    margin-top: var(--pl-space-xl);
    padding-top: var(--pl-space-lg);
    border-top: 1px solid var(--pl-color-border);
  }
  /* Why the record's delete is dead (Resource.deleteUnavailableReason): on screen
     beside the button, because a disabled button cannot be focused and a tooltip
     alone would announce it to nobody. */
  .cms-delete-unavailable {
    margin: 0;
    color: var(--pl-color-text-muted);
    font-size: var(--pl-font-size-sm);
  }
  /* A menu item is ONE flex row by plumage contract (the slotted affordance in
     plumage's _menu.scss sets display:flex on the slotted <a>/<button>), and that
     rule is unreachable from here -- the cms `patterns` layer sits BELOW plumage's
     `components`. So the item's TEXT is one flex item that stacks inside itself,
     exactly as pl-select-item wraps its label + subtitle. Without the wrapper the
     reason becomes a second COLUMN of the row, wrapping into a tall block that
     spans the whole item height beside the label. */
  .cms-menu-action-body {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: flex-start;
    min-width: 0;
    /* Bounds the popup: a long reason wraps instead of widening the whole menu. */
    max-width: 22rem;
  }
  /* The same fact inside a row/record menu item: a second line under the label. */
  .cms-menu-action-reason {
    display: block;
    color: var(--pl-color-text-muted);
    font-size: var(--pl-font-size-xs);
    white-space: normal;
  }
  /* ===== Resource list ===== */
  /* The list table is a card that owns its chrome: title, search seam,
     view switcher and primary actions live in the card header; the
     filter strip and bulk bar are rows inside the card; the table
     renders flush (pl-table has no outer border of its own). */
  .cms-list-card {
    background: var(--pl-color-surface);
    border: 1px solid var(--pl-color-border-muted);
    border-radius: var(--pl-radius-lg);
    /* The card is the size container the row-action register queries (the
       @container rules beside .cms-row-action-group): the register is a
       fact of how much room THIS card has, never of the viewport. */
    container: cms-list-card/inline-size;
  }
  /* Three header zones: identity left, the search seam as its own middle
     zone, the action cluster right (Create is the one primary element). */
  .cms-list-card-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--pl-space-md);
    padding: var(--pl-space-md) var(--pl-space-lg);
    border-bottom: 1px solid var(--pl-color-border-muted);
  }
  .cms-list-card-header h1 {
    font-size: var(--pl-font-size-xl);
    font-weight: var(--pl-font-weight-bold);
    letter-spacing: -0.02em;
    margin: 0;
  }
  .cms-list-card-heading {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
    min-width: 0;
  }
  /* The quiet record count beside the title: orientation, not a control. */
  .cms-list-total {
    font-size: var(--pl-font-size-sm);
    font-weight: var(--pl-font-weight-medium);
    color: var(--pl-color-text-muted);
    background: var(--pl-color-background);
    border: 1px solid var(--pl-color-border-muted);
    border-radius: var(--pl-radius-full);
    padding: 0 var(--pl-space-sm);
    line-height: 1.4rem;
  }
  .cms-list-card-tools {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--pl-space-sm);
    margin-left: auto;
  }
  /* The search seam: an input among buttons reads as clutter, so it gets its
     own zone with a bounded width instead of joining the button cluster. */
  .cms-list-card-search {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
    flex: 0 1 auto;
    margin-inline-start: var(--pl-space-md);
  }
  .cms-list-card-search cms-list-search {
    width: 16rem;
    max-width: 100%;
  }
  /* The saved-views switcher is QUIET chrome next to the real actions:
     borderless at rest, button-shaped on hover. Doubled class outranks the
     later .cms-toolbar-menu base metrics (same layer, source order). */
  .cms-toolbar-menu.cms-views-menu > summary {
    border-color: transparent;
    background: transparent;
    color: var(--pl-color-text-muted);
  }
  .cms-toolbar-menu.cms-views-menu > summary:hover {
    background: var(--pl-color-secondary);
    color: var(--pl-color-text);
  }
  /* ONE aligned strip: stacked-label quick filters on the left, the quiet
     advanced/chips/clear group on the right. flex-end keeps every control's
     bottom edge on one line however tall its label is. */
  .cms-list-card-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--pl-space-md);
    padding: var(--pl-space-sm) var(--pl-space-lg);
    border-bottom: 1px solid var(--pl-color-border-muted);
  }
  /* A quick filter is ONE compact pl-field. Its stack, label register and
     control heights are the COMPONENT's (pl-field/pl-input/pl-select
     size="sm"); a consumer rule could not reach them anyway -- `patterns`
     sits below `components`. What the page owns is the WIDTH: one figure for
     every quick filter, so the strip is a row of like controls instead of a
     ragged one. */
  .cms-toolbar-filter {
    inline-size: 11rem;
  }
  /* The strip's right side: chips, clear and the advanced disclosure share
     one quiet group, bottom-aligned with the filter controls so the toggle
     does not float. An OPEN builder takes the whole strip width. */
  .cms-filter-strip-tools {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: var(--pl-space-md);
    margin-inline-start: auto;
  }
  .cms-filter-strip-tools:has(.cms-advanced-filter[open]) {
    flex-basis: 100%;
    margin-inline-start: 0;
  }
  .cms-list-card-quick-add {
    margin: var(--pl-space-sm) var(--pl-space-lg);
    padding: var(--pl-space-sm) var(--pl-space-md);
    background: var(--pl-color-background);
    border: 1px dashed var(--pl-color-border);
    border-radius: var(--pl-radius-md);
  }
  .cms-quick-add-heading {
    display: flex;
    align-items: center;
    gap: var(--pl-space-xs);
    margin-bottom: var(--pl-space-xs);
    font-size: var(--pl-font-size-xs);
    font-weight: var(--pl-font-weight-medium);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--pl-color-text-muted);
  }
  .cms-list-bulk-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--pl-space-sm);
    padding: var(--pl-space-sm) var(--pl-space-lg);
    border-bottom: 1px solid var(--pl-color-border-muted);
  }
  /* The always-present footer strip: closes the card (keeps the last
     row's hover inside the radius) and orients: range summary left,
     prev/next right (disabled instead of hidden on a single page). */
  .cms-list-card-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--pl-space-md);
    padding: var(--pl-space-sm) var(--pl-space-lg);
    border-top: 1px solid var(--pl-color-border-muted);
  }
  .cms-list-count {
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
  }
  .cms-list-pager {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
  }
  .cms-list-card pl-empty-state {
    padding: var(--pl-space-xl);
  }
  /* The under-header filter row: the SAME compact controls as the card's
     filter bar (the partial declares size="sm"), on a quiet background. Only
     the column-width slot is the page's business. */
  .cms-filter-row .cms-filter-cell {
    padding: var(--pl-space-2xs) var(--pl-space-xs);
    background: var(--pl-color-surface);
    vertical-align: middle;
    /* The control fills the grid track below; the track's minmax floor is
       what keeps it usable, so the component's own minimum must not
       re-inflate the column (the old 9rem knob made every boolean/select
       column 9rem wide regardless of its data). */
    --pl-select-min-width: 100%;
  }
  /* The one column-width contract of the filter row: filter CHROME never
     defines a column's min-content, the column's own header and data do.
     A grid track of minmax(floor, auto) contributes only the floor to the
     cell's min-content -- a native text input otherwise demands its
     20-character default (~11.5rem) and inflates the column past its data,
     which is what pushed narrow-desktop list tables into horizontal
     overflow. The control stretches to whatever the column really got. */
  .cms-filter-cell-inner {
    display: grid;
    grid-template-columns: minmax(var(--cms-filter-floor, 6rem), auto);
  }
  .cms-filter-cell-inner > pl-input,
  .cms-filter-cell-inner > pl-select {
    width: 100%;
    min-width: 0;
  }
  .cms-filter-cell-inner {
    /* Two date inputs side by side need more room than one control. */
  }
  .cms-filter-cell-inner:has(.cms-filter-range) {
    --cms-filter-floor: 11rem;
  }
  .cms-filter-range {
    display: flex;
    gap: var(--pl-space-2xs);
  }
  .cms-filter-actions-cell {
    text-align: right;
    white-space: nowrap;
  }
  /* The chrome header cell: the actions label alone, right-aligned to match
     the row action groups. The flex lives on the inner div because the head
     cell's component-layer display:table-cell wins. */
  .cms-chrome-head {
    white-space: nowrap;
  }
  .cms-chrome-head .cms-chrome-head-inner {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--pl-space-sm);
  }
  /* The column picker is a QUIET gear in the card toolbar, like the views
     switcher beside it: borderless at rest, its panel anchored to the card's
     right edge so it still drops from the table's top-right corner. Doubled
     class outranks the later .cms-toolbar-menu base metrics (same layer,
     source order). */
  .cms-toolbar-menu.cms-columns-menu > summary {
    border-color: transparent;
    background: transparent;
    padding: var(--pl-space-xs);
    color: var(--pl-color-text-muted);
  }
  .cms-toolbar-menu.cms-columns-menu > summary:hover {
    background: var(--pl-color-secondary);
    color: var(--pl-color-text);
  }
  .cms-columns-menu > .cms-toolbar-panel {
    right: 0;
  }
  .cms-column-sort {
    color: inherit;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-xs);
  }
  .cms-column-sort:hover {
    color: var(--pl-color-primary);
  }
  /* The flex layout lives on the INNER wrapper: pl-table-cell's
     component-layer display:table-cell would outrank a patterns-layer
     display on the cell class itself. */
  .cms-row-action-group {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--pl-space-sm);
    white-space: nowrap;
  }
  /* The row-action REGISTER, a @container query off the list card's own
     inline size (the card declares `container` where it is styled above): a
     wide card shows the split lane plus the ordinary overflow menu, a card
     below the 48rem floor swaps both for ONE compact menu carrying the whole
     action set -- an opaque pinned lane of labelled buttons in a narrow
     scrollport otherwise occludes the data columns scrolling under it (the
     F9 measurement: a 288px lane in a 703px viewport hid a whole column at
     rest). The card, never the viewport: a 1024px window minus the admin
     sidebar leaves the card tablet-narrow, which a media query cannot see.
     All three wrappers are plain divs so these display toggles never fight a
     component-layer display on a plumage host, and the hidden menus are
     unreachable by construction -- their portalled popups only open through
     triggers the active register actually shows. */
  .cms-row-action-lane,
  .cms-row-action-more {
    display: contents;
  }
  .cms-row-action-compact {
    display: none;
  }
  @container cms-list-card (width < 48rem) {
    .cms-row-action-lane {
      display: none;
    }
    .cms-row-action-compact {
      display: contents;
    }
    /* The ordinary menu yields to the compact one only when the row HAS
       one (a row without lane actions keeps its ordinary menu at every
       width -- the compact register changes nothing it could improve). */
    .cms-row-action-group:has(.cms-row-action-compact) .cms-row-action-more {
      display: none;
    }
  }
  /* ===== Typed list cells ===== */
  .cms-color-swatch {
    display: inline-block;
    inline-size: 1rem;
    block-size: 1rem;
    margin-inline-end: var(--pl-space-xs);
    border: 1px solid var(--pl-color-border);
    border-radius: var(--pl-radius-sm);
    vertical-align: -0.15rem;
  }
  .cms-color-value {
    font-size: var(--pl-font-size-xs);
    color: var(--pl-color-text-muted);
  }
  .cms-url-cell {
    color: var(--pl-color-primary);
    text-decoration: none;
  }
  .cms-url-cell:hover {
    text-decoration: underline;
  }
  /* THE column-width clamp of the list table, on every data cell and every head
     (resource-list-row.hwk, resource-list.hwk). The table keeps its auto layout;
     what changes is a column's MIN-CONTENT: a block's min-content contribution is
     clamped by its max-width (css-sizing), so one unbreakable hostname or URL can
     no longer widen its column until the table overflows the card and its tail
     disappears under the pinned actions column ("Docker 9 minutes ago" reduced to
     a "9 m ag" sliver, a "Source" head cut to "Sour"). Whatever does not fit ends
     in an ellipsis instead of a mid-word cut; the head carries the full label on
     its title. The cap is generous on purpose: it only ever bites on values that
     would otherwise break the layout. */
  .cms-cell-clamp {
    max-width: var(--cms-cell-clamp, 24rem);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Two-line cell: the column's own value over its declared subtext source. */
  .cms-cell-composite {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
  }
  .cms-cell-subtext {
    font-size: var(--pl-font-size-xs);
    color: var(--pl-color-text-muted);
  }
  /* The copy chip is chrome: revealed on row hover, but ALWAYS reachable by
     keyboard and always visible where there is no hover. The reveal is
     plumage's `m.hover-reveal`, which keys the keyboard half on the ROW's
     :focus-within -- the hand-rolled version keyed it on the chip's own, so
     tabbing to the cell's link left the chip invisible. */
  pl-table-row .cms-cell-copy {
    opacity: 0;
    transition: opacity var(--pl-transition-fast);
  }
  pl-table-row:hover .cms-cell-copy, pl-table-row:focus-within .cms-cell-copy {
    opacity: 1;
  }
  @media (hover: none) {
    pl-table-row .cms-cell-copy {
      opacity: 1;
    }
  }
  .cms-cell-copy {
    margin-inline-start: var(--pl-space-xs);
  }
  .cms-pagination {
    display: flex;
    align-items: center;
    gap: var(--pl-space-md);
    margin-top: var(--pl-space-lg);
    justify-content: center;
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
  }
  .cms-bulk-counter {
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
  }
  /* ===== Dashboard ===== */
  .cms-dashboard-subtitle {
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
    margin: 0;
  }
  /* The banded record action row: inline buttons plus one quiet overflow.
     The carrier form for portalled menu submitters renders nothing itself. */
  .cms-record-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--pl-space-sm);
  }
  .cms-record-actions-form {
    display: none;
  }
  .cms-dashboard-grid {
    display: grid;
    /* the zn-widget-surface host reads this slot: its components-layer
       display declaration would otherwise beat this patterns-layer rule */
    --zn-widget-surface-display: grid;
    /* min(18rem, 100%): a track never exceeds its container, so a phone
       gets one full-width column instead of one that overflows it. */
    grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr));
    gap: var(--pl-space-lg);
    align-items: start;
    /* A bare button widget is a CONTROL, not a panel: it must not stretch
       into a full-width bar that reads as a page element. Grid placement,
       so the widget's own styling stays untouched. */
  }
  .cms-dashboard-grid > pl-button {
    justify-self: start;
  }
  .cms-dashboard-grid {
    /* The record action row is a BAR, not a card: squeezed into one 18rem
       column its overflow trigger wraps onto a second line, which is the
       hierarchy defect this band exists to fix. */
  }
  .cms-dashboard-grid > .cms-record-actions-widget {
    grid-column: 1/-1;
  }
  /* ===== Revisions / diff ===== */
  .cms-record-revisions-table,
  .cms-revision-diff-table {
    width: 100%;
  }
  .cms-record-revisions-actions {
    display: flex;
    align-items: center;
    gap: var(--pl-space-sm);
    /* The restore control is wrapped in a CSRF/POST <form>; strip its
       block flow so its button sits inline with the compare link and the
       current badge instead of dropping to its own line. */
  }
  .cms-record-revisions-actions > form {
    display: contents;
    margin: 0;
  }
  .cms-revision-compare-form {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: var(--pl-space-md);
    margin-bottom: var(--pl-space-lg);
  }
  .cms-revision-compare-form > pl-field {
    min-width: 12rem;
  }
  .cms-diff-before {
    background: var(--pl-color-destructive-tint);
  }
  .cms-diff-after {
    background: var(--pl-color-success-tint);
  }
  /* AIDEV-NOTE: the wrap policy lives HERE, not as an inline style attribute on the
     cell -- an inline style is unlayered and outranks every cascade layer, which is
     exactly the silent-override shape this file is built to avoid. Stored values keep
     their newlines (a diff that folds them lies about what changed) and long unbroken
     tokens (ids, urls) break anywhere rather than widening the table. */
  .cms-diff-value {
    padding: var(--pl-space-xs) var(--pl-space-sm);
    border-radius: var(--pl-radius-sm);
    font-family: var(--pl-font-family-mono, monospace);
    font-size: var(--pl-font-size-sm);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
  }
  /* ===== zenit-forms structural entries rendered inside CMS forms ===== */
  .zf-key-value-rows {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-sm);
  }
  .zf-key-value-row {
    display: grid;
    grid-template-columns: minmax(8rem, 1fr) 2fr;
    gap: var(--pl-space-sm);
    align-items: center;
  }
  .zf-key-value-key,
  .zf-key-value-value {
    color: var(--pl-color-text);
    font-size: var(--pl-font-size-sm);
  }
}
@layer patterns {
  .cms-toolbar-menu {
    position: relative;
    /* Same box metrics as a pl-button: the card-header toolbar reads as one
       row of same-height controls. A <summary> cannot BE a pl-button (the
       disclosure needs the native element), so this is the one place the
       metrics are restated rather than inherited. */
  }
  .cms-toolbar-menu > summary {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-xs);
    cursor: pointer;
    padding: var(--pl-space-xs) var(--pl-space-md);
    border: 1px solid var(--pl-color-border);
    border-radius: var(--pl-radius-md);
    background: var(--pl-color-surface);
    font-size: var(--pl-font-size-sm);
    font-weight: var(--pl-font-weight-medium);
    list-style: none;
    user-select: none;
  }
  .cms-toolbar-menu > summary:hover {
    background: var(--pl-color-secondary);
  }
  .cms-toolbar-menu > .cms-toolbar-panel {
    position: absolute;
    z-index: 30;
    min-width: 18rem;
    margin-top: var(--pl-space-xs);
    padding: var(--pl-space-md);
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-sm);
    background: var(--pl-color-surface);
    border: 1px solid var(--pl-color-border-muted);
    border-radius: var(--pl-radius-lg);
    box-shadow: var(--pl-shadow-lg);
  }
  .cms-view-list {
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-2xs);
  }
  .cms-view-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--pl-space-sm);
  }
  .cms-view-row[data-view-active] > .cms-view-link {
    font-weight: 600;
  }
  .cms-view-link {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-xs);
    color: var(--pl-color-text);
    text-decoration: none;
  }
  .cms-view-link:hover {
    text-decoration: underline;
  }
  .cms-view-save-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--pl-space-xs);
    padding-top: var(--pl-space-sm);
    border-top: 1px solid var(--pl-color-border-muted);
  }
  .cms-view-shared-option,
  .cms-column-picker-option {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-xs);
    font-size: var(--pl-font-size-sm);
  }
  .cms-view-default-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--pl-space-xs);
  }
  .cms-view-base-link {
    font-size: var(--pl-font-size-sm);
  }
  .cms-column-picker {
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-2xs);
    max-height: 18rem;
    overflow-y: auto;
  }
  .cms-column-picker-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--pl-space-sm);
  }
  .cms-column-picker-move {
    display: inline-flex;
    gap: var(--pl-space-2xs);
  }
  .cms-column-picker-move button {
    border: none;
    background: none;
    cursor: pointer;
    padding: var(--pl-space-2xs);
    color: var(--pl-color-text-muted);
  }
  .cms-column-picker-move button:disabled {
    opacity: 0.3;
    cursor: default;
  }
  .cms-filter-query {
    min-width: 20rem;
  }
  /* Alignment is the strip-tools group's job; the link only sets its type. */
  .cms-filter-clear {
    font-size: var(--pl-font-size-sm);
    padding-block: var(--pl-space-2xs);
  }
  .cms-filter-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--pl-space-xs);
  }
  .cms-filter-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-2xs);
    padding: var(--pl-space-2xs) var(--pl-space-sm);
    background: var(--pl-color-surface);
    border: 1px solid var(--pl-color-border-muted);
    border-radius: var(--pl-radius-full);
    font-size: var(--pl-font-size-sm);
  }
  .cms-filter-chip .cms-filter-chip-label {
    color: var(--pl-color-text-muted);
  }
  .cms-filter-chip .cms-filter-chip-remove {
    text-decoration: none;
    color: var(--pl-color-text-muted);
    padding-inline: var(--pl-space-2xs);
  }
  .cms-filter-chip .cms-filter-chip-remove:hover {
    color: var(--pl-color-destructive);
  }
  .cms-advanced-filter {
    /* Inside the card's filter strip; the open builder takes the full
       strip width so the flex row does not squeeze it. */
  }
  .cms-advanced-filter[open] {
    flex-basis: 100%;
  }
  .cms-advanced-filter > summary {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-xs);
    padding: var(--pl-space-2xs) var(--pl-space-sm);
    border-radius: var(--pl-radius-md);
    cursor: pointer;
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
    user-select: none;
  }
  .cms-advanced-filter > summary:hover {
    background: var(--pl-color-secondary);
    color: var(--pl-color-text);
  }
  .cms-advanced-filter .cms-advanced-filter-form {
    margin-top: var(--pl-space-sm);
    padding: var(--pl-space-md);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--pl-space-md);
    background: var(--pl-color-surface);
    border: 1px solid var(--pl-color-border-muted);
    border-radius: var(--pl-radius-lg);
  }
}
@layer patterns {
  .cms-settings-page {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-lg);
  }
  .cms-settings-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--pl-space-lg);
  }
  .cms-settings-header h1 {
    margin: 0;
    white-space: nowrap;
  }
  .cms-settings-header .cms-settings-search {
    min-width: 20rem;
  }
  .cms-settings-body {
    display: grid;
    grid-template-columns: 14rem minmax(0, 1fr);
    gap: var(--pl-space-xl);
    align-items: start;
  }
  .cms-settings-nav {
    position: sticky;
    /* The DOCUMENT is the scrollport (plumage's app shell: pl-app-content is not a
       scroll container, so a sticky descendant pins against the viewport), and top: 0
       is the top of the viewport -- which the sticky app header already occupies.
       Everything that sticks or scrolls into view below the band offsets by its
       declared height; see .cms-settings-section for the anchor half. */
    top: calc(var(--pl-app-header-height, 3.5rem) + var(--pl-space-lg));
    display: flex;
    flex-direction: column;
    gap: 2px;
    /* dvh, like the shell's own sidebar: the nav must fit the viewport the browser
       actually shows, and a vh unit ignores a mobile browser's dynamic toolbar. */
    max-height: calc(100dvh - var(--pl-app-header-height, 3.5rem) - 6rem);
    overflow-y: auto;
  }
  .cms-settings-nav .cms-settings-nav-item {
    display: flex;
    align-items: center;
    gap: var(--pl-space-xs);
    padding: var(--pl-space-xs) var(--pl-space-sm);
    border-radius: var(--pl-radius-md);
    color: var(--pl-color-text-muted);
    font-size: var(--pl-font-size-sm);
    text-decoration: none;
  }
  .cms-settings-nav .cms-settings-nav-item:hover {
    background: var(--pl-color-secondary);
    color: var(--pl-color-text);
  }
  .cms-settings-nav .cms-settings-nav-item {
    /* The active nav row is DECLARED by the page (attr:data-active bound to
       the clicked anchor); .active is kept for the :target deep-link path. */
  }
  .cms-settings-nav .cms-settings-nav-item.active, .cms-settings-nav .cms-settings-nav-item[data-active] {
    background: var(--pl-color-secondary);
    color: var(--pl-color-text);
    font-weight: var(--pl-font-weight-medium);
  }
  .cms-settings-nav .cms-settings-nav-item[data-depth="1"] {
    padding-left: calc(var(--pl-space-sm) + var(--pl-space-md));
  }
  .cms-settings-nav .cms-settings-nav-item[data-depth="2"] {
    padding-left: calc(var(--pl-space-sm) + 2 * var(--pl-space-md));
  }
  .cms-settings-nav .cms-settings-nav-item[data-depth="3"] {
    padding-left: calc(var(--pl-space-sm) + 3 * var(--pl-space-md));
  }
  .cms-settings-main {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-xl);
    min-width: 0;
    max-width: 52rem;
    scroll-behavior: smooth;
  }
  .cms-settings-section {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-md);
    /* The group links are in-page anchors, the document is the scrollport and the
       app header is STICKY against the viewport: without the band's own height in
       the margin the browser scrolls the section's top edge to the top of the
       viewport, i.e. underneath the header, and the operator lands mid-section
       with the group name hidden. The height is plumage's declared token, never a
       number copied from the component. */
    scroll-margin-top: calc(var(--pl-app-header-height, 3.5rem) + var(--pl-space-lg));
  }
  .cms-settings-section .cms-settings-section-header h2 {
    margin: 0;
    font-size: var(--pl-font-size-lg);
  }
  .cms-settings-section[data-depth="1"] .cms-settings-section-header h2, .cms-settings-section[data-depth="2"] .cms-settings-section-header h2 {
    font-size: var(--pl-font-size-md);
  }
  .cms-settings-section .cms-settings-section-description {
    margin: var(--pl-space-xs) 0 0;
    color: var(--pl-color-text-muted);
    font-size: var(--pl-font-size-sm);
  }
  .cms-setting {
    display: flex;
    flex-direction: column;
    gap: var(--pl-space-xs);
    padding: var(--pl-space-sm) var(--pl-space-md);
    border-left: 2px solid transparent;
    border-radius: var(--pl-radius-sm);
  }
  .cms-setting[data-configured] {
    border-left-color: var(--pl-color-primary);
  }
  .cms-setting[data-overridden] {
    opacity: 0.75;
  }
  .cms-setting-meta {
    display: flex;
    align-items: center;
    gap: var(--pl-space-md);
    font-size: var(--pl-font-size-sm);
    color: var(--pl-color-text-muted);
  }
  .cms-setting-meta code {
    font-size: var(--pl-font-size-sm);
  }
  .cms-setting-note {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-xs);
  }
  .cms-setting-note-restart {
    color: var(--pl-color-warning, var(--pl-color-text-muted));
  }
  /* pl-checkbox + pl-label, NOT a link: the reset is a destructive STAGED
     toggle, so it must read as armed-until-save. pl-checkbox brings the
     visible box, its own :focus-within ring and the disabled register, so
     this row deliberately adds NO ring of its own (the old label-wrapping-a-
     hidden-input shape needed one because its focus target was invisible). */
  .cms-setting-reset {
    display: inline-flex;
    align-items: center;
    gap: var(--pl-space-xs);
    font-size: var(--pl-font-size-sm);
    /* AIDEV-NOTE: colour the label through the TOKEN, never with a
       `.cms-setting-reset pl-label { color: ... }` rule. pl-label sets
       `color: var(--pl-field-label-color, var(--pl-color-text))` from
       plumage's `components` layer; this file writes into `patterns`,
       which plumage declares LOWER (reset, defaults, patterns, components,
       themes, utilities, overrides). A direct colour rule here therefore
       loses the cascade silently -- no warning, no error, just the wrong
       colour. Custom-property inheritance sidesteps layer priority
       entirely, which is why the armed state below re-points the same
       token instead of restating the selector. Do not "simplify" this. */
    --pl-field-label-color: var(--pl-color-primary);
  }
  .cms-setting-reset pl-label {
    cursor: pointer;
  }
  .cms-setting-reset .cms-setting-reset-staged {
    display: none;
  }
  .cms-setting-reset {
    /* The setting's own label is present in the accessibility tree only: it is what
       makes each row's reset control distinguishable by name. It carries plumage's
       `.pl-sr-only` utility (the sanctioned spelling) rather than a hand-rolled clip
       recipe -- NOT display:none, which would drop it from the tree and put every row
       back to the identical "Reset to default". `.cms-setting-reset-name` survives as
       the test hook only and deliberately has no rules of its own. */
    /* Armed: the box is ticked, the wording flips to the pending phrasing
       and the color warns. Staged reads as PENDING (it applies on save),
       never as done. */
  }
  .cms-setting-reset:has(pl-checkbox[checked]) {
    --pl-field-label-color: var(--pl-color-warning);
  }
  .cms-setting-reset:has(pl-checkbox[checked]) .cms-setting-reset-idle {
    display: none;
  }
  .cms-setting-reset:has(pl-checkbox[checked]) .cms-setting-reset-staged {
    display: inline;
  }
  .cms-settings-actions {
    position: sticky;
    bottom: 0;
    padding: var(--pl-space-md) 0;
    background: var(--pl-color-background);
    border-top: 1px solid var(--pl-color-border-muted);
  }
  @media (max-width: 60rem) {
    .cms-settings-body {
      grid-template-columns: 1fr;
    }
    .cms-settings-nav {
      position: static;
      max-height: none;
      flex-direction: row;
      flex-wrap: wrap;
    }
  }
}
@layer patterns {
  /* Settings header + search, appended as its own block so the layout work in
     the sections above stays untouched.

     The heading and the search used to be two unshrinkable flex items on one
     line (`white-space: nowrap` on the h1, a `min-width` on the input), which
     OVERLAPPED as soon as the pair outgrew the row -- measured at 1440px. The
     row now wraps: the heading may break, the search keeps a usable floor and
     drops to its own line rather than colliding. */
  .cms-settings-header {
    flex-wrap: wrap;
    align-items: flex-end;
    row-gap: var(--pl-space-md);
  }
  .cms-settings-header h1 {
    flex: 1 1 auto;
    min-width: 0;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .cms-settings-search-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--pl-space-sm);
    /* Never narrower than a usable search box, never wider than it needs;
       `flex-basis` is what lets the whole form wrap below the heading. */
    flex: 1 1 20rem;
    min-width: 16rem;
    max-width: 32rem;
  }
  .cms-settings-search-form .cms-settings-search {
    flex: 1 1 12rem;
    min-width: 0;
  }
  /* "N settings match" / "No settings match ..." -- a live region, so it takes
     the full row under the input instead of competing with it for width. */
  .cms-settings-search-status {
    flex: 1 0 100%;
    margin: 0;
    color: var(--pl-color-text-muted);
    font-size: var(--pl-font-size-sm);
  }
}
@layer patterns {
  /* The list table's scroll box is a pl-scroll-area, so the SCROLLING is the
     component's (hand-written scroll-container CSS in a consumer is exactly what
     the design system forbids) and this block only says how TALL it may be.

     Bounding the block axis is what makes a sticky header mean anything: a sticky
     element sticks to its nearest SCROLLPORT, and an unbounded box grows with its
     content, so the header used to scroll off the page with everything else. The
     floor keeps a short viewport from collapsing the table to a couple of rows.
     The knob is the component's published API, not a max-height on the host --
     pl-scroll-area applies it to the scrollport itself, where it can actually
     scroll instead of clip. */
  .cms-table-scroll {
    --pl-scroll-area-max-block-size: max(24rem, calc(100vh - 16rem));
  }
  /* The actions column is STICKY at the trailing edge of that scroll box: a
     table wider than the card scrolls its data columns, never its verbs. This
     is what keeps Admit/Edit/the row menu reachable on the wide lists (hosts,
     certificates) -- and on a phone, where the whole table is wider than the
     viewport.

     The RULES are not here: this is plumage's pinned-column contract, and the
     three cells opt in by stamping data-sticky="end" (resource-list.hwk for
     the chrome head and the filter apply cell, resource-list-row.hwk for the
     row's action cell). Component-owned, for the reason the alignment contract
     beside it gives: a patterns-layer copy cannot set the z-index the pinned
     header cell needs, because patterns sits BELOW components. */
  /* An EMPTY cell says so. Quiet enough to read as absence rather than as a
     value, but present enough that a blank column cannot be mistaken for a
     broken one. The marker itself is picked server-side per cell
     (AbsentCellState) and painted by the zenitcms:cell/absent partial. */
  .cms-cell-absent {
    color: var(--pl-color-text-subtle);
    font-variant-numeric: tabular-nums;
  }
  /* The header search seam is a pl-field now (it owns the submit button and the
     assistive-technology hint), so the field must not stretch the header row. */
  .cms-list-card-search .cms-list-search-field {
    flex: 1 1 auto;
    min-width: 0;
  }
}

/*# sourceMappingURL=cms.css.map */
