/* =============================================================================
   DEX Portal - Integrations and Telephony

   Screens that are not tables: the app catalogue, the webhook lists, the API key
   reveal and the dial pad. Kept out of components.css because none of it is
   shared - if a second module needs one of these, it moves.
   ============================================================================= */


/* =============================================================================
   API KEY REVEAL

   The one moment the plaintext key exists. Styled to be obviously a value to be
   copied rather than a field to be edited.
   ============================================================================= */

.api-key-reveal {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px;
    border: 1px solid var(--accent-border);
    border-radius: var(--radius-medium);
    background: var(--accent-tint);
}

.api-key-reveal__value {
    flex: 1;
    min-width: 0;
    font-family: var(--font-monospace);
    font-size: 12.5px;
    color: var(--text-primary);
    word-break: break-all;
    line-height: 1.5;
    user-select: all;
}

.data-table__row-actions {
    text-align: right;
    white-space: nowrap;
}


/* =============================================================================
   WEBHOOKS
   ============================================================================= */

.webhook-lists {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
    padding: var(--gutter-tight) var(--gutter) var(--gutter);
}

.webhook-section + .webhook-section {
    margin-top: 26px;
}

.webhook-section__header {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding-bottom: 12px;
    margin-bottom: 14px;
    border-bottom: 1px solid var(--border-subtle);
}

.webhook-section__title {
    font-size: var(--font-size-medium);
    font-weight: 600;
    letter-spacing: -0.015em;
}

.webhook-section__description {
    font-size: var(--font-size-small);
    color: var(--text-muted);
    margin-top: 3px;
    line-height: 1.55;
    max-width: 60ch;
}

.webhook-empty {
    padding: 22px;
    text-align: center;
    font-size: var(--font-size-small);
    color: var(--text-muted);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-medium);
}

.webhook-card {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    padding: 16px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-large);
    background: var(--surface-panel);
    transition: border-color var(--duration-instant) var(--easing-standard);
}

.webhook-card + .webhook-card {
    margin-top: 10px;
}

.webhook-card:hover {
    border-color: var(--border-strong);
}

/* A paused endpoint is dimmed rather than hidden - it still exists, and hiding
   it would leave people wondering where it went. */
.webhook-card--inactive {
    opacity: 0.62;
}

.webhook-card__main {
    flex: 1;
    min-width: 0;
}

.webhook-card__heading {
    display: flex;
    align-items: center;
    gap: 9px;
    flex-wrap: wrap;
}

.webhook-card__name {
    font-size: var(--font-size-base);
    font-weight: 600;
    color: var(--text-primary);
}

.webhook-card__description {
    font-size: var(--font-size-small);
    color: var(--text-muted);
    margin-top: 4px;
    line-height: 1.5;
}

.webhook-card__address {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    padding: 8px 10px;
    border-radius: var(--radius-small);
    background: var(--surface-subtle);
    border: 1px solid var(--border-subtle);
}

.webhook-card__address code {
    flex: 1;
    min-width: 0;
    font-family: var(--font-monospace);
    font-size: 11.5px;
    color: var(--text-secondary);
    word-break: break-all;
    line-height: 1.5;
}

.webhook-card__events {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 10px;
}

.webhook-card__event {
    font-family: var(--font-monospace);
    font-size: 10.5px;
    padding: 3px 7px;
    border-radius: var(--radius-button);
    background: var(--accent-tint);
    color: var(--accent);
}

.webhook-card__event--all {
    background: var(--status-neutral-tint);
    color: var(--status-neutral);
    font-family: var(--font-interface);
    font-weight: 600;
}

.webhook-card__meta {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    margin-top: 10px;
}

.webhook-card__failures {
    color: var(--status-danger);
    font-weight: 600;
}

.webhook-card__actions {
    display: flex;
    flex-direction: column;
    gap: 7px;
    flex: none;
}

.webhook-events {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--border-subtle);
}

.webhook-events__label {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 4px;
}

.webhook-events__option {
    padding: 7px 0;
    border-bottom: 1px solid var(--border-subtle);
}

.webhook-events__option:last-child {
    border-bottom: 0;
}


/* =============================================================================
   CONNECTED APPS CATALOGUE
   ============================================================================= */

.app-catalogue-scroll {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
    padding: var(--gutter-tight) var(--gutter) var(--gutter);
}

.app-catalogue {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
    gap: 14px;
}

/* -----------------------------------------------------------------------------
   Two columns: categories on the left, apps on the right

   The rail sticks rather than scrolling away, so the category you are in stays
   on screen however far down the cards you go. One scroll area for the page,
   not two nested ones, because a panel that scrolls inside another panel is
   the thing that makes a mouse wheel feel broken.
-------------------------------------------------------------------------- */

.app-catalogue-layout {
    display: flex;
    align-items: flex-start;
    gap: 22px;
}

.app-category-rail {
    position: sticky;
    top: 0;
    flex: none;
    width: 208px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.app-category-rail__item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    border: 1px solid transparent;
    border-radius: var(--radius-medium, 8px);
    background: transparent;
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-secondary);
    text-align: left;
    cursor: pointer;
    transition:
        background-color var(--duration-instant) var(--easing-standard),
        color var(--duration-instant) var(--easing-standard),
        border-color var(--duration-instant) var(--easing-standard);
}

.app-category-rail__item:hover {
    background: var(--surface-subtle);
    color: var(--text-primary);
}

.app-category-rail__item--selected,
.app-category-rail__item--selected:hover {
    background: var(--surface-panel);
    border-color: var(--border-default);
    color: var(--text-primary);
    font-weight: 600;
}

.app-category-rail__label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-category-rail__count {
    flex: none;
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.app-catalogue-content {
    flex: 1;
    min-width: 0;
}

/* -----------------------------------------------------------------------------
   Groups

   A heading, a count, and a hairline that runs to the edge of the grid. The
   rule does the separating, so the headings themselves stay quiet: they are
   signposts between cards, not titles competing with the page's own.
-------------------------------------------------------------------------- */

.app-group + .app-group {
    margin-top: 26px;
}

.app-group__heading {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.app-group__name {
    margin: 0;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    white-space: nowrap;
}

.app-group__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--surface-subtle);
    border: 1px solid var(--border-default);
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    color: var(--text-muted);
}

/* The hairline fills whatever is left of the row, so it ends with the grid
   rather than at some fixed width that stops looking right on a wide screen. */
.app-group__heading::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border-default);
}

.app-card {
    display: flex;
    flex-direction: column;
    padding: 16px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-large);
    /* Every card on these two screens sits on the same tinted face, built or
       not, connected or not. The status is carried by the pill and the border,
       which is where somebody looks for it, rather than by a background that
       has to be compared against a neighbouring card to be read at all. */
    background: var(--surface-app-card);
    transition:
        border-color var(--duration-instant) var(--easing-standard),
        box-shadow   var(--duration-instant) var(--easing-standard);
}

/* Not built yet: a dashed edge, so the difference between "you could turn this
   on" and "this is on the list" reads without anybody having to get as far as
   the pill. After .app-card, not before it, or the base rule would put the
   solid border back. The text stays at full strength, because it is the part
   somebody is here to read.
   The paler face this used to carry is gone: every card shares one background
   now, and a shade that can only be read by comparing it with the card next to
   it was never doing the work the dashed edge and the pill already do. */
.app-card--coming-soon {
    border-style: dashed;
}

/* There is deliberately no .app-card--coming-soon .app-card__logo rule. The
   mark used to be faded to 0.55 here; with one tile colour for every card,
   fading this one would put a second shade back on the page doing nothing the
   dashed border and the pill are not already saying in plainer terms. */

.app-card:hover {
    border-color: var(--border-strong);
    box-shadow: 0 6px 20px -12px rgba(16, 24, 40, 0.4);
}

.app-card__header {
    display: flex;
    align-items: center;
    gap: 11px;
}

/* The colour lives here, not on the card, so Connected apps and Modules stay
   the same shade without either page having to know about the other. */
.app-card__logo {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-medium);
    flex: none;
    display: grid;
    place-items: center;
    background: var(--surface-app-mark);
    color: var(--text-on-dark);
    font-size: 17px;
    font-weight: 700;
}

.app-card__identity {
    flex: 1;
    min-width: 0;
}

.app-card__name {
    font-size: var(--font-size-base);
    font-weight: 600;
    color: var(--text-primary);
}

.app-card__category {
    display: block;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    margin-top: 1px;
}

.app-card__description {
    font-size: var(--font-size-small);
    color: var(--text-muted);
    line-height: 1.55;
    margin: 12px 0 14px;
    flex: 1;
}

/* Only appears once a live check has come back with something worth knowing -
   the account balance, which is what turns a working integration into a failing
   one without any of its settings changing. */
.app-card__connection-detail {
    font-size: var(--font-size-tiny);
    font-weight: 600;
    color: var(--status-success);
    margin: -8px 0 12px;
}

.app-card__actions {
    display: flex;
    gap: 8px;
}

.app-card__actions .button {
    flex: 1;
}

.app-panel__lead {
    font-size: var(--font-size-base);
    color: var(--text-secondary);
    line-height: 1.6;
    margin-bottom: 18px;
}

.app-panel__heading {
    font-size: var(--font-size-small);
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 18px 0 8px;
}

.app-panel__list {
    margin: 0;
    padding-left: 18px;
    font-size: var(--font-size-base);
    color: var(--text-secondary);
    line-height: 1.7;
}


/* =============================================================================
   CALL PANEL
   ============================================================================= */

.call-panel__tabs {
    display: flex;
    gap: 6px;
    margin-bottom: 16px;
    padding: 4px;
    border-radius: var(--radius-medium);
    background: var(--surface-subtle);
}

.call-panel__tab {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    height: 34px;
    border-radius: var(--radius-small);
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-muted);
    transition:
        background var(--duration-instant) var(--easing-standard),
        color      var(--duration-instant) var(--easing-standard);
}

.call-panel__tab:hover {
    color: var(--text-primary);
}

.call-panel__tab--active {
    background: var(--surface-panel);
    color: var(--accent);
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.08);
}

/* ---- Dial pad ------------------------------------------------------------ */

/* No max-width: the panel is already sized to the keypad, so the pad simply
   fills it. Constraining it again here would leave a second gutter inside the
   first. */
.dial-pad {
    margin: 0 auto;
}

.dial-pad__display {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 18px;
    padding-bottom: 12px;
    border-bottom: 2px solid var(--border-default);
}

.dial-pad__number {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: 0;
    background: none;
    font-family: var(--font-interface);
    font-size: 26px;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--text-primary);
    text-align: center;
}

.dial-pad__number::placeholder {
    color: var(--text-muted);
    font-size: 17px;
    font-weight: 400;
}

.dial-pad__backspace {
    width: 34px;
    height: 34px;
    border-radius: var(--radius-small);
    display: grid;
    place-items: center;
    color: var(--text-muted);
    flex: none;
    transition:
        background var(--duration-instant) var(--easing-standard),
        color      var(--duration-instant) var(--easing-standard);
}

.dial-pad__backspace:hover {
    background: var(--border-subtle);
    color: var(--text-primary);
}

.dial-pad__keys {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.dial-pad__key {
    height: 58px;
    border-radius: var(--radius-large);
    background: var(--surface-subtle);
    border: 1px solid var(--border-subtle);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    transition:
        background var(--duration-instant) var(--easing-standard),
        transform  var(--duration-instant) var(--easing-standard);
}

.dial-pad__key:hover {
    background: var(--accent-tint);
}

.dial-pad__key:active {
    transform: scale(0.95);
    background: var(--accent-border);
}

.dial-pad__digit {
    font-size: 21px;
    font-weight: 500;
    color: var(--text-primary);
    line-height: 1;
}

.dial-pad__letters {
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.1em;
    color: var(--text-muted);
}

.dial-pad__actions {
    margin-top: 18px;
}

.call-button {
    width: 100%;
    height: 48px;
    font-size: var(--font-size-medium);
    border-radius: var(--radius-pill);
}

.call-button--outbound {
    background: var(--status-success);
    border-color: var(--status-success);
}

.call-button--outbound:hover:not(:disabled) {
    background: #0c8a4c;
    border-color: #0c8a4c;
}

.dial-pad__note {
    margin-top: 16px;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    text-align: center;
    line-height: 1.6;
}

/* =============================================================================
   INCOMING CALL SCREEN-POP

   Bottom right, above everything including an open slide-over. A ringing phone
   outranks whatever is on screen - that is the entire point of it - so this
   sits at a higher z-index than the panel layer rather than politely below it.
   ============================================================================= */

.incoming-call-stack {
    position: fixed;
    right: var(--gutter);
    bottom: var(--gutter);
    z-index: 300;
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* The stack spans the corner but must not swallow clicks aimed at the page
       behind it; the cards themselves take pointer events back. */
    pointer-events: none;
}

.incoming-call {
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: 12px;
    width: 320px;
    padding: 13px 14px;
    border-radius: var(--radius-large);
    background: var(--surface-panel);
    border: 1px solid var(--border-default);
    box-shadow: 0 18px 40px -18px rgba(16, 24, 40, 0.55);
    cursor: pointer;

    /* Enters from the right rather than fading: movement in the corner of the
       eye is what gets noticed while somebody is reading something else. */
    transform: translateX(16px);
    opacity: 0;
    transition:
        transform var(--duration-panel-open) var(--easing-standard),
        opacity   var(--duration-panel-open) var(--easing-standard);
}

.incoming-call--visible {
    transform: translateX(0);
    opacity: 1;
}

.incoming-call__icon {
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    flex: none;
    border-radius: 50%;
    background: var(--accent-tint);
    color: var(--accent);

    /* A ringing call pulses. A settled one does not - see below. */
    animation: incoming-call-pulse 1.4s var(--easing-standard) infinite;
}

/* Once the call is answered, missed or cancelled the card stops demanding
   attention and simply reports. Leaving it pulsing would keep pulling the eye
   back to something already over. */
.incoming-call--settled .incoming-call__icon {
    animation: none;
    background: var(--surface-subtle);
    color: var(--text-muted);
}

@keyframes incoming-call-pulse {
    0%,
    100% { box-shadow: 0 0 0 0 var(--accent-border); }
    50%  { box-shadow: 0 0 0 7px transparent; }
}

/* Respects the system setting. A pulsing badge is exactly the kind of motion
   people turn this off to avoid. */
@media (prefers-reduced-motion: reduce) {
    .incoming-call__icon {
        animation: none;
    }

    .incoming-call {
        transition: opacity var(--duration-quick) var(--easing-standard);
        transform: none;
    }
}

.incoming-call__detail {
    flex: 1;
    min-width: 0;
}

.incoming-call__status {
    font-size: var(--font-size-tiny);
    font-weight: 600;
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.incoming-call--settled .incoming-call__status {
    color: var(--text-muted);
}

.incoming-call__number {
    font-size: var(--font-size-base);
    font-weight: 600;
    color: var(--text-primary);
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.incoming-call__line {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    margin-top: 1px;
}

.incoming-call__dismiss {
    flex: none;
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    border: 0;
    border-radius: var(--radius-small);
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}

.incoming-call__dismiss:hover {
    background: var(--surface-subtle);
    color: var(--text-primary);
}

/* On a phone the card spans the width, above the safe area so it clears the
   home indicator. */
@media (max-width: 560px) {
    .incoming-call-stack {
        left: var(--gutter);
        right: var(--gutter);
        bottom: calc(var(--gutter) + env(safe-area-inset-bottom));
    }

    .incoming-call {
        width: auto;
    }
}

/* ---- Call recordings ------------------------------------------------------ */

/* The two quiet ways out from under the keypad - the recordings panel beside
   the dialler, and the full call log as a page. Side by side rather than
   stacked, so neither reads as a third action competing with Call. */
.dial-pad__secondary-links {
    display: flex;
    gap: 8px;
    margin-top: 14px;
}

.dial-pad__secondary-link {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    flex: 1 1 0;
    min-width: 0;
    padding: 9px 10px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-small);
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--font-size-small);
    font-weight: 500;
    cursor: pointer;
    transition: background var(--duration-instant) var(--easing-standard),
                color var(--duration-instant) var(--easing-standard);
}

.dial-pad__secondary-link:hover {
    background: var(--accent-tint);
    color: var(--accent);
    border-color: var(--accent);
}

.call-recording {
    border-bottom: 1px solid var(--border-subtle);
    padding: 10px 2px;
}

.call-recording:last-child {
    border-bottom: 0;
}

.call-recording__summary {
    display: flex;
    align-items: center;
    gap: 11px;
}

.call-recording__detail {
    flex: 1;
    min-width: 0;
}

.call-recording__play {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-small);
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--font-size-tiny);
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
}

.call-recording__play:hover:not(:disabled) {
    background: var(--accent-tint);
    color: var(--accent);
    border-color: var(--accent);
}

.call-recording__play:disabled {
    opacity: 0.6;
    cursor: default;
}

.call-recording__player:not(:empty) {
    margin-top: 10px;
}

.call-recording__leg {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    margin-bottom: 4px;
}

.call-recording__audio {
    width: 100%;
    height: 34px;
}

/* ---- Call history -------------------------------------------------------- */

/* Loading, empty and failed all land here. Each says which one it is - the
   panel used to paper over an empty list with sample rows, and a list that
   invents its own contents cannot be used to check whether anything works. */
.call-history__placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 34px 18px;
    text-align: center;
}

.call-history__placeholder-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    margin-bottom: 4px;
    border-radius: 50%;
    background: var(--accent-tint);
    color: var(--accent);
}

.call-history__placeholder-title {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

.call-history__placeholder-text {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    line-height: 1.6;
    max-width: 34ch;
}

.call-history__entry {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px 4px;
    border-bottom: 1px solid var(--border-subtle);
}

.call-history__entry:last-child {
    border-bottom: 0;
}

.call-history__direction {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    flex: none;
    display: grid;
    place-items: center;
}

.call-history__direction--inbound {
    background: var(--status-success-tint);
    color: var(--status-success);
}

.call-history__direction--outbound {
    background: var(--accent-tint);
    color: var(--accent);
}

.call-history__direction--missed {
    background: var(--status-danger-tint);
    color: var(--status-danger);
}

.call-history__detail {
    flex: 1;
    min-width: 0;
}

.call-history__name {
    font-size: var(--font-size-base);
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.call-history__number {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    font-family: var(--font-monospace);
    margin-top: 1px;
}

.call-history__meta {
    text-align: right;
    flex: none;
}

.call-history__when {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.call-history__outcome {
    font-size: var(--font-size-tiny);
    font-weight: 600;
    color: var(--text-secondary);
    margin-top: 2px;
}

.call-history__outcome--missed {
    color: var(--status-danger);
}

.call-history__redial {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    flex: none;
    display: grid;
    place-items: center;
    color: var(--status-success);
    background: var(--status-success-tint);
    transition: transform var(--duration-instant) var(--easing-standard);
}

.call-history__redial:hover {
    transform: scale(1.08);
}


/* =============================================================================
   RESPONSIVE
   ============================================================================= */

@media (max-width: 900px) {
    .app-catalogue {
        grid-template-columns: minmax(0, 1fr);
    }

    /* A 208px column beside one card per row is most of a narrow screen spent
       on a menu, so the rail lies down and becomes a row of chips you swipe. */
    .app-catalogue-layout {
        flex-direction: column;
        gap: 16px;
    }

    .app-category-rail {
        position: static;
        width: 100%;
        flex-direction: row;
        gap: 6px;
        overflow-x: auto;
        padding-bottom: 4px;
        scrollbar-width: none;
    }

    .app-category-rail::-webkit-scrollbar {
        display: none;
    }

    .app-category-rail__item {
        width: auto;
        flex: none;
        border-color: var(--border-default);
        border-radius: var(--radius-pill);
        padding: 7px 12px;
    }

    .webhook-card {
        flex-direction: column;
    }

    .webhook-card__actions {
        flex-direction: row;
        width: 100%;
    }

    .webhook-card__actions .button {
        flex: 1;
    }

    .api-key-reveal {
        flex-direction: column;
        align-items: stretch;
    }
}


/* =============================================================================
   INTEGRATION MANAGEMENT PANEL

   The Zadarma panel: balance, price plan, numbers and extensions. Everything
   here is read from the provider, so every value has a missing state and the
   layout has to hold its shape when one arrives empty.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   Label and value pairs

   A definition list rather than a table: these are facts about one thing, not
   rows of comparable things, and a table would put a scrollbar under three
   words on a narrow panel.
   ----------------------------------------------------------------------------- */

.detail-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.detail-list__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border-subtle);
}

.detail-list__row:last-child {
    border-bottom: none;
}

.detail-list__term {
    font-size: 12.5px;
    color: var(--text-muted);
}

.detail-list__value {
    font-size: 13px;
    font-weight: 550;
    color: var(--text-primary);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* The one number on this screen somebody looks for first. */
.account-figure {
    text-align: center;
    padding: 6px 0 2px;
}

.account-figure__value {
    font-size: 30px;
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.account-figure__label {
    font-size: 12.5px;
    color: var(--text-muted);
    margin-top: 4px;
}

/* Footnotes carry the reason a figure is absent, so they sit quietly under the
   thing they explain rather than competing with it. */
.app-panel__footnote {
    font-size: 12px;
    line-height: 1.55;
    color: var(--text-muted);
    margin-top: 12px;
}

.panel-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 14px;
}

.panel-actions .button {
    flex: 1 1 auto;
}


/* -----------------------------------------------------------------------------
   Virtual numbers
   ----------------------------------------------------------------------------- */

.number-list {
    display: flex;
    flex-direction: column;
}

/* Separated by a hairline rather than the section rule. Two numbers are two
   rows of one list, not two sections - using the same weight of line for both
   would flatten that difference away. */
.number-card {
    padding: 14px 0;
    border-bottom: 1px solid var(--border-default);
}

.number-card:first-child {
    padding-top: 0;
}

.number-card:last-child {
    padding-bottom: 0;
    border-bottom: none;
}

.number-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.number-card__number {
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.number-card__description {
    font-size: 12.5px;
    color: var(--text-muted);
    margin-top: 2px;
}


/* -----------------------------------------------------------------------------
   Extensions

   Number on the left, the person on the right. The select is given the room
   because the names are the variable-length half and the extensions are three
   digits.
   ----------------------------------------------------------------------------- */

.extension-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.extension-row {
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    align-items: center;
    gap: 12px;
}

.extension-row__identity {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.extension-row__number {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

/* Below this the number and the select stop fitting side by side, and a select
   squeezed to eighty pixels shows nobody's name. */
@container (max-width: 420px) {
    .extension-row {
        grid-template-columns: minmax(0, 1fr);
        gap: 6px;
        padding-bottom: 8px;
        border-bottom: 1px solid var(--border-subtle);
    }
}


/* =============================================================================
   EMAIL - the mailbox list and the form that adds one
   ============================================================================= */

.mailbox-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.mailbox-row {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 14px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-button);
    background: var(--surface-panel);
}

.mailbox-row__identity {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.mailbox-row__address {
    font-size: var(--font-size-base);
    font-weight: 600;
    color: var(--text-primary);
    word-break: break-all;
}

/* The two servers read as one fact about the mailbox, not two settings. */
.mailbox-row__servers {
    display: flex;
    flex-direction: column;
    gap: 2px;
    color: var(--text-muted);
    font-size: var(--font-size-tiny);
    font-variant-numeric: tabular-nums;
}

.mailbox-row__trouble {
    margin: 2px 0 0;
    padding: 7px 10px;
    border-radius: var(--radius-button);
    background: rgba(220, 38, 38, 0.09);
    color: #b91c1c;
    font-size: var(--font-size-tiny);
    line-height: 1.45;
}

.mailbox-row__actions {
    display: flex;
    align-items: center;
    gap: 14px;
    padding-top: 4px;
}

.task-link--danger { color: var(--status-danger, #dc2626); }

.mailbox-form__heading {
    margin: 18px 0 10px;
    padding-top: 14px;
    border-top: 1px solid var(--border-subtle);
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

/* Server, security and port on one line, because they are one decision. */
.mailbox-form__grid {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1.1fr) minmax(0, 0.8fr);
    gap: 10px;
}

.mailbox-form__grid--pair {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    margin-top: 10px;
}

@media (max-width: 900px) {
    .mailbox-form__grid,
    .mailbox-form__grid--pair {
        grid-template-columns: minmax(0, 1fr);
    }
}

.mailbox-form__mirror { margin-bottom: 10px; }

.form-field__hint {
    color: var(--text-muted);
    font-weight: 400;
}

/* Which server said no, and what it said, kept apart. */
.mailbox-verdict {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 14px 0 0;
    padding: 0;
    list-style: none;
}

.mailbox-verdict__line {
    padding: 8px 11px;
    border-radius: var(--radius-button);
    font-size: var(--font-size-tiny);
    line-height: 1.45;
}

.mailbox-verdict__line strong {
    display: inline-block;
    min-width: 62px;
    font-weight: 600;
}

.mailbox-verdict__line--ok {
    background: rgba(16, 185, 129, 0.12);
    color: #047857;
}

.mailbox-verdict__line--bad {
    background: rgba(220, 38, 38, 0.1);
    color: #b91c1c;
}

/* =============================================================================
   GOOGLE CALENDAR - the per-person switches

   Two lists of the same shape: what leaves DEX for somebody's Google account,
   and which of their own calendars come back. A dot carries the calendar's
   colour so the list reads like the calendar sidebar it mirrors.
   ============================================================================= */

.gcal-toggles {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 4px;
}

.gcal-toggle {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    cursor: pointer;
    transition: background 120ms ease;
}

.gcal-toggle:hover {
    background: var(--surface-muted);
}

.gcal-toggle input {
    width: 15px;
    height: 15px;
    accent-color: var(--brand-primary);
    cursor: pointer;
    flex: 0 0 auto;
}

.gcal-toggle__dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex: 0 0 auto;
}

.gcal-toggle__label {
    font-size: 13px;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.gcal-busy {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border-subtle);
    font-size: 12px;
    color: var(--text-muted);
    cursor: pointer;
}

.gcal-busy input {
    width: 15px;
    height: 15px;
    accent-color: var(--brand-primary);
    cursor: pointer;
    flex: 0 0 auto;
}