/* Services Registry — corporate UI */

.srv {
    /* ── Fonts: theme-first, Bootstrap fallback ── */
    --srv-font:       var(--font-sans, var(--bs-font-sans-serif, system-ui, sans-serif));
    --srv-font-mono:  var(--font-mono, var(--bs-font-monospace, monospace));

    /* ── Color tokens: all Bootstrap/theme-driven ── */
    --srv-border:         var(--bs-border-color);
    --srv-border-subtle:  var(--bs-border-color-translucent);
    --srv-text:           var(--bs-body-color);
    --srv-text-muted:     var(--bs-secondary-color);
    --srv-surface:        var(--bs-tertiary-bg);
    --srv-accent:         var(--bs-primary);
    --srv-accent-hover:   var(--bs-primary-text-emphasis, var(--bs-primary));

    /* ── Badge fg + border — Bootstrap semantic tokens ── */
    --srv-badge-active-fg:   var(--bs-success-text-emphasis);
    --srv-badge-active-bd:   var(--bs-success-border-subtle);
    --srv-badge-planned-fg:  var(--bs-warning-text-emphasis);
    --srv-badge-planned-bd:  var(--bs-warning-border-subtle);
    --srv-badge-retired-fg:  var(--bs-secondary-color);
    --srv-badge-retired-bd:  var(--bs-border-color);
    --srv-badge-sub-fg:      var(--bs-primary-text-emphasis);
    --srv-badge-sub-bd:      var(--bs-primary-border-subtle);
    --srv-badge-neutral-fg:  var(--bs-secondary-color);
    --srv-badge-neutral-bd:  var(--bs-border-color);

    /* ── Shape ── */
    --srv-radius: 8px;

    font-family: var(--srv-font);
    color: var(--srv-text);
    min-height: 100%;
    padding: .75rem 1rem;
}

/* service-platform.html's root is .app-nodeone-page, not .srv — that
   class only wraps the sibling service-app.html/services.html/
   service.html pages. Every var(--srv-*) reference used inside
   service-platform.html (Classic mode's cat-pills, product cards,
   badges, AND the whole App View carousel — dock, arrows, progress
   bar) was silently resolving to nothing here: no fallback was given,
   so per spec the declaration goes invalid and the property resets to
   its initial value (e.g. a "border: 1px solid var(--srv-border)"
   with an undefined --srv-border computes to no border at all).
   Redeclaring just the custom properties (none of .srv's other
   side effects like padding/font, which would alter the whole app
   shell's layout) fixes borders/accents across both. */
.app-nodeone-page {
    --srv-border:         var(--bs-border-color);
    --srv-border-subtle:  var(--bs-border-color-translucent);
    --srv-text:           var(--bs-body-color);
    --srv-text-muted:     var(--bs-secondary-color);
    --srv-surface:        var(--bs-tertiary-bg);
    --srv-accent:         var(--bs-primary);
    --srv-accent-hover:   var(--bs-primary-text-emphasis, var(--bs-primary));
}

/* ── Toolbar ──────────────────────────────────── */
.srv-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: .5rem 0 1rem;
    border-bottom: 1px solid var(--srv-border);
    /*margin-bottom: 1rem;*/
}

.srv-toolbar-left {
    display: flex;
    flex-direction: column;
    gap: .1rem;
}

.srv-toolbar-eyebrow {
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--srv-text-muted);
}

.srv-toolbar-title {
    font-size: var(--text-lg);
    font-weight: 700;
    color: var(--srv-text);
    letter-spacing: -.01em;
}

.srv-toolbar-right {
    display: flex;
    gap: .5rem;
    align-items: center;
}

/* ── Alerts ───────────────────────────────────── */
.srv-alert {
    display: flex;
    align-items: center;
    padding: .6rem 1rem;
    border-radius: var(--srv-radius);
    font-size: var(--text-sm);
    margin-bottom: .75rem;
    border: 1px solid transparent;
}

.srv-alert--success { color: var(--bs-success-text-emphasis); border-color: var(--bs-success-border-subtle); }
.srv-alert--danger  { color: var(--bs-danger-text-emphasis);  border-color: var(--bs-danger-border-subtle); }

/* ── Search bar ───────────────────────────────── */
.srv-search-bar {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1rem;
    flex-wrap: wrap;
}

.srv-search-field {
    position: relative;
    flex: 1;
    min-width: 140px;
}

.srv-search-field--lg  { flex: 2; min-width: 200px; }
.srv-search-field--sm  { flex: .8; min-width: 100px; }

.srv-search-icon {
    position: absolute;
    left: .6rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--srv-text-muted);
    font-size: var(--text-sm);
    pointer-events: none;
}

.srv-search-field .srv-input { padding-left: 1.8rem; }

/* ── Buttons ──────────────────────────────────── */
.srv-btn {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .38rem .38rem;
    border-radius: 6px;
    font-size: var(--text-md);
    font-weight: 500;
    cursor: pointer;
    border: 1px solid transparent;
    background: transparent;
    transition: background .12s, border-color .12s, color .12s;
    white-space: nowrap;
    text-decoration: none;
}

.srv-btn--primary {
    background: var(--srv-accent);
    color: var(--bs-white, #fff);
    border-color: var(--srv-accent);
}

.srv-btn--primary:hover {
    background: var(--srv-accent-hover);
    border-color: var(--srv-accent-hover);
}

.srv-btn--ghost {
    color: var(--srv-text-muted);
    /*border-color: var(--srv-border);*/
}

.srv-btn--ghost:hover {
    color: var(--srv-text);
    border-color: var(--srv-text-muted);
}

.srv-btn--outline {
    color: var(--srv-accent);
    border-color: var(--srv-accent);
}

.srv-btn--outline:hover {
    background: var(--srv-accent);
    color: var(--bs-white, #fff);
}

/* ── Cards ────────────────────────────────────── */
.srv-card {
    border: 1px solid var(--srv-border);
    border-radius: var(--srv-radius);
    margin-bottom: 1rem;
    overflow: hidden;
}

.srv-card-header {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .7rem 1rem;
    border-bottom: 1px solid var(--srv-border);
}

.srv-card-label {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--srv-text);
    letter-spacing: .01em;
}

.srv-card-eyebrow {
    font-size: var(--text-xs);
    font-weight: 500;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--srv-text-muted);
}

.srv-count-pill {
    margin-left: auto;
    border: 1px solid var(--srv-border);
    color: var(--srv-text-muted);
    font-size: var(--text-xs);
    font-weight: 600;
    border-radius: 999px;
    padding: .1rem .55rem;
    min-width: 1.4rem;
    text-align: center;
}

/* ── Tables ───────────────────────────────────── */
.srv-table-wrap { overflow-x: auto; }

.srv-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
}

.srv-table thead th {
    padding: .55rem 1rem;
    text-align: left;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--srv-text-muted);
    border-bottom: 1px solid var(--srv-border);
    white-space: nowrap;
}

.srv-table tbody td {
    padding: .55rem 1rem;
    border-bottom: 1px solid var(--srv-border-subtle);
    color: var(--srv-text);
    vertical-align: middle;
}

.srv-row {
    cursor: pointer;
    transition: background .1s;
}

.srv-row:hover { background: var(--srv-surface); }

.srv-table tbody tr:last-child td { border-bottom: none; }

.srv-cell-name  { font-weight: 500; }

.srv-cell-mono {
    font-family: var(--srv-font-mono);
    font-size: var(--text-xs);
    color: var(--srv-text-muted);
}

.srv-empty {
    text-align: center;
    padding: 2rem !important;
    color: var(--srv-text-muted);
    font-size: var(--text-sm);
}

.srv-row-action {
    background: none;
    border: 1px solid var(--srv-border);
    border-radius: 4px;
    padding: .15rem .45rem;
    color: var(--srv-text-muted);
    cursor: pointer;
    font-size: var(--text-sm);
    transition: background .1s, color .1s, border-color .1s;
}

.srv-row-action:hover {
    background: var(--srv-accent);
    color: var(--bs-white, #fff);
    border-color: var(--srv-accent);
}

/* ── Badges ───────────────────────────────────── */
.srv-badge {
    display: inline-flex;
    align-items: center;
    border-radius: 999px;
    font-size: var(--text-md);
    font-weight: 600;
    padding: .15rem .5rem;
    letter-spacing: .03em;
    border: 1px solid var(--srv-badge-neutral-bd);
    color: var(--srv-badge-neutral-fg);
    white-space: nowrap;
}

.srv-badge--active  { border-color: var(--srv-badge-active-bd);  color: var(--srv-badge-active-fg); }
.srv-badge--planned { border-color: var(--srv-badge-planned-bd); color: var(--srv-badge-planned-fg); }
.srv-badge--retired { border-color: var(--srv-badge-retired-bd); color: var(--srv-badge-retired-fg); }
.srv-badge--sub     { border-color: var(--srv-badge-sub-bd);     color: var(--srv-badge-sub-fg); }
.srv-badge--neutral { border-color: var(--srv-badge-neutral-bd); color: var(--srv-badge-neutral-fg); }
.srv-badge--link {
    border-color: var(--srv-badge-sub-bd);
    color: var(--srv-badge-sub-fg);
    cursor: pointer;
}

.srv-badge--link:hover {
    border-color: currentColor;
}

/* ── Form ─────────────────────────────────────── */
.srv-form {
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: .25rem;
}

.srv-form-section {
    padding: .75rem 0;
    border-bottom: 1px solid var(--srv-border-subtle);
}

.srv-form-section:last-of-type { border-bottom: none; }

.srv-form-eyebrow {
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--srv-text-muted);
    margin-bottom: .6rem;
}

.srv-form-row {
    display: flex;
    gap: .75rem;
    flex-wrap: wrap;
    align-items: flex-start;
}

.srv-field {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    flex: 1;
    min-width: 160px;
}

.srv-field--lg   { flex: 2; min-width: 200px; }
.srv-field--full { flex: 100; }

.srv-field-label {
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--srv-text-muted);
    letter-spacing: .03em;
}

.srv-field-checks {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    padding-top: 1.4rem;
    min-width: 120px;
}

.srv-toggle-label,
.srv-check-label {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: var(--text-sm);
    color: var(--srv-text);
    cursor: pointer;
    white-space: nowrap;
}

/* ── Inputs ───────────────────────────────────── */
.srv-input {
    width: 100%;
    padding: .4rem .65rem;
    border: 1px solid var(--srv-border);
    border-radius: 6px;
    font-size: var(--text-sm);
    color: var(--srv-text);
    background: transparent;
    outline: none;
    transition: border-color .15s, box-shadow .15s;
    font-family: var(--srv-font);
    box-sizing: border-box;
}

.srv-input:focus {
    border-color: var(--srv-accent);
    box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb, 37 99 235) / .12);
}

.srv-input::placeholder { color: var(--srv-text-muted); opacity: .6; }

.srv-input--mono {
    font-family: var(--srv-font-mono);
    font-size: var(--text-xs);
}

/* ── Form footer ──────────────────────────────── */
.srv-form-footer {
    display: flex;
    justify-content: flex-end;
    gap: .5rem;
    padding-top: .75rem;
}

/* ── View tabs ────────────────────────────────── */
.srv-view-tabs {
    display: flex;
    gap: 0;
    border-bottom: 1px solid var(--srv-border);
    /* horizontal scroll instead of wrapping/squeezing when content
       overflows — needed once the App View toggle joins this row on
       narrow screens (see .srv-view-tabs__toggle) */
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

.srv-view-tabs::-webkit-scrollbar { display: none; }

/* When the row also holds the App View toggle, let it scroll
   horizontally instead of shrinking every tab to fit — the mobile
   flex:1 stretch further down is for the plain-tabs case elsewhere
   (service-app.html, services.html) that don't have a 4th item. */
.srv-view-tabs:has(.srv-view-tabs__toggle) .srv-view-tab {
    flex: 0 0 auto;
}

.srv-view-tabs__toggle {
    flex-shrink: 0;
    white-space: nowrap;
    /* pushes the toggle to the far edge when there's room (desktop);
       auto margins resolve to 0 once the row overflows and scrolls,
       so it sits right after Topics on narrow screens instead of
       leaving a large gap */
    margin-left: auto;
}

.srv-view-tab {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .4rem .9rem;
    font-size: var(--text-md);
    font-weight: 500;
    cursor: pointer;
    border: none;
    border-bottom: 2px solid transparent;
    background: transparent;
    color: var(--srv-text-muted);
    color: var(--bs-gray-100);
    transition: color .12s, border-color .12s;
    white-space: nowrap;
    margin-bottom: -1px;
}

.srv-view-tab:hover { color: var(--srv-text); }

.srv-view-tab--active {
    color: var(--srv-accent);
    border-bottom-color: var(--srv-accent);
    font-weight: 600;
}

/* ── Category filter pills ────────────────────── */
.srv-cat-pills {
    display: flex;
    gap: .5rem;
    flex-wrap: nowrap;
    overflow-x: auto;
    /*margin-bottom: 1rem;*/
    /*margin: 1rem;*/
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.srv-cat-pills::-webkit-scrollbar { display: none; }

/* App View catalog toolbar only (Classic layout's .srv-cat-pills is
   never added/removed, so these never trigger there): fades in/out as
   it swaps with .srv-cat-search-bar above. */
.srv-cat-pills.ng-enter {
    opacity: 0;
    transition: opacity .18s ease;
}
.srv-cat-pills.ng-enter-active {
    opacity: 1;
}
.srv-cat-pills.ng-leave {
    opacity: 1;
    transition: opacity .12s ease;
}
.srv-cat-pills.ng-leave-active {
    opacity: 0;
}

.srv-cat-pill {
    display: inline-flex;
    align-items: center;
    gap: .9rem;
    padding: .28rem .7rem;
    border-radius: 999px;
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
    border: 1px solid var(--srv-border);
    border: 1px solid var(--bs-body-bg-100);
    background: transparent;
    color: var(--srv-text-muted);
    transition: color .1s, border-color .1s;
    flex-shrink: 0;
    white-space: nowrap;
}

.srv-cat-pill:hover { color: var(--srv-text); border-color: var(--srv-text-muted); }

.srv-cat-pill--active {
    border-color: var(--srv-accent);
    color: var(--srv-accent);
    font-weight: 600;
}

/* ── Pills + search + (Data Sources slide) layout/multi-select ── */
/* No sticky/fixed positioning needed: catalog-toolbar-partial.html is now
   included as a sibling of .wv-slides-viewport (in service-platform.html),
   not nested inside .wv-slide — .wv-slide has its own transform (scale
   in/out on activate) which breaks position:sticky/fixed for anything
   nested inside it. Sitting outside .wv-slides-viewport entirely, in
   .wv-slides-shell's flex column layout, this bar is simply never part
   of the scrolling area, so it stays visible with zero position tricks —
   same reasoning .wv-dock already relies on for its own fixed bottom nav. */
.srv-cat-bar {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: .5rem;
    margin-bottom: .5rem;
}

/* Icon-only pill (search toggle, clear) */
.srv-cat-pill--icon {
    padding: .28rem .5rem;
    min-width: unset;
}

/* Filter pill — wider to fit type label when active */
.srv-cat-pill--filter {
    gap: .35rem;
    transition: color .3s ease, border-color .3s ease, background .3s ease;
}

/* Funnel icon rotates softly when filter is active */
.srv-cat-pill--filter .bi {
    transition: transform .35s cubic-bezier(.34,1.3,.64,1), color .3s ease;
    display: inline-block;
}
.srv-cat-pill--filter.srv-cat-pill--active .bi {
    transform: rotate(-30deg);
}

/* Filter label fades in */
.srv-cat-pill--filter span {
    overflow: hidden;
    max-width: 0;
    opacity: 0;
    transition: max-width .3s ease, opacity .25s ease;
    white-space: nowrap;
}
.srv-cat-pill--filter.srv-cat-pill--active span {
    max-width: 120px;
    opacity: 1;
}

/* Clear pill — muted tone, fades in via ng-if */
.srv-cat-pill--clear {
    padding: .28rem .4rem;
    color: var(--srv-text-muted);
}
.srv-cat-pill--clear.ng-enter {
    opacity: 0;
    transform: scale(.7);
    transition: opacity .2s ease, transform .2s ease;
}
.srv-cat-pill--clear.ng-enter-active {
    opacity: 1;
    transform: scale(1);
}
.srv-cat-pill--clear.ng-leave {
    opacity: 1;
    transform: scale(1);
    transition: opacity .15s ease, transform .15s ease;
}
.srv-cat-pill--clear.ng-leave-active {
    opacity: 0;
    transform: scale(.7);
}

/* ── Category pills — staggered fade + slide on ng-show toggle ── */
.srv-cat-pills .srv-cat-pill.ng-animate-stagger {
    transition-delay: 35ms;
    transition-duration: 0s;
}
.srv-cat-pills .srv-cat-pill.ng-hide-remove {
    display: inline-flex !important;
    opacity: 0;
    transform: translateY(-4px) scale(.95);
}
.srv-cat-pills .srv-cat-pill.ng-hide-remove-active {
    opacity: 1;
    transform: translateY(0) scale(1);
    transition: opacity .28s ease, transform .28s cubic-bezier(.34,1.2,.64,1);
}
.srv-cat-pills .srv-cat-pill.ng-hide-add {
    opacity: 1;
    transform: translateY(0) scale(1);
}
.srv-cat-pills .srv-cat-pill.ng-hide-add-active {
    opacity: 0;
    transform: translateY(-4px) scale(.95);
    transition: opacity .18s ease, transform .18s ease;
}

/* ── Search bar — slide-down fade on ng-show toggle ── */
.srv-cat-search.ng-hide-remove {
    display: flex !important;
    opacity: 0;
    transform: translateY(-8px);
}
.srv-cat-search.ng-hide-remove-active {
    opacity: 1;
    transform: translateY(0);
    transition: opacity .3s ease, transform .3s cubic-bezier(.34,1.1,.64,1);
}
.srv-cat-search.ng-hide-add {
    opacity: 1;
    transform: translateY(0);
}
.srv-cat-search.ng-hide-add-active {
    opacity: 0;
    transform: translateY(-8px);
    transition: opacity .22s ease, transform .22s ease;
}

/* Search takes remaining space beside pills on desktop, but is capped and
   auto-centered within that space — wide and centered like a Google search
   bar rather than squeezed into a small toolbar slot. */
.srv-cat-search {
    flex: 1;
    min-width: 140px;
    max-width: 560px;
    margin: .21rem auto;
}

/* Mobile: Classic layout's own search bar (service-platform.html, uses
   ng-show, not the App View one below) still stacks underneath its
   .srv-cat-pills on narrow screens — no room to share a row there.
   767px matches this app's dominant desktop/mobile cutoff
   (min-width:768px used elsewhere). */
@media (max-width: 767px) {
    .srv-cat-bar {
        flex-direction: column;
        align-items: stretch;
    }
    .srv-cat-search {
        max-width: 100%;
        margin: 1rem 0;
    }
}

/* ── App View catalog toolbar (catalog-toolbar-partial.html) — search
   takes over the whole bar via ng-if instead of stacking below the
   pills, so it's always exactly one row on every screen size. Higher
   specificity than the .srv-cat-search rules above (and their mobile
   override) so this wins at every width without needing !important. ── */
.srv-cat-search-bar {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: .5rem;
    width: 100%;
}

.srv-cat-search-bar .srv-cat-search {
    flex: 1;
    min-width: 0;
    max-width: 560px;
    margin: 0;
}

.srv-cat-search-bar.ng-enter {
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity .22s ease, transform .22s cubic-bezier(.34,1.1,.64,1);
}
.srv-cat-search-bar.ng-enter-active {
    opacity: 1;
    transform: translateY(0);
}
.srv-cat-search-bar.ng-leave {
    opacity: 1;
    transition: opacity .15s ease;
}
.srv-cat-search-bar.ng-leave-active {
    opacity: 0;
}

/* ── All / datasource-focus view ──────────────── */
.srv-ds-focus {
    padding: .25rem 0 1rem;
}

/* ════════════════════════════════════════════════
   DS4 — Activity card (rich layout)
   No background, border only, consistent with
   existing ds-card system.
   ════════════════════════════════════════════════ */
/*.ds-card--activity {
    width: clamp(210px, 26vw, 260px);
    position: relative;
    padding-left: 14px;
}*/

/* Left accent bar — colour driven by data-ds-type */
.ds-act-accent {
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 2px;
    border-radius: 2px;
    background: var(--border);
    opacity: .9;
}
.ds-card--activity[data-ds-type="1"] .ds-act-accent { background: #05966923; }
.ds-card--activity[data-ds-type="2"] .ds-act-accent { background: #7c3aed2c; }
.ds-card--activity[data-ds-type="3"] .ds-act-accent { background: #2563eb30; }

/* Type badge — DS1 / DS2 / DS3 — pushed to the right of the icon+name
   row via margin-left:auto, mirroring how ds1-status sits opposite the
   icon in ds1-top-row. */
.ds-act-pill {
    flex-shrink: 0;
    font-size: var(--text-xxs);
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 1px 5px;
    line-height: 1.6;
    opacity: .75;
    margin-left: auto;
}

/* Key-value preview grid */
.ds-act-preview {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 4px 10px;
    padding: 6px 0 2px;
    border-top: 1px solid var(--border);
    margin-top: 4px;
}

.ds-act-kv {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.ds-act-kv-key {
    font-size: var(--text-xxs);
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
    /*color: var(--text-dim);*/
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    opacity: .7;
}

.ds-act-kv-val {
    font-size: var(--text-sm, 12px);
    font-weight: 500;
    /*color: var(--text);*/
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Stats fallback row */
.ds-act-stats {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    padding: 5px 0 2px;
    border-top: 1px solid var(--border);
    margin-top: 4px;
}

.ds-act-stat {
    font-size: var(--text-xs, 11px);

    font-weight: 500;
}

/* Footer stat chips (views, uses) */
.ds-act-meta {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    font-size: var(--text-xs, 11px);
 
    letter-spacing: .01em;
}

/* ── Product grid ─────────────────────────────── */
.srv-product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: .75rem;
    margin-bottom: 1rem;
}

.srv-product-card {
    position: relative;
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: .9rem 1rem;
    background: transparent;
    border: 1px solid var(--srv-border);
    border-radius: var(--srv-radius);
    cursor: pointer;
    text-align: left;
    transition: border-color .15s, transform .1s;
    overflow: hidden;
}

.srv-product-card:hover {
    border-color: var(--srv-text-muted);
    transform: translateY(-1px);
}

.srv-product-card--active {
    border-color: var(--srv-accent);
    box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb, 37 99 235) / .12);
}

.srv-product-card--locked { opacity: .75; }
.srv-product-card--locked:hover { transform: none; }

/* Colored left accent bar — inline color applied via ng-style */
.srv-product-accent {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    border-radius: var(--srv-radius) 0 0 var(--srv-radius);
}

.srv-product-icon {
    font-size: var(--text-lg);
    flex-shrink: 0;
    width: 2rem;
    text-align: center;
    margin-left: .25rem;
}

.srv-product-body { flex: 1; min-width: 0; }

.srv-product-name {
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--srv-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.srv-product-cat {
    font-size: var(--text-xs);
    color: var(--srv-text-muted);
    margin-bottom: .35rem;
}

.srv-product-tags { display: flex; gap: .3rem; flex-wrap: wrap; }

.srv-product-cta {
    flex-shrink: 0;
    font-size: var(--text-sm);
    color: var(--srv-text-muted);
}

.srv-product-card--active .srv-product-cta { color: var(--srv-accent); }

.srv-product-empty {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    padding: 3rem;
    color: var(--srv-text-muted);
    font-size: var(--text-sm);
}

.srv-product-empty i { font-size: 2rem; opacity: .4; }

/* ── Workspace panel ──────────────────────────── */
.srv-workspace {
    border: 1px solid var(--srv-border);
    border-radius: var(--srv-radius);
    margin-bottom: 1rem;
    overflow: hidden;
}

.srv-workspace-hdr {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .75rem 1rem;
    border-bottom: 1px solid var(--srv-border);
}

.srv-workspace-hdr-accent {
    width: 4px;
    height: 2.4rem;
    border-radius: 2px;
    flex-shrink: 0;
}

.srv-workspace-hdr-body { flex: 1; min-width: 0; }

.srv-workspace-eyebrow {
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--srv-text-muted);
    margin-bottom: .1rem;
}

.srv-workspace-title {
    font-size: var(--text-md);
    font-weight: 700;
    color: var(--srv-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.srv-workspace-hdr-right {
    display: flex;
    align-items: center;
    flex-shrink: 0;
}

/* Entitlement gate */
.srv-gate {
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: .85rem 1.25rem;
    border-bottom: 1px solid var(--bs-warning-border-subtle, var(--srv-border));
}

.srv-gate-lock  { font-size: 1.2rem; color: var(--bs-warning-text-emphasis); flex-shrink: 0; }
.srv-gate-title { font-size: var(--text-sm); font-weight: 600; color: var(--bs-warning-text-emphasis); }
.srv-gate-hint  { font-size: var(--text-xs); color: var(--srv-text-muted); margin-top: .1rem; }

/* Workspace body */
.srv-workspace-body { 
    padding: 1rem;
    padding: 1px;
   
}

/* Component sections — collapsible */
.srv-component-section {
    margin-bottom: .25rem;
    /*border: 1px solid var(--srv-border-subtle);*/
    border-radius: 6px;
    overflow: hidden;
}

.srv-component-section:last-child { margin-bottom: 0; }

.srv-component-eyebrow {
    display: flex;
    align-items: center;
    padding: .5rem .75rem;
    font-size: var(--text-md);
   
    font-weight: 700;
    letter-spacing: .1em;
    /*text-transform: uppercase;*/
    color: var(--srv-text-muted);
    cursor: pointer;
    user-select: none;
    gap: .25rem;
    transition: color .1s;
}

.srv-component-eyebrow:hover { color: var(--srv-text); }

/* Chevron pushed to the far right */
.srv-section-chevron {
    margin-left: auto;
    font-size: var(--text-xs);
    opacity: .6;
    transition: transform .15s;
}

.srv-component-section--collapsed .srv-component-eyebrow {
    border-bottom: none;
}

/* Section body */
.srv-component-body {
    /*padding: .75rem;
    border-top: 1px solid var(--srv-border-subtle);*/
}

/* Locked hint */
.srv-locked-hint {
    display: flex;
    align-items: center;
    padding: 1.25rem;
    font-size: var(--text-sm);
    color: var(--srv-text-muted);
    border-radius: 6px;
    border: 1px solid var(--srv-border-subtle);
}

/* ── Tab placeholder ──────────────────────────── */
.srv-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 5rem 2rem;
    color: var(--srv-text-muted);
    text-align: center;
    gap: .5rem;
}

.srv-placeholder i { font-size: var(--text-xl); opacity: .25; margin-bottom: .5rem; }

.srv-placeholder-title {
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--srv-text);
}

.srv-placeholder-hint {
    font-size: var(--text-sm);
    color: var(--srv-text-muted);
}

/* ── AI step selector ────────────────────────── */
/* .srv-ai-data-selector (App View Create slide only) is a sibling of
   .srv-ai-selector — same "pick a card" look for browsing/reusing data
   instead of creating new, so it shares this rule rather than
   duplicating it. */
.srv-ai-selector,
.srv-ai-data-selector {
    padding: .5rem 0 .25rem;
}

.srv-ai-selector-heading {
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--srv-text-muted);
    margin-bottom: .75rem;
    padding: 0 .25rem;
}

.srv-ai-cards {
    display: flex;
    flex-direction: column;
    gap: .4rem;
}

.srv-ai-card {
    display: flex;
    align-items: center;
    gap: .875rem;
    padding: .75rem 1rem;
    border: 1px solid var(--srv-border);
    border-radius: 8px;
    cursor: pointer;
    transition: border-color .15s;
    background: transparent;
    width: 100%;
    text-align: left;
}

.srv-ai-card:hover { border-color: var(--srv-accent); }

.srv-ai-card-icon {
    flex-shrink: 0;
    width: 2.25rem;
    height: 2.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    border: 1px solid var(--srv-border);
    font-size: var(--text-md);
    color: var(--srv-accent);
}

.srv-ai-card-body {
    flex: 1;
    min-width: 0;
}

.srv-ai-card-title {
    font-size: var(--text-md);
    font-weight: 600;
    color: var(--srv-text);
    line-height: 1.25;
}

.srv-ai-card-desc {
    font-size: var(--text-xs);
    color: var(--srv-text-muted);
    margin-top: .15rem;
    line-height: 1.35;
}

.srv-ai-card-arrow {
    flex-shrink: 0;
    font-size: var(--text-sm);
    color: var(--srv-text-muted);
    opacity: .5;
    transition: opacity .15s, transform .15s;
}

.srv-ai-card:hover .srv-ai-card-arrow {
    opacity: 1;
    transform: translateX(2px);
}

/* Back bar shown when a step is active */
.srv-ai-back {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .4rem .25rem .75rem;
    margin-bottom: .25rem;
    border-bottom: 1px solid var(--srv-border-subtle);
}

.srv-ai-back-btn {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .04em;
    color: var(--srv-text-muted);
    background: transparent;
    border: none;
    padding: .25rem .5rem;
    border-radius: 4px;
    cursor: pointer;
    transition: color .12s;
}

.srv-ai-back-btn:hover { color: var(--srv-text); }

.srv-ai-back-label {
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--srv-text-muted);
    margin-left: auto;
}

/* ── Inline product datasources — hidden on desktop ─── */
.srv-product-inline-ds {
    display: none; /* datasources-wrapper handles desktop layout */
}

/* ── Responsive ───────────────────────────────── */
@media (max-width: 640px) {
    .srv-component-eyebrow { font-size: var(--text-md); }
    .srv-cat-pill          { font-size: var(--text-md); }
    .srv-product-name      { font-size: var(--text-md); }
    .srv-badge             { font-size: var(--text-md); }
    .srv-product-cat       { font-size: var(--text-md); }



    .srv-toolbar { flex-direction: column; align-items: flex-start; }
    .srv-toolbar-right { align-self: stretch; }
    .srv-toolbar-right .srv-btn { flex: 1; justify-content: center; }
    .srv-search-bar { flex-direction: column; }
    .srv-search-field,
    .srv-search-field--lg,
    .srv-search-field--sm { flex: 1; min-width: 100%; }
    .srv-view-tabs { align-self: stretch; }
    .srv-view-tab { flex: 1; justify-content: center; }
    .srv-product-grid { grid-template-columns: 1fr; }
    .srv-workspace-hdr { flex-wrap: wrap; }

    /* ── Combined view: desktop-only elements hidden on mobile ─── */
    /* datasources-wrapper is now at the top so visible on mobile too */
    .datasources-wrapper   { display: block; }
    /* ds-toolbar, layout toggle and select are desktop-only */
    .datasources-wrapper .ds-toolbar { display: none; }
    .creator               { display: none !important; }
    /* Workspace is inside srv-product-inline-ds on mobile */
    .srv-workspace--desktop { display: none !important; }

    /* ── When a product is selected: collapse to active card only ─── */
    /* Hides other product cards and filter pills so the inline
       workspace + datasources aren't sandwiched mid-list ("All" fix) */
    .products--has-selection .srv-cat-pills { display: none !important; }
    .products--has-selection .srv-product-card:not(.srv-product-card--active) { display: none !important; }

    /* ── Inline product datasources ─── */
    .srv-product-inline-ds {
        display: block;
        grid-column: 1 / -1;         /* span full grid width */
        border: 1px solid var(--srv-border);
        border-radius: var(--srv-radius);
        margin-bottom: .75rem;
        overflow: hidden;
      
    }

    /* Datasources flow vertically on mobile (both inline and wrapper) */
    .srv-product-inline-ds .datasources,
    .datasources-wrapper .datasources {
        flex-direction: column;
    }

    /* Sections inside the top-level wrapper also cancel the entrance animation */
    .datasources-wrapper .source-section {
        opacity: 1;
        transform: none;
        transition: none;
    }

    /* Cancel the approot entrance animation — sections are shown via ng-if,
       no separate reveal trigger exists here */
    .srv-product-inline-ds .source-section {
        opacity: 1;
        transform: none;
        transition: none;
        border-bottom: 1px solid var(--srv-border-subtle);
    }

    .srv-product-inline-ds .source-section:last-child {
        border-bottom: none;
    }
}

/* ══════════════════════════════════════════════════════════════
   WV SLIDES — App View style tile carousel (widget workspace)
   Same interaction language as the home page App View
   (see templates/docs/app-view-slide-mode.md): floating glass dock,
   wheel rubber-band overscroll, axis-locked touch swipe, progress bar.
   Gestures live in service-platform.module.js.
   ══════════════════════════════════════════════════════════════ */

.wv-slides-shell {
    position: relative;
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.wv-slides-viewport {
    position: relative;
    overflow: hidden;
    flex: 1;
    min-height: 0;
}

.wv-slides-track {
    display: flex;
    height: 100%;
    transition: transform 0.55s cubic-bezier(0.22, 0.9, 0.28, 1);
    will-change: transform;
}

.wv-slide {
    flex: 0 0 100%;
    max-width: 100%;
    height: 100%;
    opacity: 0.25;
    transform: scale(0.98);
    transition: opacity 0.45s ease, transform 0.45s ease;
}

.wv-slide--active {
    opacity: 1;
    transform: scale(1);
}

.wv-slide__inner {
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    /* spring-back after a rubber-band pull that didn't commit */
    transition: transform 0.4s cubic-bezier(0.22, 0.9, 0.28, 1);
}

/* While the wheel is actively pulling, follow it near-instantly */
.wv-slide__inner.wv-stretch-live {
    transition: transform 0.07s linear;
}

/* While a finger drags the track, no easing — 1:1 follow */
.wv-slides-track.wv-drag-live {
    transition: none;
}

/* ── Stack mode — every tile side by side in a plain flex row
      (see vm.stackMode / vm.setCanvasView in service-platform.module.js).
      Sits inside .wv-slides-shell, reusing its flex:1/overflow:hidden
      sizing — only the row-vs-track layout differs from Slide mode. ── */
.wv-stack-viewport {
    display: flex;
    flex-direction: row;
    flex: 1;
    min-height: 0;
    overflow-x: auto;
    overflow-y: hidden;
}

.wv-stack-pane {
    position: relative;
    height: 100%;
    min-width: 0;
    overflow-y: auto;
    overflow-x: hidden;
}

.wv-stack-handle {
    flex: 0 0 6px;
    width: 6px;
    cursor: col-resize;
    background: var(--srv-border);
    opacity: .5;
    transition: opacity .15s ease, background .15s ease;
}

.wv-stack-handle:hover {
    opacity: 1;
    background: var(--srv-accent);
}

/* While dragging: keep the handle highlighted and stop panes (their
   iframes/fnc-browser content especially) from swallowing mousemove */
body.wv-stack-resizing {
    cursor: col-resize;
    user-select: none;
}

body.wv-stack-resizing--vertical {
    cursor: row-resize;
}

body.wv-stack-resizing .wv-stack-pane {
    pointer-events: none;
}

/* Below the same breakpoint vm.pageWindow.isMobile uses (width < 768,
   see WindowResizeService in category-data.service.js): panes stack
   vertically instead of side by side. Handles switch from a vertical
   bar (col-resize) to a horizontal bar (row-resize) — same drag
   behaviour, just the other axis (see vm.startStackResize). */
@media (max-width: 767px) {
    .wv-stack-viewport {
        flex-direction: column;
        overflow-x: hidden;
        overflow-y: auto;
    }

    .wv-stack-pane {
        width: 100%;
        height: auto;
        min-height: 0;
    }

    .wv-stack-handle {
        width: 100%;
        height: 6px;
        cursor: row-resize;
    }
}

/* ── Progress bar ─────────────────────────────────── */
.wv-slides-progress {
    position: absolute;
    top: 0;
    left: 0;
    height: 2px;
    background: var(--srv-accent);
    opacity: .55;
    transition: width 0.55s cubic-bezier(0.22, 0.9, 0.28, 1);
    z-index: 3;
    pointer-events: none;
}

/* ── Glass dock — same style and position as home's .n1-dock,
      doubling as the app footer while showing (nav/footer are
      already hidden site-wide on /services, see hideNavTemplates in
      app.module.js). Edge-to-edge like a real footer/tab bar instead
      of a floating pill.
      position:fixed here anchors to the nearest containing-block
      ancestor, which is .no-panel (it has will-change:transform for
      the panel-slide animation) rather than the true viewport — but
      .no-panel is inset:0/100% and matches the viewport exactly when
      its panel is active, so this renders identically to true
      viewport-fixed while still correctly sliding off-screen with an
      inactive panel. absolute (scoped to .wv-slides-shell) used to
      sink the dock out of view whenever the shell's box rendered
      taller than the viewport. ───────────────────────────────────── */
.wv-dock {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2px;
    /*padding: 6px 10px;*/
    border-top: 1px solid var(--srv-border);
    width: 100%;
    overflow-x: auto;
}

@supports (-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px)) {
    .wv-dock {
        background: color-mix(in srgb, var(--srv-accent) 5%, transparent);
        -webkit-backdrop-filter: blur(14px);
        backdrop-filter: blur(14px);
    }
}

.wv-dock__btn {
    display: inline-flex;
    align-items: center;
    /*padding: 6px 10px;*/
    border-radius: 10px;
    border: none;
    background: transparent;
    color: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    white-space: nowrap;
    opacity: 0.55;
    cursor: pointer;
    transition: opacity 0.2s ease, background-color 0.2s ease;
}

.wv-dock__btn:hover {
    opacity: 0.85;
}

.wv-dock__btn--active {
    opacity: 1;
    background: color-mix(in srgb, var(--srv-accent) 10%, transparent);
}

/* ── Dock as the one shared footer ─────────────────────────────
   Both panels now funnel their chrome into their dock: the catalog
   dock hosts catalog-toolbar-partial.html (search/filter/select)
   beside the section buttons, and the workspace dock replaces the
   old .canvas-toolbar header (Catalog/Settings + view picker) and
   absorbs Slide view's tile pills. ──────────────────────────── */
.wv-dock__divider {
    width: 1px;
    height: 1.1rem;
    background: var(--srv-border);
    margin: 0 .4rem;
    flex-shrink: 0;
}

/* Catalog dock: toolbar include fills the remaining row */
.wv-dock__tools {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
}

/* The included .srv-cat-bar was designed as a standalone top bar —
   inside the dock it must stay a single centered row with no bottom
   margin. Higher specificity than the standalone rules (including the
   <768px column stack) so this wins at every width. */
.wv-dock .srv-cat-bar {
    flex: 1;
    min-width: 0;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 2px;
    margin-bottom: 0;
}

.wv-dock .srv-cat-search { margin: 0 auto; }

.wv-dock .srv-cat-pills { gap: 2px; }

/* Toolbar controls (search/filter/select — srv-btn, not wv-dock__btn)
   share the same footer row as the section buttons, so they get the
   same borderless, tight-padding Instagram-style treatment here —
   only inside the dock; .srv-btn stays untouched everywhere else. */
.wv-dock .srv-btn {
    padding: 6px 10px;
    border: none;
    font-size: var(--text-sm);
    opacity: 0.55;
}

.wv-dock .srv-btn:hover { opacity: 0.85; }

.wv-dock .srv-cat-pill--active {
    opacity: 1;
    border-color: transparent;
    background: color-mix(in srgb, var(--srv-accent) 10%, transparent);
}

/* Workspace dock: the view-picker and tile-picker menus must escape the
   dock box, so this variant can't scroll horizontally. */
.wv-dock--workspace { overflow: visible; }

.wv-dock__caret {
    font-size: var(--text-xs);
    opacity: .7;
    margin-left: .25rem;
}

/* ── View picker — drop-up menu anchored to its footer button ── */
.wv-dock__menu-wrap {
    position: relative;
    flex-shrink: 0;
}

/* Invisible click-away layer; sits above the dock's other buttons but
   below the menu, both inside the dock's own stacking context. */
.wv-dock-menu-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1;
}

.wv-dock-menu {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 0;
    z-index: 2;
    min-width: 150px;
    padding: .3rem;
    border: 1px solid var(--srv-border);
    border-radius: 10px;
    background: var(--bs-body-bg, #fff);
}

.wv-dock-menu__item {
    display: flex;
    align-items: center;
    width: 100%;
    padding: .35rem .6rem;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: inherit;
    font-size: var(--text-sm);
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

.wv-dock-menu__item:hover:not(:disabled) {
    background: color-mix(in srgb, var(--srv-accent) 8%, transparent);
}

.wv-dock-menu__item--active {
    color: var(--srv-accent);
    font-weight: 600;
}

.wv-dock-menu__item:disabled {
    opacity: .4;
    cursor: default;
}

/* Reserved gutter so labels align whether or not the ✓ is present */
.wv-dock-menu__item { padding-left: 1.6rem; position: relative; }
.wv-dock-menu__check {
    position: absolute;
    left: .55rem;
}

.wv-dock-menu__sep {
    height: 1px;
    background: var(--srv-border);
    margin: .3rem .2rem;
}

/* Tile picker — shortened names only, full names stay on the title
   tooltip; scrolls instead of stretching the menu when there are many. */
.wv-dock-menu--tiles {
    max-height: 260px;
    overflow-y: auto;
}

.wv-dock-menu__label {
    display: inline-block;
    max-width: 160px;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── Prev / next arrows ─────────────────────────────────────────
   Persistent navigation affordance — visible at rest (not just on
   hover) so it's obvious this direction is available, with a brief
   self-terminating "nudge" wiggle on mount to draw the eye. Shown on
   mobile too (previously hidden), sized down for a touch context, so
   swipe always has a visible, tappable fallback.
   ─────────────────────────────────────────────────────────────── */
.wv-slide-arrow {
    /* fixed, not absolute — same containing-block reasoning as .wv-dock
       above: anchors to the active .no-panel (viewport-sized) instead
       of .wv-slides-shell, which could sink it out of view */
    position: fixed;
    top: 50%;
    transform: translateY(-50%);
    z-index: 4;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 1px solid var(--srv-border);
    background: var(--bs-body-bg, #fff);
    color: inherit;
    font-size: 1.2rem;
    line-height: 1;
    opacity: 0.85;
    cursor: pointer;
    transition: border-color 0.2s ease, opacity 0.2s ease;
    animation: wvArrowNudge 1.1s ease-in-out 2;
}

.wv-slide-arrow:hover:not(:disabled) {
    opacity: 1;
    border-color: var(--srv-accent);
}

.wv-slide-arrow:disabled {
    opacity: 0.15;
    cursor: default;
    animation: none; /* boundary reached — no nudge toward a direction you can't go */
}

@keyframes wvArrowNudge {
    0%, 100% { transform: translateY(-50%) translateX(0); }
    50%      { transform: translateY(-50%) translateX(calc(4px * var(--nudge-dir, 1))); }
}

.wv-slide-arrow--prev { left: 10px; --nudge-dir: -1; }
.wv-slide-arrow--next { right: 10px; --nudge-dir: 1; }

/* Next carries more visual weight than Prev — accent-tinted so the
   forward direction reads as the primary action at a glance */
.wv-slide-arrow--next:not(:disabled) {
    opacity: 1;
    color: var(--srv-accent);
    border-color: var(--srv-accent);
    background: color-mix(in srgb, var(--srv-accent) 12%, var(--bs-body-bg, #fff));
}

.wv-slide-arrow--next:not(:disabled):hover {
    background: color-mix(in srgb, var(--srv-accent) 20%, var(--bs-body-bg, #fff));
}

@media (max-width: 767px) {
    /* ⚠️ These used to shrink to 30x30 here, described as "sized down for a
       touch context" — backwards. Touch is the context that needs the LARGER
       target, and 30px is below the 44px floor <app-panel-bar> sets for every
       control under 768px. Same floor, same reason: a thumb is not a cursor. */
    .wv-slide-arrow {
        width: 44px;
        height: 44px;
        font-size: 1.2rem;
    }
    .wv-slide-arrow--prev { left: 4px; }
    .wv-slide-arrow--next { right: 4px; }
    .wv-dock-menu__label { max-width: 100px; }
}

@media (prefers-reduced-motion: reduce) {
    .wv-slide-arrow { animation: none; }
}

@media (prefers-reduced-motion: reduce) {
    .wv-slides-track,
    .wv-slide,
    .wv-slide__inner,
    .wv-slides-progress {
        transition: none;
    }
}

/* ══════════════════════════════════════════════════════════════
   CV SLIDES — Products tab App View content padding
   Carousel mechanics (viewport/track/slide/dock/arrows) reuse the
   wv-* classes above as-is; this is the only catalog-specific rule
   needed since slide content reuses existing .srv-* components.
   ══════════════════════════════════════════════════════════════ */
.cv-slide-pad {
    /* No big bottom clearance needed anymore: the shell's measured height
       (measureCvSlidesHeight) already stops above the fixed dock. */
    /*padding: 12px 16px 16px;*/
    padding: 2px;
}

/* Grid view scrolls under the fixed workspace footer dock — reserve
   space so the last row of widgets isn't hidden behind it. Slide/Stack
   don't need it: their shell height already subtracts the dock
   (measureWvSlidesHeight). */
.main-canvas--dockpad {
    padding-bottom: 64px;
}

/* ══════════════════════════════════════════════════════════════
   WV COACH — first-time App View navigation hint
   Shared visual language with home's .n1-coach; shown once ever
   (localStorage), dismissible, and auto-dismissed on the first
   successful navigation. Used by both the workspace tile carousel
   and the catalog carousel.
   ══════════════════════════════════════════════════════════════ */
.wv-coach {
    position: absolute;
    /* ⚠️ Anchored to the BOTTOM, not the top. At `top: 14px` this sat on the
       exact same pixel row as the panel bar (.pb) inside .wv-slides-viewport
       and covered it outright — measured 175-233 against the bar's 175-210,
       hiding the panel title and its chips behind the hint that explains
       them. The bottom is also where the thing it points at lives: "Tap a dot
       below to jump to any section". */
    bottom: 14px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 6;
    display: flex;
    align-items: center;
    gap: 10px;
    max-width: calc(100% - 32px);
    padding: 10px 14px;
    border-radius: 14px;
    border: 1px solid var(--srv-accent);
    background: var(--bs-body-bg, #fff);
    animation: wvCoachIn 0.4s cubic-bezier(0.22, 0.9, 0.28, 1) both;
}

@keyframes wvCoachIn {
    /* rises INTO place from below, matching the bottom anchor above */
    from { opacity: 0; transform: translate(-50%, 8px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}

.wv-coach__icon {
    font-size: var(--text-lg);
    line-height: 1;
    flex-shrink: 0;
}

.wv-coach__body { min-width: 0; }

.wv-coach__title {
    font-weight: 700;
    font-size: var(--text-sm);
    line-height: 1.3;
}

.wv-coach__hint {
    font-size: var(--text-sm);
    opacity: 0.75;
    line-height: 1.3;
}

.wv-coach__dismiss {
    flex-shrink: 0;
    border: none;
    background: transparent;
    color: inherit;
    opacity: 0.6;
    padding: 4px;
    border-radius: 8px;
    cursor: pointer;
    transition: opacity 0.2s ease;
    /* ⚠️ Measured 21x34 — the smallest control on the screen, and the one
       every first-time visitor has to hit to get the hint out of the way.
       The glyph stays its own size; only the hit area grows to the floor. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
}

.wv-coach__dismiss:hover { opacity: 1; }

@media (max-width: 767px) {
    /* ⚠️ Sized to content, this collapsed to ~219px and broke the title
       across three lines ("Swipe or / scroll to / explore") once the
       dismiss button grew to the 44px floor. Give it the width it is
       already allowed so it reads as one deliberate bar rather than a
       cramped box floating over a card. */
    .wv-coach {
        width: calc(100% - 32px);
        justify-content: flex-start;
    }
    .wv-coach__body { flex: 1 1 auto; }
}

@media (max-width: 480px) {
    .wv-coach__hint { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .wv-coach { animation: none; }
}

/* Defensive backstop for the catalog App View — see the comment on
   .welcome-wrapper in service-platform.html. */
.welcome-wrapper--locked {
    overflow: hidden !important;
}

/* Mobile: the Create slide's header competes with embedded components'
   own chrome (e.g. ai-conversation-prompt brings its own back-bar +
   toolbar) for very little vertical space. "Entitled" is the default,
   expected state and doesn't need an always-visible badge, and the
   locked case already gets its own .srv-gate banner — so both badges
   are safe to drop here without losing any information the user can't
   get elsewhere. */
@media (max-width: 767px) {
    .srv-workspace-hdr .srv-badge--collapsible {
        display: none;
    }
    .srv-workspace-hdr {
        padding: .5rem .75rem;
    }
}

/* Focus mode — toggled by vm.toggleFocusMode() (button in
   srv-workspace-hdr-right, any screen size). Collapses this header
   down to a thin strip and fully hides the embedded
   ai-conversation-prompt's own aicp-chat-header, since the two headers
   otherwise stack and leave less room for the actual conversation.
   wv-dock is hidden separately via ng-if in service-platform.html;
   .wv-slides-shell--focus here just reclaims the padding that normally
   clears it. */
.srv-create--focus .srv-workspace-hdr {
    padding: .3rem .6rem;
}
.srv-create--focus .srv-workspace-hdr-accent {
    display: none;
}
.srv-create--focus .srv-workspace-eyebrow {
    display: none;
}
.srv-create--focus .srv-workspace-title {
    font-size: var(--text-sm);
}
.srv-create--focus .aicp-chat-header {
    display: none;
}
.wv-slides-shell--focus .cv-slide-pad {
    padding-bottom: 12px;
}


/* ══════════════════════════════════════════════════════════════
   TOUCH FLOOR — every control on this route, below 768px
   <app-panel-bar> guarantees a 44px floor for the controls it holds
   (see CLAUDE.md, "Title menus"). The controls that sit OUTSIDE a bar
   had no such guarantee, and measuring /services at 476px found six
   under it — the carousel arrows at 30x30, the coach dismiss at 21x34,
   "Group by topic" at 32 tall, the Sign in link at 39x21.

   ⚠️ This block is LAST in the file ON PURPOSE. These selectors carry
   the same specificity as the base rules they raise, so placed any
   earlier the base rule wins on source order and the floor silently
   does nothing. Same lesson as the .ib-card-toggle block in
   import-browser. Keep it at the end.
   ══════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
    /* A control inside a section heading is still a control. Nesting it
       in the <h3> is a separate semantics problem, noted but not fixed
       here — the size is what makes it unusable with a thumb. */
    .srv .srv-ds-grid h3 .ff-btn,
    .srv .srv-ds-grid section > h3 .ff-btn {
        min-height: 44px;
    }

    /* The one action on the signed-out notice, and the smallest thing on
       the screen after the coach dismiss. Padding rather than a height,
       so the link keeps its inline type and only its hit area grows. */
    .srv .auth-notice a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        padding-inline: 8px;
    }
}
