/* ─────────────────────────────────────────────────────────────────
   Connections modal — visual rules for the two-pane list/detail layout.

   S2 of the Connections refactor (see Documentation/Connections-NEXTACTIONS.md).
   The Tailwind utility classes in _ConnectionsModal.cshtml carry most of the
   shape. The rules here cover the things Tailwind utilities can't compose
   cleanly: the list-row + accent-stripe pattern, the type-card grid, the
   pane scroll behaviour.

   Class inventory (every class here is referenced from _ConnectionsModal.cshtml
   or painted by connRender() in connections.js):
     .conn-modal-shell        outer modal wrapper (height + flex)
     .conn-list-pane          left pane (the <aside>)
     .conn-detail-pane        right pane (the <section>)
     .conn-list               <ul> inside the left pane
     .conn-list-row           saved-connection / sample row button
     .conn-list-active        active-row treatment (left accent stripe)
     .conn-list-name          row name text
     .conn-list-meta          right side of row (type label + pill)
     .conn-list-type          db-type label (e.g. "snowflake")
     .conn-list-pill          inline pill (currently unused — left in for future)
     .conn-list-divider       <li> spacer (legacy; no longer painted by connRender)
     .conn-list-new           "+ New Connection" button row
     .conn-type-grid          right-pane card grid (in 'new' state)
     .conn-type-card          single db-type card
     .conn-type-icon          card icon
     .conn-type-name          card name
     .conn-type-tag           card one-line tagline
     .conn-secure-link        footer "How we secure your secrets" trigger
     .conn-secure-sheet       the in-shell security explainer slide-over
     .conn-secure-title       sheet heading
     .conn-secure-item        one claim row (glyph + body)
     .conn-secure-glyph       claim row icon well
     .conn-secure-body        claim row h4 + p
     .conn-secure-more        sheet's "full security overview" out-link
   ───────────────────────────────────────────────────────────────── */

/* ─── Left-pane filter search box ───
   A discrete, premium keyword filter that sits at the very top of the
   left list pane in all three Hub dialogs (Connections / Graphs / Portals).
   It filters the already-loaded list client-side — distinct from the Hub's
   big "FIND" command bar (which searches the server). Shared here because
   all three modals load connections.css and reuse .conn-list-pane.

   Markup contract (per dialog): a `.conn-search` wrapper as the first child
   of `.conn-list-pane`, holding a magnifier glyph + an `<input class="conn-search-input">`.
   The input's `oninput` calls the dialog's filter handler. */
.conn-search {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.625rem 0.75rem;
    border-bottom: 1px solid rgb(228, 228, 231); /* zinc-200 */
    background: transparent;
}
:root.dark .conn-search {
    border-bottom-color: rgb(63, 63, 70); /* zinc-700 */
}
.conn-search svg,
.conn-search .conn-search-glyph {
    width: 0.875rem;
    height: 0.875rem;
    flex-shrink: 0;
    color: rgb(161, 161, 170); /* zinc-400 */
}
.conn-search-input {
    flex: 1;
    min-width: 0;
    border: 0;
    background: transparent;
    padding: 0;
    font-size: 0.75rem;
    line-height: 1.4;
    color: rgb(24, 24, 27); /* zinc-900 */
    outline: none;
}
:root.dark .conn-search-input { color: rgb(244, 244, 245); /* zinc-100 */ }
.conn-search-input::placeholder {
    color: rgb(161, 161, 170); /* zinc-400 */
}
:root.dark .conn-search-input::placeholder {
    color: rgb(113, 113, 122); /* zinc-500 */
}
/* The wrapper picks up a quiet accent underline on focus-within — premium,
   no ring. Uses the platform accent so each dialog inherits its own theme. */
.conn-search:focus-within {
    border-bottom-color: var(--color-accent);
}

/* ─── Modal shell ─── */

.conn-modal-shell {
    /* Stable height: the dialog holds one premium size for its whole life,
       so filtering the left list (1 match or 50) never resizes the chrome —
       only the contents reflow inside it. Without this the shell sized to
       its content and collapsed vertically as the filtered list shrank.
       The body row inside is flex-1 + min-h-0, so it fills this height and
       its panes scroll; the header/footer are flex-shrink-0 and ride along.
       Capped so it never exceeds the viewport on short screens. */
    height: 70vh;
    max-height: 700px;
}
@media (max-height: 760px) {
    .conn-modal-shell { height: 86vh; max-height: none; }
}

/* Filtered-count line — pinned at the bottom of the left list pane, shown
   only while a keyword filter is active. Turns the quiet space beneath a
   narrowed list into a deliberate signal ("3 of 12") rather than emptiness.
   Painted by each dialog's render fn (connRender / gmRender / pmRender),
   which set the text and toggle .is-visible. Ink-soft, tabular, no border
   noise — premium-quiet, matching the search box above the list. */
.conn-list-count {
    flex-shrink: 0;
    display: none;
    padding: 0.5rem 0.875rem;
    font-size: 0.625rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: rgb(161, 161, 170); /* zinc-400 */
    border-top: 1px solid rgb(228, 228, 231); /* zinc-200 */
    font-variant-numeric: tabular-nums;
}
.conn-list-count.is-visible { display: block; }
:root.dark .conn-list-count {
    color: rgb(113, 113, 122); /* zinc-500 */
    border-top-color: rgb(63, 63, 70); /* zinc-700 */
}

/* ─── Left pane: connections list ─── */

.conn-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.conn-list li {
    margin: 0;
    padding: 0 0.5rem;
}

/* Saved-connection / sample row button. Full-width, two-row visual
   (name on top, type/pill row beneath). Accent stripe on the left edge
   when active. */
.conn-list-row {
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.125rem;
    text-align: left;
    padding: 0.5rem 0.75rem 0.5rem 0.875rem;
    margin: 0.125rem 0;
    border-radius: 0.5rem;
    border: 1px solid transparent;
    border-left: 3px solid transparent;
    background: transparent;
    color: inherit;
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease, transform 120ms ease;
}

.conn-list-row:hover {
    background: rgba(0, 0, 0, 0.04);
    border-color: rgba(0, 0, 0, 0.05);
    transform: translateX(1px);
}

:root.dark .conn-list-row:hover {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.06);
}

.conn-list-row.conn-list-active {
    background: color-mix(in srgb, var(--color-accent) 12%, transparent);
    border-color: color-mix(in srgb, var(--color-accent) 25%, transparent);
    border-left-color: var(--color-accent);
}

:root.dark .conn-list-row.conn-list-active {
    background: color-mix(in srgb, var(--color-accent) 18%, transparent);
}

.conn-list-name {
    font-size: 0.8125rem;
    font-weight: 600;
    line-height: 1.2;
    color: rgb(24, 24, 27); /* zinc-900 */
}

:root.dark .conn-list-name {
    color: rgb(228, 228, 231); /* zinc-200 */
}

.conn-list-row.conn-list-active .conn-list-name {
    color: var(--color-accent);
}

.conn-list-meta {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.625rem;
    color: rgb(113, 113, 122); /* zinc-500 */
    text-transform: uppercase;
    letter-spacing: 0.05em;
    line-height: 1.2;
}

.conn-list-type {
    font-weight: 600;
}

.conn-list-pill {
    display: inline-flex;
    align-items: center;
    padding: 0.0625rem 0.375rem;
    border-radius: 999px;
    font-size: 0.5625rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    background: color-mix(in srgb, var(--color-status-ok-fg) 18%, transparent);
    color: var(--color-status-ok-fg);
}

/* Beta pill — shown next to a connection-type name to flag a connector
   that's still maturing (currently SharePoint). Uses the platform amber
   accent so it reads as "evolving feature, may have rough edges" without
   shouting. The HTML hook is `<span class="conn-beta-pill">Beta</span>`,
   placed inside `.conn-type-name` (in the type-card grid) and appended
   to the editor pane name by `_updateEditHeader()` in connections.js. */
.conn-beta-pill {
    display: inline-flex;
    align-items: center;
    padding: 0.0625rem 0.375rem;
    margin-left: 0.375rem;
    border-radius: 999px;
    font-size: 0.5625rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    vertical-align: middle;
    background: rgb(254, 243, 199);  /* amber-100 */
    color: rgb(180, 83, 9);          /* amber-700 */
    border: 1px solid rgb(252, 211, 77); /* amber-300 */
}

:root.dark .conn-beta-pill {
    background: rgba(245, 158, 11, 0.15); /* amber-500 / 15% */
    color: rgb(251, 191, 36);             /* amber-400 */
    border-color: rgba(245, 158, 11, 0.4);
}

/* Reconnect affordance (post-net8 secret migration). Uses the same amber
   language as the Beta pill so it reads as "needs a quick re-auth", not an
   error. Three surfaces: the list-row pill, the edit-header pill, and the
   inline notice at the top of the credentials form. */

.conn-reconnect-pill {
    display: inline-flex;
    align-items: center;
    padding: 0.0625rem 0.375rem;
    border-radius: 999px;
    font-size: 0.5625rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    vertical-align: middle;
    background: rgb(254, 243, 199);      /* amber-100 */
    color: rgb(180, 83, 9);              /* amber-700 */
    border: 1px solid rgb(252, 211, 77); /* amber-300 */
}

/* List-row variant: overrides the default ok-green .conn-list-pill wash. */
.conn-list-pill-reconnect {
    background: rgb(254, 243, 199);      /* amber-100 */
    color: rgb(180, 83, 9);             /* amber-700 */
}

:root.dark .conn-reconnect-pill,
:root.dark .conn-list-pill-reconnect {
    background: rgba(245, 158, 11, 0.15); /* amber-500 / 15% */
    color: rgb(251, 191, 36);             /* amber-400 */
    border-color: rgba(245, 158, 11, 0.4);
}

.conn-reconnect-notice {
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    font-size: 0.75rem;
    line-height: 1.1rem;
    background: rgb(254, 243, 199);      /* amber-100 */
    color: rgb(146, 64, 14);             /* amber-800 */
    border: 1px solid rgb(252, 211, 77); /* amber-300 */
}

:root.dark .conn-reconnect-notice {
    background: rgba(245, 158, 11, 0.12);
    color: rgb(252, 211, 77);            /* amber-300 */
    border-color: rgba(245, 158, 11, 0.35);
}

/* Divider above the "+ New Connection" row */
.conn-list-divider {
    height: 1px;
    margin: 0.5rem 0.75rem;
    padding: 0 !important;
    background: rgb(228, 228, 231); /* zinc-200 */
    list-style: none;
}

:root.dark .conn-list-divider {
    background: rgb(63, 63, 70); /* zinc-700 */
}

/* "+ New Connection" entry. Lives in the static .conn-list-footer at the
   bottom of the left aside (S2.1 anchored footer). Dashed border at rest,
   accent fill on hover, solid accent when active (the type-card grid is
   open in the right pane). */
.conn-list-new {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    margin: 0;
    border-radius: 0.5rem;
    border: 1px dashed rgb(212, 212, 216); /* zinc-300 */
    background: transparent;
    color: rgb(113, 113, 122); /* zinc-500 */
    font-size: 0.75rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 120ms ease, color 120ms ease, background-color 120ms ease;
}

:root.dark .conn-list-new {
    border-color: rgb(82, 82, 91); /* zinc-600 */
    color: rgb(161, 161, 170); /* zinc-400 */
}

.conn-list-new:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
    background: color-mix(in srgb, var(--color-accent) 6%, transparent);
}

.conn-list-new.conn-list-active {
    border-style: solid;
    border-color: var(--color-accent);
    color: var(--color-accent);
    background: color-mix(in srgb, var(--color-accent) 10%, transparent);
}

/* ─── Right pane: type-card grid (new-connection state) ───
   3-col at desktop, 2-col below 768px. Cards lock to a uniform min-height
   so the 8 entries align in a 3-3-2 rhythm regardless of tagline length.
   Each card carries a per-vendor brand stripe at the top edge driven by
   the data-accent attribute (sky / red / blue / indigo / orange / cyan /
   amber / emerald). Stripe is faint at rest, saturates on hover. */

.conn-type-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.875rem;
}

@media (min-width: 768px) {
    .conn-type-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.conn-type-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 1rem 1rem 0.875rem;
    min-height: 6.5rem;
    border-radius: 0.75rem;
    border: 1px solid rgb(228, 228, 231); /* zinc-200 */
    background: white;
    color: rgb(63, 63, 70); /* zinc-700 */
    text-align: left;
    cursor: pointer;
    overflow: hidden;
    transition: border-color 140ms ease, transform 140ms ease, box-shadow 140ms ease, background-color 140ms ease;
}

:root.dark .conn-type-card {
    border-color: rgb(63, 63, 70); /* zinc-700 */
    background: rgb(39, 39, 42); /* zinc-800 */
    color: rgb(212, 212, 216); /* zinc-300 */
}

/* Brand stripe at the top edge — 2px, faint at rest, full opacity on hover. */
.conn-type-card::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 2px;
    background: rgb(161, 161, 170); /* zinc-400 fallback */
    opacity: 0.4;
    transition: opacity 140ms ease;
}

.conn-type-card[data-accent="sky"]::before     { background: rgb(56, 189, 248); }   /* Snowflake */
.conn-type-card[data-accent="red"]::before     { background: rgb(239, 68, 68); }    /* Databricks */
.conn-type-card[data-accent="blue"]::before    { background: rgb(59, 130, 246); }   /* SQL Server */
.conn-type-card[data-accent="indigo"]::before  { background: rgb(99, 102, 241); }   /* PostgreSQL */
.conn-type-card[data-accent="orange"]::before  { background: rgb(249, 115, 22); }   /* SharePoint */
.conn-type-card[data-accent="cyan"]::before    { background: rgb(6, 182, 212); }    /* MySQL */
.conn-type-card[data-accent="amber"]::before   { background: rgb(245, 158, 11); }   /* MariaDB */
.conn-type-card[data-accent="emerald"]::before { background: rgb(16, 185, 129); }   /* CSV */

.conn-type-card:hover {
    border-color: rgb(212, 212, 216); /* zinc-300 — quieter than accent */
    transform: translateY(-1px);
    box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.14);
    background: rgb(250, 250, 250); /* zinc-50 */
}

:root.dark .conn-type-card:hover {
    border-color: rgb(82, 82, 91); /* zinc-600 */
    background: rgb(45, 45, 50);
    box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.6);
}

.conn-type-card:hover::before {
    opacity: 1;
}

.conn-type-card:focus-visible {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 25%, transparent);
}

.conn-type-icon {
    width: 1.5rem;
    height: 1.5rem;
    color: rgb(82, 82, 91); /* zinc-600 — neutral; the brand color is the top stripe */
    margin: 0 0 0.625rem 0;
    flex-shrink: 0;
}

:root.dark .conn-type-icon {
    color: rgb(161, 161, 170); /* zinc-400 */
}

.conn-type-name {
    display: block;
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1.3;
    color: rgb(24, 24, 27); /* zinc-900 */
    margin: 0 0 0.1875rem 0;
}

:root.dark .conn-type-name {
    color: rgb(244, 244, 245); /* zinc-100 */
}

.conn-type-tag {
    display: block;
    font-size: 0.6875rem;
    font-weight: 500;
    line-height: 1.4;
    color: rgb(113, 113, 122); /* zinc-500 */
}

:root.dark .conn-type-tag {
    color: rgb(161, 161, 170); /* zinc-400 */
}

/* ─── Empty / list-only nudges in right pane ─── */
/* Tailwind utility classes (`hidden flex flex-col items-center ...`)
   already shape these. The only local rule we need is: when the modal
   first paints, the conn-pane-empty / conn-pane-listonly blocks need
   to flex-center vertically inside the right pane. The .hidden class
   from Tailwind is `display: none` — so when connRender() removes
   .hidden, we need the element to be `display: flex`, not block. */

#conn-pane-empty:not(.hidden),
#conn-pane-listonly:not(.hidden) {
    display: flex;
    min-height: 14rem;
}

/* ─── Editor pane db-type header (top of #conn-pane-edit) ───
   Small icon tile + name + tagline that tells the user what kind of
   connection they're configuring. Icon takes the per-vendor brand color
   via data-accent (sky / red / blue / etc.). */

.conn-edit-header-iconwrap {
    width: 2rem;
    height: 2rem;
    border-radius: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgb(244, 244, 245); /* zinc-100 */
    color: rgb(82, 82, 91); /* zinc-600 fallback */
    flex-shrink: 0;
}

:root.dark .conn-edit-header-iconwrap {
    background: rgb(63, 63, 70); /* zinc-700 */
    color: rgb(212, 212, 216); /* zinc-300 fallback */
}

.conn-edit-header-iconwrap[data-accent="sky"]     { color: rgb(56, 189, 248); }
.conn-edit-header-iconwrap[data-accent="red"]     { color: rgb(239, 68, 68); }
.conn-edit-header-iconwrap[data-accent="blue"]    { color: rgb(59, 130, 246); }
.conn-edit-header-iconwrap[data-accent="indigo"]  { color: rgb(99, 102, 241); }
.conn-edit-header-iconwrap[data-accent="orange"]  { color: rgb(249, 115, 22); }
.conn-edit-header-iconwrap[data-accent="cyan"]    { color: rgb(6, 182, 212); }
.conn-edit-header-iconwrap[data-accent="amber"]   { color: rgb(245, 158, 11); }
.conn-edit-header-iconwrap[data-accent="emerald"] { color: rgb(16, 185, 129); }

/* ─── Premium skeleton loading state ─── */
/* Shown the instant either modal opens, before the async list fetch lands,
   so the dialog never flashes a flat empty void that suddenly inflates with
   data. Painted by connShowSkeleton() (connections.js) and gmShowSkeleton()
   (graphs.js); cleared automatically when connRender()/gmRender() paint real
   rows. Class names are shared by both modals — the graphs modal reuses the
   same .conn-list <ul> shape, so one skeleton vocabulary covers both. */

@keyframes conn-skeleton-sweep {
    0%   { background-position: -160% 0; }
    100% { background-position: 160% 0; }
}

/* Left-pane skeleton rows — mirror the .conn-list-row footprint (two stacked
   bars: a name line + a meta line) so the transition to real rows is seamless. */
.conn-skeleton-row {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.5rem 0.75rem 0.5rem 0.875rem;
    margin: 0.125rem 0.5rem;
    border-radius: 0.5rem;
    border: 1px solid transparent;
    border-left: 3px solid transparent;
}

.conn-skeleton-bar {
    height: 0.5rem;
    border-radius: 999px;
    background: linear-gradient(
        90deg,
        rgba(0, 0, 0, 0.05) 25%,
        rgba(0, 0, 0, 0.10) 37%,
        rgba(0, 0, 0, 0.05) 63%
    );
    background-size: 220% 100%;
    animation: conn-skeleton-sweep 1.4s ease-in-out infinite;
}

:root.dark .conn-skeleton-bar {
    background: linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.05) 25%,
        rgba(255, 255, 255, 0.10) 37%,
        rgba(255, 255, 255, 0.05) 63%
    );
    background-size: 220% 100%;
}

.conn-skeleton-bar.conn-skeleton-name { width: 70%; height: 0.6rem; }
.conn-skeleton-bar.conn-skeleton-meta { width: 42%; }

/* Right-pane skeleton — a header block (icon tile + title/subtitle bars) plus
   a couple of content bars, so the detail side isn't a blank rectangle while
   the list loads. */
.conn-skeleton-pane {
    padding: 0.25rem 0;
}

.conn-skeleton-pane-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1.5rem;
}

.conn-skeleton-tile {
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 0.5rem;
    flex-shrink: 0;
    background: linear-gradient(
        90deg,
        rgba(0, 0, 0, 0.05) 25%,
        rgba(0, 0, 0, 0.10) 37%,
        rgba(0, 0, 0, 0.05) 63%
    );
    background-size: 220% 100%;
    animation: conn-skeleton-sweep 1.4s ease-in-out infinite;
}

:root.dark .conn-skeleton-tile {
    background: linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.05) 25%,
        rgba(255, 255, 255, 0.10) 37%,
        rgba(255, 255, 255, 0.05) 63%
    );
    background-size: 220% 100%;
}

.conn-skeleton-pane-headtext {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.conn-skeleton-pane-body {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
}

/* Reduced-motion: drop the sweep, fall back to a gentle static pulse so the
   affordance still reads as "loading" without animating across the screen. */
@media (prefers-reduced-motion: reduce) {
    .conn-skeleton-bar,
    .conn-skeleton-tile {
        animation: conn-skeleton-pulse 1.8s ease-in-out infinite;
        background: rgba(0, 0, 0, 0.07);
    }
    :root.dark .conn-skeleton-bar,
    :root.dark .conn-skeleton-tile {
        background: rgba(255, 255, 255, 0.08);
    }
}

@keyframes conn-skeleton-pulse {
    0%, 100% { opacity: 0.55; }
    50%      { opacity: 1; }
}

/* ─── Footer affordance ─── */

#stg-test-btn:disabled,
#stg-save-btn:disabled {
    cursor: not-allowed;
}

/* Footer Delete button: inline plain-text "Delete" style. Subtle in
   neutral state; reddens on hover. */
#stg-footer-delete-btn {
    border: none;
    background: transparent;
}

/* Save button hover. The layout's .ormle-accent-bg:hover rule uses
   var(--color-accent-hover); some palettes don't define that token, in
   which case the hover does nothing. Fall back to a manual darken so
   the Save button always reacts on hover. */
#stg-save-btn:not(:disabled):hover {
    background-color: color-mix(in srgb, var(--color-accent) 88%, black);
}

/* Test button hover. The layout's .ormle-accent-text-hover and
   .ormle-accent-border-hover utilities should handle this, but the
   palette compatibility shim for amber utilities + Tailwind CDN cascade
   order have been masking them in production. Force the accent on hover
   with id-scoped !important so it always reads. */
#stg-test-btn:not(:disabled):hover {
    color: var(--color-accent) !important;
    border-color: var(--color-accent) !important;
}

/* Delete button hover. Tailwind's hover:text-red-500 utility *should*
   work, but for parity with Save and Test we force it id-scoped so the
   footer affordance is uniform across the band — every button reacts. */
#stg-footer-delete-btn:hover {
    color: rgb(239, 68, 68) !important; /* red-500 */
}

/* ---------------------------------------------------------------------------
   SECTION 13 -- USED BY dependency panel + multiselect (Hub.md section 13).
   Connections port (Slice 4), mirroring the Graphs-modal patterns in
   graphs.css. The USED BY block is premium-quiet: a few counts with ink-soft
   summary lines, never a grid. Multiselect adds a per-row checkbox lane (calm
   at rest) + a thin selection bar above the list. No loud red destructive
   control at rest -- signal-amber on hover (section 13.10).

   Prefix: cm- ("connections modal") so these never collide with the existing
   .conn-* list/detail family.
   --------------------------------------------------------------------------- */

/* USED BY block (lives in the connection detail/editor pane). */
.cm-usedby {
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid #e4e4e7;
}
:root.dark .cm-usedby { border-top-color: #3f3f46; }

.cm-usedby-title {
    font-family: "Space Mono", "JetBrains Mono", monospace;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: #a1a1aa;
    margin-bottom: 8px;
}

/* One dependency line: count + noun on the left, ink-soft summary trailing. */
.cm-usedby-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 12px;
    line-height: 1.5;
    color: #3f3f46;
}
:root.dark .cm-usedby-row { color: #d4d4d8; }

.cm-usedby-count {
    font-family: "Space Mono", "JetBrains Mono", monospace;
    font-weight: 700;
    font-size: 13px;
    color: #18181b;
    min-width: 16px;
    text-align: right;
    flex-shrink: 0;
}
:root.dark .cm-usedby-count { color: #fafafa; }

.cm-usedby-noun { font-weight: 600; flex-shrink: 0; }

.cm-usedby-summary {
    font-family: "Space Mono", "JetBrains Mono", monospace;
    font-size: 11px;
    color: #71717a;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
:root.dark .cm-usedby-summary { color: #a1a1aa; }

/* Empty + loading states render as a single quiet line, no count column. */
.cm-usedby-row.cm-usedby-empty,
.cm-usedby-row.cm-usedby-loading {
    font-family: "Space Mono", "JetBrains Mono", monospace;
    font-size: 11px;
    color: #71717a;
    display: block;
}
:root.dark .cm-usedby-row.cm-usedby-empty,
:root.dark .cm-usedby-row.cm-usedby-loading { color: #a1a1aa; }

/* --- Multiselect: per-row checkbox lane (mirrors graphs.css). --- */

/* The checkbox is `position: absolute`, and it's a SIBLING of .conn-list-row
   (not a child) -- so it anchors to the nearest positioned ANCESTOR. That has
   to be the <li> that wraps both the checkbox and the row button. Without this
   the checkbox anchors to some ancestor further up (the scroll container) and
   paints off the row entirely -- present, opacity flipping, but invisible.
   (Graphs puts its checkbox inside the row, so the row's own position:relative
   is enough there; here the li must own the context.) */
#conn-list-ul li { position: relative; }
.conn-list-row { position: relative; }

.cm-row-checkbox {
    position: absolute;
    top: 0.6rem;       /* aligns with the row name line, not the row top edge */
    left: 0.75rem;     /* sits inside the gutter reserved by padding-left below */
    width: 13px;
    height: 13px;
    margin: 0;
    cursor: pointer;
    accent-color: var(--color-accent);
    opacity: 0;
    transition: opacity 120ms ease-out;
    z-index: 1;
}
/* Reveal on row hover, or persistently once any row is selected. The checkbox
   is a SIBLING of .conn-list-row (not a child) -- so the hover scope is the
   <li>, not the row button. (Graphs puts its checkbox inside the row, hence the
   `.gm-list-row:hover .gm-row-checkbox` shape there; here it must be the li.) */
#conn-list-ul li:hover .cm-row-checkbox,
.cm-has-selection .cm-row-checkbox,
.cm-row-checkbox:checked {
    opacity: 1;
}

/* Reserve the checkbox lane by growing the ROW's left padding -- never child
   margins (border-box reflows content inward; child margins push content past
   the right edge -> horizontal scrollbar, the bug we hit on graphs). Gated to
   hover OR an active selection so an un-selected list looks pixel-identical. */
.conn-list-row {
    transition: background-color 120ms ease, border-color 120ms ease,
                transform 120ms ease, padding-left 120ms ease-out;
}
.conn-list-row:hover,
.cm-has-selection .conn-list-row {
    padding-left: 2.1rem;
}

/* The connection list scrolls vertically only -- never sideways. The :hover
   transform on .conn-list-row (translateX(1px)) paints 1px past the right edge;
   clamping overflow-x on the <ul> kills the spurious horizontal scrollbar.
   Scoped to #conn-list-ul so other lists are untouched. */
#conn-list-ul { overflow-x: hidden; }

/* Selected row -- faint signal-amber wash, distinct from the accent-colored
   .conn-list-active "open" treatment so "selected" != "open". */
.conn-list-row.cm-row-selected {
    background: rgba(245, 163, 0, 0.07);
}
:root.dark .conn-list-row.cm-row-selected {
    background: rgba(245, 163, 0, 0.10);
}

/* Selection bar -- thin, mono-caps count + actions, sits above the list. */
.cm-selection-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 12px;
    border-bottom: 1px solid #e4e4e7;
    background: rgba(245, 163, 0, 0.06);
}
.cm-selection-bar.hidden { display: none; }
:root.dark .cm-selection-bar {
    border-bottom-color: #3f3f46;
    background: rgba(245, 163, 0, 0.08);
}

.cm-selection-count {
    font-family: "Space Mono", "JetBrains Mono", monospace;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: #71717a;
}
:root.dark .cm-selection-count { color: #a1a1aa; }

.cm-selection-actions {
    display: inline-flex;
    align-items: center;
    gap: 10px;
}

.cm-selection-clear,
.cm-selection-delete {
    font-family: "Space Mono", "JetBrains Mono", monospace;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    background: transparent;
    border: 0;
    padding: 2px 4px;
    cursor: pointer;
    transition: color 120ms ease-out;
}

/* Clear -- quiet ink-soft, brightens to ink on hover. */
.cm-selection-clear { color: #a1a1aa; }
.cm-selection-clear:hover { color: #52525b; }
:root.dark .cm-selection-clear { color: #71717a; }
:root.dark .cm-selection-clear:hover { color: #d4d4d8; }

/* Delete -- ink-soft at rest, signal-amber on hover. Never loud red at rest. */
.cm-selection-delete { color: #71717a; }
.cm-selection-delete:hover { color: #b45309; }   /* amber-700 light */
:root.dark .cm-selection-delete { color: #a1a1aa; }
:root.dark .cm-selection-delete:hover { color: #fbbf24; }  /* amber-400 dark */

/* ===========================================================================
   SECURE SHEET  —  "How we secure your secrets"
   ---------------------------------------------------------------------------
   A quiet footer link opens an in-shell slide-over explaining what happens to
   the password the user is about to type. NOT a second dialog: a modal over a
   modal reads as cheap, and stacking backdrops is a focus-management mess. The
   sheet is absolute inside .conn-twopane, so it covers exactly the two panes
   and the dialog header stays visible above it.

   NO DESIGN TOKENS IN HERE — literal hex only. This partial is shared by the
   Hub (Views/GraphSqlPro/Index.cshtml) and _DesignerLayout, but --color-accent
   and .ormle-accent-* are defined ONLY by the Designer / the React theme CSS.
   On the Hub they don't resolve, so a token-styled link silently renders as
   body ink and stops reading as a link at all. Same escape hatch as
   _PortalsModal.cshtml:122 and the workarounds at ~line 660 above.
   Zinc values are Tailwind's, hardcoded to match the surrounding chrome.
   =========================================================================== */

/* The footer trigger. Ink-soft at rest, deepens on hover — the same restrained
   idiom as .cm-selection-delete below: present, but never competing with the
   Save button for attention. */
.conn-secure-link {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    flex-shrink: 1;
    min-width: 0;                       /* lets the label ellipsis rather than push the buttons */
    padding: 0.25rem 0;
    background: transparent;
    border: 0;
    font-size: 0.6875rem;               /* 11px */
    font-weight: 600;
    line-height: 1.2;
    color: #71717a;                     /* zinc-500 */
    cursor: pointer;
    transition: color 120ms ease;
}
.conn-secure-link:hover { color: #18181b; }              /* zinc-900 */
.conn-secure-link > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.conn-secure-link svg { width: 12px; height: 12px; flex-shrink: 0; }
:root.dark .conn-secure-link { color: #a1a1aa; }         /* zinc-400 */
:root.dark .conn-secure-link:hover { color: #ffffff; }

/* The sheet. Parked off the right edge; slid in by [data-sheet="secure"] on the
   shell. z-index 5 clears the mobile detail pane's z-index 2, so it covers the
   drill-down layer too. visibility:hidden at rest keeps it out of the tab and
   AT trees; .conn-modal-shell is already overflow-hidden, so the parked sheet
   can't raise a scrollbar. */
.conn-secure-sheet {
    position: absolute;
    inset: 0;
    z-index: 5;
    overflow-y: auto;
    padding: 1.75rem;
    background: #ffffff;
    transform: translateX(100%);
    visibility: hidden;
    transition: transform 240ms cubic-bezier(0.22, 0.61, 0.36, 1), visibility 240ms;
}
:root.dark .conn-secure-sheet { background: rgb(39, 39, 42); }   /* zinc-800 — the shell surface */

.conn-modal-shell[data-sheet="secure"] .conn-secure-sheet {
    transform: translateX(0);
    visibility: visible;
}

/* Footer swap: the resting row (link + actions) gives way to a lone "Back to
   connections". Reading a security explainer next to a live "Create Connection"
   button is the incoherence this avoids. */
#conn-secure-actions { display: none; }
.conn-modal-shell[data-sheet="secure"] #conn-footer-row { display: none; }
.conn-modal-shell[data-sheet="secure"] #conn-secure-actions { display: flex; }

.conn-secure-title {
    margin: 0 0 1.375rem 0;
    font-size: 0.9375rem;               /* 15px */
    font-weight: 700;
    letter-spacing: -0.01em;
    color: #18181b;                     /* zinc-900 */
}
:root.dark .conn-secure-title { color: #ffffff; }

.conn-secure-item {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    margin-bottom: 1.125rem;
}
.conn-secure-item:last-of-type { margin-bottom: 1.5rem; }

/* Icon well: a soft zinc square, not the home page's black-on-green brutalist
   tile. That treatment belongs to home-terminal-press.css's aesthetic and would
   fight this rounded-2xl zinc dialog. */
.conn-secure-glyph {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 0.5rem;
    background: #f4f4f5;                /* zinc-100 */
    color: #52525b;                     /* zinc-600 */
}
.conn-secure-glyph svg { width: 15px; height: 15px; }
:root.dark .conn-secure-glyph { background: rgb(63, 63, 70); color: #d4d4d8; }  /* zinc-700 / zinc-300 */

.conn-secure-body { min-width: 0; }
.conn-secure-body h4 {
    margin: 0 0 0.1875rem 0;
    font-size: 0.75rem;                 /* 12px */
    font-weight: 700;
    color: #18181b;
}
.conn-secure-body p {
    margin: 0;
    font-size: 0.6875rem;               /* 11px */
    line-height: 1.65;
    color: #52525b;                     /* zinc-600 */
}
:root.dark .conn-secure-body h4 { color: #ffffff; }
:root.dark .conn-secure-body p { color: #a1a1aa; }

/* Descendant-qualified on purpose — do not flatten to `.conn-secure-more`.
   hub.css resets `body.hub-shell a { color: inherit }`, and that selector
   (0,1,2) outranks a bare class (0,1,0), so a flat rule loses on the Hub and the
   link silently inherits zinc-400. The Designer has no .hub-shell, so it would
   have won there — the same colour reading differently on the two surfaces that
   share this partial. Qualifying by the sheet (0,2,0) beats the reset on both.
   The sibling .conn-secure-link dodges this only by being a <button>. */
.conn-secure-sheet .conn-secure-more {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    padding-top: 0.875rem;
    border-top: 1px solid rgb(228, 228, 231);   /* zinc-200 */
    width: 100%;
    font-size: 0.6875rem;
    font-weight: 600;
    color: #71717a;
    text-decoration: none;
    transition: color 120ms ease;
}
.conn-secure-sheet .conn-secure-more:hover { color: #18181b; text-decoration: underline; }
:root.dark .conn-secure-sheet .conn-secure-more { border-top-color: rgb(63, 63, 70); color: #a1a1aa; }
:root.dark .conn-secure-sheet .conn-secure-more:hover { color: #ffffff; }

/* Global reduced-motion. The existing one at the foot of this file is nested
   inside @media (max-width: 600px) and so would never reach desktop. */
@media (prefers-reduced-motion: reduce) {
    .conn-secure-sheet { transition: none; }
}

/* Phone portrait: the link gets its own line above the buttons.
   Measured on a 375px viewport, the four action buttons leave the link ~60px —
   the label ellipsised to "How we secu…", which is worse than not offering it.
   Wrapping is the honest fix: the label is the whole point of the affordance.

   This is the one place the secure sheet adds height to the footer. The shell
   height is untouched (still 70vh / 86vh) and the body is flex-1 min-h-0, so it
   simply yields ~22px — the drill-down panes are absolute inside that body and
   still cannot reach the chrome. Iron Rule 2 holds.

   Kept here rather than in the drill-down block below so the whole secure-sheet
   concern stays in one place; that block stays about the drill-down. */
@media (max-width: 600px) {
    #conn-footer-row { flex-wrap: wrap; }
    #conn-secure-link,
    #conn-footer-actions { flex: 1 0 100%; }
    .conn-secure-link > span { white-space: normal; }   /* room to breathe; no ellipsis needed now */
    .conn-secure-sheet { padding: 1.25rem; }
}

/* ===========================================================================
   PHONE-PORTRAIT DRILL-DOWN  (shared by Graphs / Connections / Portals)
   ---------------------------------------------------------------------------
   Premium native master-detail: on a phone-portrait viewport the dialog shows
   ONE pane at a time. Land on the full-width list; tapping a row slides the
   detail pane in from the right over it; a quiet "Back" row in the detail
   header returns to the list. Driven by data-mobile-pane="list|detail" on the
   two-pane body (.conn-twopane), flipped by hub-mobile-panes.js.

   THREE IRON RULES (these are why a prior attempt broke desktop + the chrome):

   1. DESKTOP IS PROTECTED BY CONSTRUCTION. Every rule that changes layout is
      inside @media (max-width: 600px). The ONE rule outside the query is the
      back-button's base `display: none` — so the button is hidden at every
      width by default and only the mobile query reveals it. Above 600px,
      nothing here applies and the chevron cannot appear.

   2. THE SHELL HEIGHT + HEADER/FOOTER ARE NEVER TOUCHED. The committed shell
      (.conn-modal-shell 70vh / 86vh) already sandwiches the two-pane body
      (flex-1 min-h-0) between the flex-shrink-0 header and footer. The slide
      panes are position:absolute INSIDE that body only (.conn-twopane gets
      position:relative), so they fill exactly the body region and physically
      cannot overlap the header (filter) or footer (close button).

   3. THE DETAIL PANE IS NEVER HIDDEN AS A RESTING STATE. The list is the
      resting visible layer; the detail is the slide-in layer. The toolkit
      fills the detail on row-select; the slide reveals already-rendered
      content. If the JS never fires, you still see the list (the dialog is
      usable), never a blank panel.
   =========================================================================== */

/* RULE 1's global rules — the only two in this block. Both are safe at every
   width by construction:

   1. The back affordance is hidden everywhere by default; only the mobile query
      below reveals it. This guarantees no chevron on desktop / tablet /
      phone-landscape (all > 600px).

   2. The two-pane body is a positioning context at every width, so the secure
      sheet (above) can fill exactly the pane region and never the header or
      footer — Rule 2 holds for it as much as for the drill-down panes. This is
      inert on desktop: `position: relative` on a flex row whose children are
      all static changes nothing visually. The mobile query below sets the same
      property again, which is now redundant but harmless — it is left in place
      so that block still reads as self-contained. */
.conn-mobile-back { display: none; }
.conn-twopane { position: relative; }

@media (max-width: 600px) {
    /* The two-pane body becomes a positioning context for the two absolute
       panes. We do NOT touch the shell height — the body is already flex-1
       between the header and footer, so absolute panes fill only this region. */
    .conn-twopane {
        position: relative;
        overflow: hidden;
    }

    /* Both panes fill the body region. The list is the resting layer; the
       detail is parked off the right edge and slides in. Width override beats
       the desktop fixed-260 / flex-1 split. */
    .conn-twopane > .conn-list-pane,
    .conn-twopane > .conn-detail-pane {
        position: absolute;
        inset: 0;
        width: 100%;
        min-width: 0;
        max-height: none;          /* body had max-h-[70vh]; the pane fills the body now */
        border-right: 0;           /* no side-by-side divider in single-pane mode */
    }

    /* Detail starts off-screen to the right, slides in on demand. Opaque
       surface so it fully covers the list beneath it. */
    .conn-twopane > .conn-detail-pane {
        transform: translateX(100%);
        transition: transform 240ms cubic-bezier(0.22, 0.61, 0.36, 1);
        z-index: 2;
        background: white;
    }
    :root.dark .conn-twopane > .conn-detail-pane {
        background: rgb(39, 39, 42); /* zinc-800 — matches the shell surface */
    }

    /* DETAIL state: slide the detail in; once it's covering, pull the list out
       of the scroll/AT tree so it can't be reached behind the detail. */
    .conn-twopane[data-mobile-pane="detail"] > .conn-detail-pane {
        transform: translateX(0);
    }
    .conn-twopane[data-mobile-pane="detail"] > .conn-list-pane {
        visibility: hidden;
    }

    /* The "Back" affordance: a quiet ink-soft row pinned at the top of the
       detail pane, revealed only when we're in the detail layer. */
    /* A present, obvious nav header — not a thin line. Taller band (a
       comfortable thumb target), bigger label, larger chevron, so it reads
       unmistakably as "tap to go back" at the top of the detail pane. */
    .conn-twopane[data-mobile-pane="detail"] .conn-mobile-back {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        width: 100%;
        margin: 0 0 1rem 0;
        padding: 0.875rem 0;
        background: transparent;
        border: 0;
        border-bottom: 1px solid rgb(228, 228, 231); /* zinc-200 */
        color: rgb(82, 82, 91); /* zinc-600 */
        font-size: 1rem;
        font-weight: 700;
        line-height: 1.2;
        text-align: left;
        cursor: pointer;
    }
    :root.dark .conn-twopane[data-mobile-pane="detail"] .conn-mobile-back {
        border-bottom-color: rgb(63, 63, 70); /* zinc-700 */
        color: rgb(212, 212, 216); /* zinc-300 */
    }
    .conn-mobile-back svg { width: 1.25rem; height: 1.25rem; flex-shrink: 0; }

    /* Tighten the type-card grid gap so cards don't overflow a 360px viewport. */
    .conn-type-grid { gap: 0.625rem; }

    /* Reduced-motion: no slide, just swap. */
    @media (prefers-reduced-motion: reduce) {
        .conn-twopane > .conn-detail-pane { transition: none; }
    }
}
