/* IRANSansWeb. Bundled so the portal renders identically on a server with no Persian
   fonts installed — falling back to a system font makes Persian and Arabic text ragged and
   changes every metric in the layout.

   WOFF2 rather than the original TTF: half the bytes for identical rendering, which
   matters because these four faces load on the very first paint of every session, often
   over a link to a remote server. font-display: swap so text is readable immediately
   rather than invisible while the face loads; the fallback stack keeps Persian and Arabic
   legible in that window. */
@font-face {
    font-family: "IRANSansWeb";
    src: url("fonts/IRANSansWeb.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "IRANSansWeb";
    src: url("fonts/IRANSansWeb_Medium.woff2") format("woff2");
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "IRANSansWeb";
    src: url("fonts/IRANSansWeb_Bold.woff2") format("woff2");
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

/* Yasna administration portal.
   Logical properties (inline-start/end, margin-inline) are used throughout instead of
   left/right, so setting dir="rtl" on <html> mirrors the whole layout with no extra
   stylesheet. */

:root {
    --bg: #0f1729;
    --surface: #16203a;
    --surface-2: #1d2947;
    --border: #2a3860;
    --text: #e8edf9;
    --muted: #97a4c4;
    --accent: #4f8cff;
    --accent-strong: #2f6fe4;
    --ok: #2ecc8f;
    --warn: #f0b429;
    --danger: #ff5c5c;
    --radius: 10px;
    --sidebar-width: 236px;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);
    color: var(--text);
    font-family: "IRANSansWeb", "Segoe UI", Tahoma, system-ui, -apple-system, sans-serif;
    font-size: 15px;
    line-height: 1.55;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

h1 { font-size: 1.6rem; margin: 0 0 .3rem; }
h2 { font-size: 1.15rem; margin: 1.8rem 0 .7rem; }
h3 { font-size: 1rem; margin: 1.2rem 0 .5rem; }

/* ---------- Shell ---------- */

.yasna-shell { display: flex; min-height: 100vh; }

.yasna-sidebar {
    width: var(--sidebar-width);
    flex: 0 0 var(--sidebar-width);
    background: var(--surface);
    border-inline-end: 1px solid var(--border);
    padding: 1.1rem .8rem;
    display: flex;
    flex-direction: column;
    gap: 1.2rem;
}

.yasna-brand {
    font-weight: 700;
    font-size: 1.1rem;
    color: var(--text);
    padding-inline: .55rem;
}

.yasna-sidebar nav { display: flex; flex-direction: column; gap: .15rem; }

.yasna-sidebar nav a {
    color: var(--muted);
    padding: .5rem .55rem;
    border-radius: 8px;
    font-size: .93rem;
}

.yasna-sidebar nav a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.yasna-sidebar nav a.active { background: var(--accent-strong); color: #fff; }

/* A page that belongs to the item above it: indented, slightly smaller, with a guide line. */
.yasna-sidebar nav a.nav-sub {
    padding-inline-start: 1.4rem;
    font-size: .86rem;
    position: relative;
}

.yasna-sidebar nav a.nav-sub::before {
    content: "";
    position: absolute;
    inset-inline-start: .7rem;
    top: 25%;
    bottom: 25%;
    border-inline-start: 2px solid var(--border, rgba(127, 127, 127, .35));
}

.yasna-shell > main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.yasna-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .75rem 1.4rem;
    border-block-end: 1px solid var(--border);
    background: var(--surface);
}

.yasna-topbar .who { color: var(--muted); font-size: .88rem; }
.yasna-topbar .topbar-link { color: var(--muted); font-size: .88rem; white-space: nowrap; }
.yasna-topbar .topbar-link:hover { color: var(--text); }

.yasna-content { padding: 1.4rem; max-width: 1600px; width: 100%; }

/* ---------- Cards and stats ---------- */

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem 1.1rem;
    margin-block-end: 1rem;
}

.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: .8rem;
    margin-block-end: 1.2rem;
}

.stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .85rem 1rem;
}

.stat .label { color: var(--muted); font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; }
.stat .value { font-size: 1.75rem; font-weight: 500; margin-block-start: .2rem; }
.stat.ok .value { color: var(--ok); }
.stat.warn .value { color: var(--warn); }
.stat.danger .value { color: var(--danger); }

/* ---------- Tables ---------- */

.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); }

table { width: 100%; border-collapse: collapse; font-size: .9rem; }

th, td {
    text-align: start;
    padding: .6rem .75rem;
    border-block-end: 1px solid var(--border);
    white-space: nowrap;
}

th { background: var(--surface-2); color: var(--muted); font-weight: 500; font-size: .8rem; text-transform: uppercase; letter-spacing: .03em; }
tbody tr:hover { background: var(--surface-2); }
tbody tr:last-child td { border-block-end: none; }

/* ---------- Forms ---------- */

.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: .9rem; }

label { display: block; font-size: .85rem; color: var(--muted); margin-block-end: .3rem; }
label.checkbox { display: flex; align-items: center; gap: .5rem; color: var(--text, inherit); font-size: .95rem; }

/* Matched by omission rather than by listing every type: an <input> with no type attribute
   defaults to text, and listing types left those rendering as an unstyled browser box. */
input:not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=file]), select, textarea {
    width: 100%;
    padding: .5rem .6rem;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    font: inherit;
}

input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: .35rem; }
.checks label { display: flex; align-items: center; gap: .45rem; color: var(--text); font-size: .88rem; }
.checks input { width: auto; }
/* The "select all" row sits above the grid, separated from it, so it reads as an action on the
   list rather than as one more permission inside it. */
.checks .checks-all { grid-column: 1 / -1; padding-bottom: .4rem; margin-bottom: .25rem; border-bottom: 1px dashed var(--line, rgba(160, 190, 230, .22)); font-weight: 700; cursor: pointer; }

.field-error { color: var(--danger, #d33); font-size: .82rem; margin-block-start: .35rem; }

/* ---------- Shamsi (Solar Hijri) calendar picker ---------- */

.shamsi-picker { position: relative; display: inline-block; width: 100%; max-width: 20rem; }

.shamsi-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    width: 100%;
    padding: .55rem .7rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    cursor: pointer;
    text-align: start;
}
.shamsi-trigger:hover { border-color: var(--accent); }
.shamsi-trigger-value { display: flex; flex-direction: column; line-height: 1.3; }
.shamsi-trigger-main { font-size: 1rem; font-weight: 600; }
.shamsi-trigger-sub { font-size: .78rem; color: var(--muted); }
.shamsi-trigger-placeholder { color: var(--muted); }
.shamsi-trigger-icon { font-size: 1.1rem; opacity: .8; }

.shamsi-cal {
    position: absolute;
    inset-block-start: calc(100% + .4rem);
    inset-inline-start: 0;
    z-index: 30;
    width: 19rem;
    max-width: 90vw;
    padding: .7rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 12px 32px rgba(0, 0, 0, .45);
}

.shamsi-cal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .25rem;
    margin-block-end: .6rem;
}
.shamsi-cal-title { flex: 1; text-align: center; font-weight: 600; }
.shamsi-nav {
    width: 2rem;
    padding: .25rem 0;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text);
    cursor: pointer;
    line-height: 1;
}
.shamsi-nav:hover { border-color: var(--accent); }

.shamsi-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: .18rem; }
.shamsi-weekday {
    text-align: center;
    font-size: .74rem;
    color: var(--muted);
    padding-block-end: .25rem;
}
.shamsi-day-blank { aspect-ratio: 1; }
.shamsi-day {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 8px;
    color: var(--text);
    cursor: pointer;
    font-size: .9rem;
}
.shamsi-day:hover { background: var(--surface-2); border-color: var(--border); }
.shamsi-day.today { border-color: var(--accent); }
.shamsi-day.selected { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; }

.shamsi-cal-readout {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: .3rem .6rem;
    margin-block-start: .6rem;
    padding-block-start: .55rem;
    border-block-start: 1px solid var(--border);
    font-size: .82rem;
    color: var(--text);
}

.shamsi-cal-foot { display: flex; gap: .4rem; margin-block-start: .6rem; }
.shamsi-foot-btn { flex: 1; padding: .4rem 0; font-size: .82rem; }

/* ---------- Inline help tooltips ---------- */

/* A circled "i" next to a label. Hovering it, or focusing it with the keyboard,
   reveals a bubble explaining the field. Centring the bubble with physical
   left/transform keeps it over the icon in both the LTR and RTL layouts. */
.info-hint {
    position: relative;
    display: inline-flex;
    vertical-align: middle;
    margin-inline-start: .35rem;
    cursor: help;
}

.info-hint-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    border: 1px solid var(--muted);
    color: var(--muted);
    font: 700 .68rem/1 "Segoe UI", system-ui, sans-serif;
    font-style: normal;
    user-select: none;
    transition: color .15s ease, border-color .15s ease;
}

.info-hint:hover .info-hint-icon,
.info-hint:focus-visible .info-hint-icon { border-color: var(--accent); color: var(--accent); }

.info-hint-bubble {
    position: absolute;
    left: 50%;
    bottom: calc(100% + 8px);
    transform: translateX(-50%) translateY(4px);
    width: max-content;
    max-width: 240px;
    padding: .5rem .65rem;
    background: var(--surface-2);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 10px 24px rgba(0, 0, 0, .4);
    font-size: .8rem;
    font-weight: 400;
    line-height: 1.45;
    text-align: start;
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    transition: opacity .15s ease, transform .15s ease;
    z-index: 20;
    pointer-events: none;
}

.info-hint:hover .info-hint-bubble,
.info-hint:focus-within .info-hint-bubble {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
}

/* ---------- Buttons ---------- */

button, .btn {
    font: inherit;
    padding: .45rem .9rem;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text);
    cursor: pointer;
}

button:hover, .btn:hover { border-color: var(--accent); text-decoration: none; }
button:disabled { opacity: .55; cursor: not-allowed; }

.btn-primary, button.primary { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; }
.btn-danger, button.danger { background: transparent; border-color: var(--danger); color: var(--danger); }

.actions { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin-block-start: 1rem; }
.toolbar { display: flex; gap: .6rem; flex-wrap: wrap; align-items: end; margin-block-end: 1rem; }

/* ---------- Badges ---------- */

.badge { display: inline-block; padding: .12rem .5rem; border-radius: 999px; font-size: .76rem; border: 1px solid; }
.badge.active, .badge.ok { color: var(--ok); border-color: var(--ok); }
.badge.suspended { color: var(--warn); border-color: var(--warn); }
.badge.disabled, .badge.revoked { color: var(--danger); border-color: var(--danger); }
.badge.neutral { color: var(--muted); border-color: var(--border); }

/* A row the platform does not offer: still present and still editable, but visibly not part
   of what a customer is shown. Dimming rather than hiding keeps it one click away. */
.muted-row td { opacity: .62; }

/* ---------- Alerts and the one-time secret panel ---------- */

.alert { padding: .7rem .9rem; border-radius: var(--radius); border: 1px solid; margin-block-end: 1rem; }
.alert.error { color: var(--danger); border-color: var(--danger); background: rgba(255, 92, 92, .08); }
.alert.success { color: var(--ok); border-color: var(--ok); background: rgba(46, 204, 143, .08); }
.alert.info { color: var(--muted); border-color: var(--border); background: var(--surface-2); }

/* The one-time secret is visually loud on purpose: it is shown exactly once and an
   operator who scrolls past it has to rotate the credential to get another. */
.secret-panel { border: 2px solid var(--warn); border-radius: var(--radius); padding: 1rem; background: rgba(240, 180, 41, .07); margin-block-end: 1rem; }
.secret-panel h3 { margin-block-start: 0; color: var(--warn); }
.secret-value { display: block; font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: .9rem; word-break: break-all; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: .7rem; margin: .6rem 0; user-select: all; }

/* Each identifier, secret, and endpoint URL sits on its own row with a copy button, so an
   operator copies one value at a time instead of selecting text out of a combined block. */
.copy-field { margin: .55rem 0; }
.copy-field-label { display: block; font-size: .8rem; color: var(--muted); margin-block-end: .25rem; }
.copy-field-row { display: flex; gap: .5rem; align-items: stretch; }
.copy-field-value { flex: 1 1 auto; min-width: 0; font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: .9rem; word-break: break-all; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: .55rem .7rem; user-select: all; }
.copy-field-block { margin: 0; white-space: pre-wrap; text-align: left; user-select: text; }
.copy-btn { flex: 0 0 auto; align-self: stretch; white-space: nowrap; }
.copy-btn.copied { border-color: var(--ok); color: var(--ok); }
.secret-note { margin: .25rem 0 .6rem; font-size: .82rem; }
.secret-section { margin-block-start: 1.1rem; }

.muted { color: var(--muted); }
.empty { padding: 2rem 1rem; text-align: center; color: var(--muted); }
code { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: .87em; }

/* ---------- Sign-in ---------- */

.login-wrap { position: relative; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1.5rem; overflow: hidden; }

/* Three slowly drifting colour fields behind a blurred glass card. Pure CSS on purpose:
   the sign-in page is served before authentication, so it must not pull in a script. */
.login-aurora { position: absolute; inset: 0; z-index: 0; pointer-events: none; }

.login-aurora span {
    position: absolute;
    width: 46vmax;
    height: 46vmax;
    border-radius: 50%;
    filter: blur(90px);
    opacity: .5;
    will-change: transform;
}

.login-aurora span:nth-child(1) { background: radial-gradient(circle, #4f8cff, transparent 70%); inset-block-start: -12vmax; inset-inline-start: -10vmax; animation: aurora-a 22s ease-in-out infinite; }
.login-aurora span:nth-child(2) { background: radial-gradient(circle, #7b5cff, transparent 70%); inset-block-end: -16vmax; inset-inline-end: -12vmax; animation: aurora-b 27s ease-in-out infinite; }
.login-aurora span:nth-child(3) { background: radial-gradient(circle, #2ecc8f, transparent 70%); inset-block-start: 30%; inset-inline-start: 35%; opacity: .3; animation: aurora-c 33s ease-in-out infinite; }

@keyframes aurora-a {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
    50% { transform: translate3d(12vmax, 8vmax, 0) scale(1.15); }
}

@keyframes aurora-b {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1.1); }
    50% { transform: translate3d(-14vmax, -9vmax, 0) scale(.9); }
}

@keyframes aurora-c {
    0%, 100% { transform: translate3d(0, 0, 0) scale(.9); }
    33% { transform: translate3d(-10vmax, 7vmax, 0) scale(1.2); }
    66% { transform: translate3d(9vmax, -8vmax, 0) scale(1); }
}

.login-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 380px;
    background: color-mix(in srgb, var(--surface) 82%, transparent);
    backdrop-filter: blur(18px);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 1.6rem;
    box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
    animation: login-card-in .55s ease-out both;
}

@keyframes login-card-in {
    from { opacity: 0; transform: translateY(14px); }
    to { opacity: 1; transform: none; }
}

.login-card h1 { text-align: center; margin-block-end: 1.2rem; }
.login-card label { margin-block-start: .8rem; }
.login-card input { width: 100%; }
.login-card button { width: 100%; margin-block-start: 1.2rem; }

.login-meta { margin-block-start: 1.3rem; padding-block-start: .9rem; border-block-start: 1px solid var(--border); text-align: center; font-size: .8rem; color: var(--muted); }
.login-meta p { margin: .15rem 0; }
.login-meta .version { font-variant-numeric: tabular-nums; }

/* Backgrounds that drift for half a minute are exactly the kind of motion that triggers
   vestibular discomfort, so honour the system preference and hold everything still. */
@media (prefers-reduced-motion: reduce) {
    .login-aurora span, .login-card { animation: none; }
}

/* ---------- Responsive ---------- */

/* The menu button, the scrim and the close control exist only for the drawer. On the wide
   layout the sidebar is always on screen, so all three are meaningless and hidden. */
.yasna-nav-toggle { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.yasna-nav-scrim, .yasna-nav-open, .yasna-nav-close { display: none; }

@media (max-width: 860px) {
    .yasna-shell { flex-direction: column; overflow-x: clip; }

    /* ---- Drawer ----
       The sidebar is taken out of the flow and slid off-canvas instead of being reduced to a
       horizontal scroller. A row of links that scrolls sideways hides half the menu and gives
       no sign there is more; a drawer is what a phone user already expects, and every item is
       reachable by scrolling vertically inside it. */
    .yasna-sidebar {
        position: fixed;
        inset-block: 0;
        inset-inline-start: 0;
        z-index: 60;
        width: min(84vw, 20rem);
        flex: none;
        flex-direction: column;
        align-items: stretch;
        gap: .15rem;
        padding: .9rem;
        overflow-y: auto;
        overscroll-behavior: contain;
        border-inline-end: 1px solid var(--border);
        border-block-end: none;
        background: var(--surface);
        transform: translateX(-104%);
        visibility: hidden;
        pointer-events: none;
        transition: transform .22s ease, visibility .22s;
        /* Clears the notch and the home indicator when installed as an app. */
        padding-block-start: calc(.9rem + env(safe-area-inset-top));
        padding-block-end: calc(.9rem + env(safe-area-inset-bottom));
    }

    /* Mirrored for a right-to-left UI, so the drawer comes from the side the text starts on. */
    [dir="rtl"] .yasna-sidebar { transform: translateX(104%); }

    .yasna-nav-toggle:checked ~ .yasna-sidebar {
        transform: translateX(0);
        visibility: visible;
        pointer-events: auto;
    }
    [dir="rtl"] .yasna-nav-toggle:checked ~ .yasna-sidebar { transform: translateX(0); }

    .yasna-sidebar nav { flex-direction: column; gap: .15rem; }
    .yasna-sidebar nav a { padding: .6rem .7rem; }

    .yasna-nav-scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 55;
        background: rgb(15 23 42 / .45);
        opacity: 0;
        pointer-events: none;
        transition: opacity .22s ease;
    }

    .yasna-nav-toggle:checked ~ .yasna-nav-scrim { opacity: 1; pointer-events: auto; }

    /* The hamburger, sized for a thumb rather than a cursor: 44px is the smallest target
       that is reliably tappable. */
    .yasna-nav-open {
        display: flex;
        flex-direction: column;
        justify-content: center;
        gap: 4px;
        width: 44px;
        height: 44px;
        margin-inline-end: .4rem;
        cursor: pointer;
        flex: none;
    }

    .yasna-nav-open span {
        display: block;
        height: 2px;
        border-radius: 2px;
        background: var(--text);
    }

    .yasna-nav-open span:nth-child(2) { width: 70%; }

    .yasna-nav-close {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 44px;
        height: 44px;
        flex: none;
        margin-inline-start: auto;
        border-radius: 8px;
        font-size: 1.3rem;
        line-height: 1;
        color: var(--muted);
        cursor: pointer;
    }

    .yasna-nav-close:hover { color: var(--text); background: var(--surface-2); }

    .yasna-brand-row { display: flex; align-items: center; gap: .5rem; }
    .yasna-brand { display: flex; align-items: center; gap: .5rem; flex: 1; min-width: 0; }
    .yasna-brand span { flex: 1; }

    .yasna-content { padding: 1rem; }
    .yasna-topbar { padding: .65rem 1rem; flex-wrap: wrap; gap: .5rem; }
    .yasna-topbar-title { flex: 1; min-width: 0; }
    .yasna-topbar .who { min-width: 0; overflow-wrap: anywhere; }
    .yasna-content { overflow-wrap: anywhere; }
}

/* ---------- Install prompt ---------- */

.yasna-install {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin: .6rem 1rem 0;
    padding: .6rem .75rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
}

.yasna-install[hidden] { display: none; }
.yasna-install img { border-radius: 8px; flex: none; }
.yasna-install-text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.yasna-install-text span { color: var(--muted); font-size: .82rem; }
.yasna-install button { white-space: nowrap; }

@media (max-width: 520px) {
    .yasna-install { flex-wrap: wrap; }
    .yasna-install-text { flex-basis: 100%; order: 3; }
}

/* ---------- Localisation ---------- */

/* Digits stay Latin in every language. Tenant codes, token prefixes, client IDs, and
   timestamps are copied, pasted, and compared by operators; rendering them in Eastern
   Arabic numerals would make that error-prone for no readability gain. */
:root { font-variant-numeric: lining-nums; }

code, .secret-value, table td, .stat .value {
    font-variant-numeric: tabular-nums lining-nums;
}

/* Identifiers and secrets are Latin text and must read left-to-right even inside an
   RTL page, or the characters appear reordered and a copied value is wrong. */
code, .secret-value {
    direction: ltr;
    unicode-bidi: embed;
    text-align: start;
}

[dir="rtl"] code,
[dir="rtl"] .secret-value {
    text-align: left;
}

.lang-switch { display: flex; gap: .3rem; align-items: center; }

.lang-switch a {
    color: var(--muted);
    font-size: .82rem;
    padding: .2rem .45rem;
    border-radius: 6px;
    border: 1px solid transparent;
}

.lang-switch a:hover { background: var(--surface-2); text-decoration: none; }
.lang-switch a.current { color: var(--text); border-color: var(--border); background: var(--surface-2); }
/* ---------- Credit, keys, and the assistant ---------- */

/* The toggle row keeps its label and its control together; the wrapping .checks grid already
   drives the column layout, so these only need the inline alignment. */
.row-check { display: flex; align-items: center; gap: .45rem; color: var(--text); font-size: .88rem; }
.row-check input { width: auto; }

.row-actions { display: flex; gap: .35rem; flex-wrap: wrap; }
.row-actions button { padding: .25rem .55rem; font-size: .8rem; }

/* A card that has to be noticed: the one-time credential, and the ledger-drift warning. */
.card.highlight { border-color: var(--ok); box-shadow: 0 0 0 1px rgba(46, 204, 143, .25); }

/* ---------- The assistant playground ---------- */

.chat-controls {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    align-items: center;
    margin-block-end: .9rem;
}

.chat-controls label { margin: 0; font-size: .85rem; }
.chat-controls select { width: auto; min-width: 9rem; }
.chat-controls input[type="number"] { width: 4.25rem; }
.chat-controls .muted { margin-inline-start: auto; }

.chat-transcript {
    display: flex;
    flex-direction: column;
    gap: .85rem;
    max-height: 26rem;
    overflow-y: auto;
    padding: .5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg);
}

.chat-turn { display: flex; flex-direction: column; gap: .3rem; max-width: 90%; }
.chat-turn.user { align-self: flex-end; align-items: flex-end; }
.chat-turn.assistant { align-self: flex-start; align-items: flex-start; }

.chat-bubble {
    padding: .55rem .8rem;
    border-radius: 12px;
    border: 1px solid var(--border);
    background: var(--surface-2);
    max-width: 100%;
    overflow-x: auto;
}

.chat-turn.user .chat-bubble { background: var(--surface); border-color: var(--muted); }

/* pre, not a div: an answer that contains aligned numbers or a short list has to keep its
   line breaks, and the model is asked for terse tabular prose rather than markdown. */
.chat-text {
    margin: 0;
    white-space: pre-wrap;
    word-break: break-word;
    font-family: inherit;
    font-size: .92rem;
    line-height: 1.55;
}

.chat-image { max-width: 15rem; max-height: 12rem; border-radius: 10px; border: 1px solid var(--border); }
.chat-billing { font-size: .76rem; color: var(--muted); }
.chat-attachment { display: flex; align-items: center; gap: .6rem; margin-block-start: .6rem; }
.chat-attachment img { max-height: 5rem; border-radius: 8px; border: 1px solid var(--border); }

/* Several attachments wrap rather than overflow. The compose box keeps its height as more
   images are added, so the send button does not move out from under the pointer. */
.chat-attachments { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin-block-start: .6rem; }

/* The remove control sits on the thumbnail, so a row of images stays one row of images
   instead of each growing a button beside it. */
.chat-attachments .chat-attachment { position: relative; margin-block-start: 0; }
.chat-attachments .chat-attachment button {
  position: absolute; inset-block-start: -.45rem; inset-inline-end: -.45rem;
  width: 1.35rem; height: 1.35rem; padding: 0; line-height: 1;
  border-radius: 50%; border: 1px solid var(--border);
  background: var(--surface); color: var(--muted); cursor: pointer;
}
.chat-attachments .chat-attachment button:hover { color: #b91c1c; border-color: #b91c1c; }

.chat-turn-images { display: flex; flex-wrap: wrap; gap: .4rem; }

/* ---------- Composer attachments ---------- */

/* The attach control is a full drop zone, not a small label. It is the input's own label, so
   clicking anywhere in it opens the picker, and it is large enough to be an obvious target on
   a phone. */
.composer-attach {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .55rem .8rem;
    border: 1.5px dashed var(--border);
    border-radius: 10px;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}

.composer-attach:hover { border-color: var(--accent); background: var(--surface-2); }

/* InputFile renders a wrapping div around the input, and the input is what must be hidden.
   display:contents takes the wrapper out of the layout while leaving the input in the DOM as a
   descendant of the label, which is what keeps the label's native "click anywhere to open the
   picker" behaviour working; display:none on the wrapper can break that association. */
.composer-attach > div { display: contents; }

.composer-attach input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.composer-attach-icon { font-size: 1.15rem; line-height: 1; color: var(--accent); }
.composer-attach-text { display: flex; flex-direction: column; line-height: 1.25; }
.composer-attach-text small { color: var(--muted); font-size: .76rem; }

.composer-attachments {
    margin-block-start: .6rem;
    padding: .6rem .7rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface-2);
}

.composer-attachments-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .86rem;
}

.composer-attachments-head .link { margin-inline-start: auto; }

.composer-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: .55rem;
    list-style: none;
    margin: .6rem 0 0;
    padding: 0;
}

.composer-thumb { position: relative; }
.composer-thumb img {
    display: block;
    width: 4.5rem;
    height: 4.5rem;
    object-fit: cover;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--surface);
}

/* A tick on every thumbnail, not just a border colour: it answers "is this one in?" without
   the operator having to interpret a shade. */
.composer-thumb-check {
    position: absolute;
    inset-block-end: .2rem;
    inset-inline-start: .2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.15rem;
    height: 1.15rem;
    border-radius: 50%;
    background: #15803d;
    color: #fff;
    font-size: .72rem;
    line-height: 1;
}

.composer-thumb-remove {
    position: absolute;
    inset-block-start: -.4rem;
    inset-inline-end: -.4rem;
    width: 1.35rem;
    height: 1.35rem;
    padding: 0;
    line-height: 1;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--muted);
    cursor: pointer;
}

.composer-thumb-remove:hover:not(:disabled) { color: #b91c1c; border-color: #b91c1c; }
.composer-thumb-remove:disabled { opacity: .5; cursor: default; }

.composer-attachments-note { margin: .55rem 0 0; color: var(--muted); font-size: .78rem; }

/* An attachment refusal sits directly above the control that caused it, with a tone that reads
   as a problem rather than a note: the whole complaint was that a refused image looked like a
   button doing nothing, so this must not blend into the page. */
.composer-alert {
    margin: 0 0 .6rem;
    border-inline-start: 3px solid #b91c1c;
    font-size: .85rem;
}

/* ---------- Rendered Markdown in a reply ---------- */

.chat-markdown { white-space: normal; }
.chat-markdown p { margin: 0 0 .6rem; }
.chat-markdown p:last-child { margin-block-end: 0; }
.chat-markdown strong { font-weight: 600; }

.chat-markdown h3,
.chat-markdown h4,
.chat-markdown h5,
.chat-markdown h6 {
    margin: .9rem 0 .4rem;
    font-size: .98rem;
    line-height: 1.35;
}

.chat-markdown h3:first-child,
.chat-markdown h4:first-child { margin-block-start: 0; }

.chat-markdown ul,
.chat-markdown ol { margin: 0 0 .6rem; padding-inline-start: 1.3rem; }
.chat-markdown li { margin-block-end: .15rem; }

.chat-markdown code {
    padding: .1rem .3rem;
    border-radius: 5px;
    background: var(--surface-2);
    font-size: .85em;
}

.chat-code {
    position: relative;
    margin: 0 0 .6rem;
    padding: .6rem .7rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface-2);
    overflow-x: auto;
    white-space: pre;
    font-size: .84rem;
    line-height: 1.45;
}

.chat-code-lang {
    display: block;
    margin-block-end: .3rem;
    color: var(--muted);
    font-size: .72rem;
    text-transform: lowercase;
}

/* Tables scroll sideways rather than wrapping, because a wrapped numeric column is
   unreadable and the numbers are the point of the table. */
.chat-table-wrap {
    margin: 0 0 .7rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    border: 1px solid var(--border);
    border-radius: 8px;
}

.chat-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .86rem;
    white-space: nowrap;
}

.chat-table th,
.chat-table td {
    padding: .45rem .6rem;
    text-align: start;
    border-block-end: 1px solid var(--border);
}

.chat-table th {
    background: var(--surface-2);
    font-weight: 600;
    color: var(--muted);
    font-size: .8rem;
}

.chat-table tr:last-child td { border-block-end: none; }

/* Right-to-left text keeps its own direction even inside a left-to-right table, so a Persian
   heading and a Latin drive letter can sit in the same row without either being reversed. */
.chat-markdown, .chat-table th, .chat-table td { unicode-bidi: plaintext; }

/* A small rotating ring for work in progress. Used beside "preparing" and on the send button,
   so the operator can see that something is happening rather than a control that looks dead. */
.spinner {
    display: inline-block;
    width: .85rem;
    height: .85rem;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: yasna-spin .7s linear infinite;
}

@keyframes yasna-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    .spinner { animation-duration: 2s; }
}

/* ---------- Conversation history sidebar ---------- */

.chat-layout {
    display: grid;
    grid-template-columns: 18rem minmax(0, 1fr);
    gap: 1rem;
    align-items: start;
}

.chat-history {
    position: sticky;
    /* Sticks below the topbar rather than at the viewport top, so the head of the list stays
       visible while the transcript scrolls. */
    top: 4.5rem;
    max-height: calc(100vh - 6rem);
    display: flex;
    flex-direction: column;
    padding: .7rem;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
}

.chat-history-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    padding-block-end: .5rem;
    border-block-end: 1px solid var(--border);
}

/* The list scrolls inside the column. The page itself does not grow with the number of
   conversations, which is what kept pushing the chat below the fold. */
.chat-history .conversation-list {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    margin-block-start: .5rem;
}

.chat-history-more {
    margin-block-start: .5rem;
    width: 100%;
    padding: .45rem;
    border: 1px dashed var(--border);
    border-radius: 8px;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
}

.chat-history-more:hover:not(:disabled) { color: var(--text); border-color: var(--accent); }
.chat-history-more:disabled { opacity: .6; cursor: default; }

.chat-main { min-width: 0; }

/* Below the sidebar breakpoint the history moves above the chat and the inner scroll is
   released, because a sticky full-height column on a phone is a column the operator cannot
   get past. */
@media (max-width: 1100px) {
    .chat-layout { grid-template-columns: minmax(0, 1fr); }

    .chat-history {
        position: static;
        max-height: 16rem;
    }
}

.chat-compose { margin-block-start: .8rem; }
.chat-compose textarea { width: 100%; resize: vertical; font-family: inherit; }

.file-label {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .3rem .6rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: .82rem;
    color: var(--muted);
    cursor: pointer;
}

.file-label input[type="file"] { display: none; }

/* ---------- Stored conversations ---------- */

/* A list of chats reads as a column of buttons rather than a table: each entry is one
   action, and the whole row is the target. */
.conversation-list { display: flex; flex-direction: column; gap: .35rem; }

.conversation-item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .15rem;
    width: 100%;
    padding: .5rem .65rem;
    text-align: start;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    cursor: pointer;
}

.conversation-item:hover { border-color: var(--accent, #4db7ea); }
.conversation-item.current { border-color: var(--ok); }

.conversation-title {
    font-size: .88rem;
    color: var(--text);
    /* A title is a truncated first question, so it can be long. One line keeps the list
       scannable and the full text is still in the transcript. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

/* ---------- What the assistant is doing ---------- */

/* The model's reasoning trace. Styled and bounded like the operation log beside it, because it
   is the other half of "what is it doing": one says what it thought, the other what it ran. */
.chat-thought {
    margin-block-end: .4rem;
    border: 1px dashed var(--border);
    border-radius: var(--radius);
    padding: .4rem .7rem;
    font-size: .8rem;
    color: var(--muted);
    background: var(--surface-2);
    max-width: 100%;
}

.chat-thought summary { cursor: pointer; }

.chat-thought-text {
    margin-block-start: .4rem;
    /* Bounded and scrollable: a thinking model can produce thousands of words, and the
       transcript must stay readable while the answer is still arriving. */
    max-height: 16rem;
    overflow-y: auto;
    white-space: pre-wrap;
    word-break: break-word;
    /* The trace is written in the language the question was asked in, but it is quoted text
       rather than part of the conversation, so it keeps its own reading direction. */
    direction: inherit;
    text-align: start;
}

.chat-activity {
    margin-block-end: .6rem;
    border: 1px dashed var(--border);
    border-radius: var(--radius);
    padding: .4rem .7rem;
    font-size: .8rem;
    color: var(--muted);
    background: var(--surface-2);
}

.chat-activity summary { cursor: pointer; }

.chat-activity ol {
    margin: .4rem 0 .1rem;
    padding-inline-start: 1.3rem;
    /* Bounded and scrollable: a turn can make dozens of calls, and the panel must not push
       the transcript off the screen while the answer is still arriving. */
    max-height: 11rem;
    overflow-y: auto;
    direction: ltr;
    text-align: left;
}

.chat-activity li { margin-block: .15rem; word-break: break-word; }

/* ---------- Watching a long turn ---------- */

/* A screen of its own.
   The browser's fullscreen API moves an element out of the page entirely — which is what "the
   chat page becomes fullscreen" means to the operator who asked for it — and the pinned
   fallback the script adds when the API is refused is styled the same way. Either path lays the
   conversation out identically: controls and composer keep their places, and the transcript
   takes everything in between. */
.card:fullscreen,
.chat-main:fullscreen,
.card.is-fullscreen,
.chat-main.is-fullscreen {
    display: flex;
    flex-direction: column;
    gap: .7rem;
    margin: 0;
    padding: 1rem 1.2rem;
    border: 0;
    border-radius: 0;
    background: var(--bg);
    overflow: hidden;
}

.card.is-fullscreen,
.chat-main.is-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 95;
}

.card:fullscreen .chat-transcript,
.chat-main:fullscreen .chat-transcript,
.card.is-fullscreen .chat-transcript,
.chat-main.is-fullscreen .chat-transcript {
    flex: 1;
    min-height: 0;
    max-height: none;
}

.card:fullscreen .chat-compose,
.chat-main:fullscreen .chat-compose,
.card.is-fullscreen .chat-compose,
.chat-main.is-fullscreen .chat-compose {
    flex: none;
}

.chat-fullscreen {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .3rem .7rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: transparent;
    color: var(--muted);
    font-size: .82rem;
    cursor: pointer;
}

.chat-fullscreen:hover { color: var(--text); border-color: var(--accent); }

/* The two panels get the same treatment: a real fullscreen element, filled by the panel with
   its own text scrolling inside it, and the pinned class for the fallback. */
.chat-thought:fullscreen,
.chat-activity:fullscreen,
.chat-thought.is-fullscreen,
.chat-activity.is-fullscreen {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: .9rem 1.1rem;
    border: 0;
    border-radius: 0;
    background: var(--surface);
    font-size: .85rem;
}

.chat-thought.is-fullscreen,
.chat-activity.is-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 95;
}

.chat-thought:fullscreen .chat-thought-text,
.chat-activity:fullscreen ol,
.chat-thought.is-fullscreen .chat-thought-text,
.chat-activity.is-fullscreen ol {
    flex: 1;
    min-height: 0;
    max-height: none;
    overflow-y: auto;
}

/* The two panels offer the same control: a trace and a log can both grow past the band the
   transcript gives them, and scrolling a box inside a box is how an operator stops reading
   either. One button, in the summary, so it sits where the panel's own title is. */
.chat-thought summary,
.chat-activity summary {
    display: flex;
    align-items: center;
    gap: .5rem;
}

.chat-thought summary > span,
.chat-activity summary > span { flex: 1; }

.panel-expand {
    flex: none;
    padding: 0 .35rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--muted);
    font-size: .8rem;
    line-height: 1.5;
    cursor: pointer;
}

.panel-expand:hover { color: var(--text); border-color: var(--accent); }

/* The assistant's plan. Above the operation log, because "how far along is it" is the question
   an operator asks before "what did each call return", and a plan read at a glance is a row of
   marks: a column of three localised words per row cannot be scanned while work is happening. */
.chat-todos {
    margin-block-end: .5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .45rem .7rem;
    background: var(--surface-2);
    font-size: .82rem;
}

.chat-todos-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
}

.chat-todos ul {
    list-style: none;
    margin: .4rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .2rem;
    /* Bounded for the same reason the operation log is: a long plan must not push the answer
       the operator is waiting for off the screen. */
    max-height: 12rem;
    overflow-y: auto;
}

.chat-todos li { display: flex; gap: .45rem; align-items: flex-start; color: var(--muted); }

.chat-todos .todo-mark { flex: none; width: 1rem; text-align: center; }
.chat-todos .todo-title { word-break: break-word; }

.chat-todos li.todo-in_progress { color: var(--text); }
.chat-todos li.todo-in_progress .todo-mark { color: var(--accent); }
.chat-todos li.todo-completed .todo-mark { color: #4ea36a; }
.chat-todos li.todo-completed .todo-title { text-decoration: line-through; opacity: .75; }

/* The notes the operator typed into a running turn, shown where the turn is: a note that
   vanished into a text box would be indistinguishable from one that was never sent. */
.chat-notes { display: flex; flex-wrap: wrap; gap: .3rem; margin-block-end: .4rem; }

.chat-note-chip {
    max-width: 100%;
    padding: .15rem .55rem;
    border: 1px solid var(--accent);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--muted);
    font-size: .76rem;
    overflow-wrap: anywhere;
}

.chat-note-chip.is-read { border-color: #4ea36a; }
.chat-note-chip.is-waiting { border-style: dashed; opacity: .8; }

/* The box itself. Present only while a turn runs, because a note is an instruction to work in
   progress and there is nothing to instruct once the answer is written. */
.chat-note-compose {
    margin-block-start: .6rem;
    padding: .55rem .7rem;
    border: 1px dashed var(--border);
    border-radius: 10px;
    background: var(--surface);
}

.chat-note-head {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: baseline;
    margin-block-end: .35rem;
}

.chat-note-row { display: flex; gap: .5rem; align-items: center; }
.chat-note-row input[type="text"] { flex: 1; min-width: 0; }



/* ---------- The API reference ---------- */

/* A documented endpoint reads as a unit: the verb, the path, then its prose and examples.
   The left rule is what makes a long reference scannable when several endpoints follow one
   another. */
.endpoint {
    border-inline-start: 3px solid var(--border);
    padding-inline-start: 1rem;
    margin-block: 1.4rem;
}

.endpoint-head { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-block-end: .5rem; }
.endpoint-head code { font-size: .92rem; }

.verb {
    display: inline-block;
    min-width: 3.2rem;
    padding: .15rem .45rem;
    border-radius: 6px;
    font-size: .74rem;
    font-weight: 700;
    text-align: center;
    letter-spacing: .03em;
}

.verb.get { color: var(--ok); border: 1px solid var(--ok); }
.verb.post { color: var(--accent, #4db7ea); border: 1px solid var(--accent, #4db7ea); }

/* The examples are literal text, so they scroll rather than wrap: a wrapped JSON line is
   harder to read than a scrolled one, and it stops a long base64 image from stretching the
   page. */
.code-block {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: .7rem .85rem;
    overflow-x: auto;
    font-size: .8rem;
    line-height: 1.5;
    margin-block: .5rem .9rem;
    white-space: pre;
    direction: ltr;
    text-align: left;
}

/* Raw stream frames are numerous and short; capping the height keeps the console usable
   without hiding that there are more of them. */
.code-block.frame { max-height: 12rem; margin-block: .3rem; }

/* A skill's instructions are prose: wrapped, and capped so the file list stays in reach. */
.code-block.wrap { white-space: pre-wrap; max-height: 32rem; overflow-y: auto; }

.api-docs h3 { margin-block: 1.1rem .3rem; font-size: .95rem; }

/* ---------- Security operations centre ---------- */

/* One severity scale, used by every part of the console: a chip, a bar, a dot and a tile that
   mean the same thing must be the same colour, or the reader has to learn the page four times.
   The value is carried by a custom property so each part decides how to paint it. */
.sev-info, .sev-low { --sev: #4f8cff; }
.sev-medium { --sev: #f0b429; }
.sev-high { --sev: #ff8a3d; }
.sev-critical { --sev: #ff4d6d; }
.sev-ok { --sev: var(--ok); }

.soc { --soc-glow: rgba(79, 140, 255, .14); }

.soc-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 1.1rem 1.25rem;
    margin-block-end: 1rem;
    border: 1px solid var(--border);
    border-radius: 14px;
    background:
        radial-gradient(1200px 220px at 0% 0%, var(--soc-glow), transparent 70%),
        linear-gradient(180deg, var(--surface-2), var(--surface));
}

.soc-head-main { display: flex; align-items: center; gap: .9rem; }
.soc-head-main h1 { margin: 0; }
.soc-head-main p { margin: .15rem 0 0; }
.soc-head-side { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }

/* The beacon. It pulses only while the page is live, so a frozen console is visibly frozen. */
.soc-pulse {
    position: relative;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--ok);
    box-shadow: 0 0 0 0 rgba(46, 204, 143, .6);
    animation: soc-pulse 1.8s ease-out infinite;
    flex: none;
}

.soc-pulse.idle { background: var(--muted); animation: none; }

@keyframes soc-pulse {
    0% { box-shadow: 0 0 0 0 rgba(46, 204, 143, .55); }
    70% { box-shadow: 0 0 0 12px rgba(46, 204, 143, 0); }
    100% { box-shadow: 0 0 0 0 rgba(46, 204, 143, 0); }
}

.soc-level {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 8.5rem;
    padding: .45rem .9rem;
    border-radius: 10px;
    border: 1px solid var(--sev);
    color: var(--sev);
    background: color-mix(in srgb, var(--sev) 12%, transparent);
}

.soc-level-label { font-size: .72rem; letter-spacing: .06em; opacity: .85; }
.soc-level-value { font-size: 1.25rem; font-weight: 700; }
.soc-level.sev-critical { animation: soc-alarm 1.2s ease-in-out infinite; }

@keyframes soc-alarm {
    50% { box-shadow: 0 0 22px color-mix(in srgb, var(--sev) 55%, transparent); }
}

.soc-clock { display: flex; flex-direction: column; align-items: flex-end; font-size: .82rem; color: var(--muted); }
.soc-live { color: var(--danger); font-weight: 700; letter-spacing: .08em; font-size: .7rem; animation: soc-blink 1.4s steps(2) infinite; }

@keyframes soc-blink { 50% { opacity: .35; } }

.soc-kpis {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: .7rem;
    margin-block-end: 1rem;
}

.soc-kpi {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: .15rem;
    padding: .75rem .9rem;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    overflow: hidden;
}

/* The accent rail. Present on every tile so the grid stays aligned; coloured only when the
   number is one that deserves attention. */
.soc-kpi::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: 3px;
    background: var(--sev, var(--border));
}

.soc-kpi .k { color: var(--muted); font-size: .76rem; }
.soc-kpi .v { font-size: 1.7rem; font-weight: 600; line-height: 1.15; color: var(--sev, var(--text)); }
.soc-kpi .sub { font-size: .74rem; color: var(--sev); }

.soc-grid {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    gap: 1rem;
    margin-block-end: 1rem;
}

@media (max-width: 1000px) {
    .soc-grid { grid-template-columns: minmax(0, 1fr); }
}

.soc-panel {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    padding: .9rem 1rem;
    min-width: 0;
}

.soc-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    flex-wrap: wrap;
    margin-block-end: .7rem;
}

.soc-panel-head h2 { margin: 0; display: flex; align-items: center; gap: .5rem; }
.soc-empty { color: var(--muted); padding: 1.2rem .2rem; text-align: center; font-size: .9rem; }
.soc-seen { font-size: .88rem; }

button.mini, a.mini {
    padding: .2rem .6rem;
    font-size: .78rem;
    border-radius: 999px;
    border: 1px solid var(--border);
    color: var(--muted);
    background: transparent;
}

a.mini:hover, button.mini:hover { color: var(--text); border-color: var(--accent); text-decoration: none; }

.soc-legend { display: flex; align-items: center; gap: .35rem; font-size: .76rem; color: var(--muted); flex-wrap: wrap; }
.soc-legend .dot { margin-inline-start: .55rem; }

.dot {
    display: inline-block;
    width: .6rem;
    height: .6rem;
    border-radius: 50%;
    background: var(--sev, var(--muted));
    flex: none;
}

.soc-chart { width: 100%; height: 190px; display: block; }
.soc-chart .grid { stroke: var(--border); stroke-width: .5; stroke-dasharray: 2 3; }
.soc-chart .slot { fill: transparent; }
.soc-chart g:hover .slot { fill: rgba(255, 255, 255, .04); }
.soc-chart .bar { fill: var(--sev); }
.soc-chart .axis { fill: var(--muted); font-size: 7px; }

.soc-sources { list-style: none; margin: 0; padding: 0; display: grid; gap: .4rem; }

.soc-sources li {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .4rem .55rem;
    border-radius: 8px;
    background: var(--surface-2);
}

.soc-sources code { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; direction: ltr; text-align: start; }
.soc-sources .count { font-weight: 600; color: var(--sev, var(--text)); }

.soc-feed { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; max-height: 34rem; overflow-y: auto; }

.soc-feed li {
    border: 1px solid var(--border);
    border-inline-start: 3px solid var(--sev);
    border-radius: 8px;
    background: var(--surface-2);
}

.soc-feed li.acked { opacity: .6; }

.soc-feed summary {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .5rem .7rem;
    cursor: pointer;
    list-style: none;
    flex-wrap: wrap;
}

.soc-feed summary::-webkit-details-marker { display: none; }

.soc-feed .chip {
    flex: none;
    min-width: 3.6rem;
    text-align: center;
    padding: .1rem .5rem;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 600;
    color: var(--sev);
    border: 1px solid var(--sev);
    background: color-mix(in srgb, var(--sev) 12%, transparent);
}

.soc-feed .title { flex: 1; min-width: 12rem; font-size: .9rem; }
.soc-feed .meta { display: flex; align-items: center; gap: .6rem; color: var(--muted); font-size: .76rem; flex-wrap: wrap; }
.soc-feed .body { padding: 0 .7rem .65rem; }

.soc-feed pre, .agent-events pre {
    margin: .3rem 0 .5rem;
    padding: .6rem .7rem;
    border-radius: 8px;
    background: var(--bg);
    border: 1px solid var(--border);
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: .78rem;
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 22rem;
    overflow-y: auto;
}

.soc-runs { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }

.soc-runs a {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: .55rem;
    align-items: center;
    padding: .45rem .6rem;
    border-radius: 8px;
    background: var(--surface-2);
    color: var(--text);
}

.soc-runs a:hover { text-decoration: none; outline: 1px solid var(--accent); }
.soc-runs .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .9rem; }
.soc-runs .meta { grid-column: 2; color: var(--muted); font-size: .75rem; display: flex; gap: .5rem; flex-wrap: wrap; }

/* A run's state as a single light: the same five states wherever a run is listed. */
.run-state {
    display: inline-block;
    width: .7rem;
    height: .7rem;
    border-radius: 50%;
    background: var(--muted);
    flex: none;
}

.run-state.run-running { background: var(--warn); animation: soc-blink 1s steps(2) infinite; }
.run-state.run-completed { background: var(--ok); }
.run-state.run-failed { background: var(--danger); }
.run-state.run-steplimitreached { background: #ff8a3d; }

/* ---------- Scheduled agents: the trace of a run ---------- */

.scope-picker { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; }
.scope-picker label { margin: 0; }
.scope-picker select { max-width: 28rem; }

.section-title { margin-block-start: 2rem; padding-block-end: .4rem; border-block-end: 1px solid var(--border); }
tr.selected-row { background: var(--surface-2); outline: 1px solid var(--accent); outline-offset: -1px; }
td.wrap { white-space: normal; min-width: 14rem; max-width: 34rem; }

.sms-preview {
    margin-block: .8rem .2rem;
    padding: .6rem .8rem;
    border-radius: 12px 12px 12px 2px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    max-width: 26rem;
    white-space: pre-wrap;
}

.agent-lane {
    border: 1px solid var(--border);
    border-inline-start: 3px solid var(--accent);
    border-radius: 12px;
    background: var(--surface-2);
    margin-block-end: .9rem;
    overflow: hidden;
}

/* A sub-agent is drawn inset and in another colour, so the delegation reads as a tree without
   the reader having to follow identifiers. */
.agent-lane.sub { margin-inline-start: 1.6rem; border-inline-start-color: #b48cff; }
.agent-lane.running { box-shadow: 0 0 0 1px color-mix(in srgb, var(--warn) 45%, transparent); }

.agent-lane > header {
    display: flex;
    align-items: center;
    gap: .7rem;
    flex-wrap: wrap;
    padding: .6rem .85rem;
    background: var(--surface);
    border-block-end: 1px solid var(--border);
}

.agent-icon { color: var(--accent); font-size: 1.1rem; }
.agent-lane.sub .agent-icon { color: #b48cff; }
.agent-name { display: flex; flex-direction: column; flex: 1; min-width: 12rem; }
.agent-name .muted { font-size: .76rem; }
.agent-times { display: flex; gap: .9rem; flex-wrap: wrap; font-size: .76rem; color: var(--muted); }

.agent-events { list-style: none; margin: 0; padding: .5rem .85rem .7rem; display: grid; gap: .3rem; }

.agent-events .ev {
    display: grid;
    grid-template-columns: 9.5rem minmax(0, 1fr);
    gap: .6rem;
    align-items: start;
    font-size: .86rem;
}

@media (max-width: 720px) {
    .agent-events .ev { grid-template-columns: minmax(0, 1fr); gap: .1rem; }
    .agent-lane.sub { margin-inline-start: .6rem; }
}

.agent-events time { color: var(--muted); font-size: .74rem; padding-block-start: .15rem; }
.agent-events summary { cursor: pointer; }
.agent-events .ev-working { color: var(--warn); grid-template-columns: minmax(0, 1fr); }

.ev-tag {
    display: inline-block;
    min-width: 3.4rem;
    text-align: center;
    padding: .05rem .45rem;
    margin-inline-end: .35rem;
    border-radius: 6px;
    font-size: .72rem;
    border: 1px solid var(--border);
    color: var(--muted);
}

.ev-thought .ev-tag { color: #b9a3ff; border-color: #6d5bb0; }
.ev-thought summary { color: var(--muted); font-style: italic; }
.ev-tool_started .ev-tag { color: var(--accent); border-color: var(--accent); }
.ev-tool_completed .ev-tag { color: var(--ok); border-color: var(--ok); }
.ev-tool_failed .ev-tag, .ev-error .ev-tag { color: var(--danger); border-color: var(--danger); }
.ev-agent_started .ev-tag, .ev-agent_finished .ev-tag { color: var(--text); border-color: var(--text); }

.ev-report { border: 1px solid var(--border); border-radius: 10px; background: var(--surface); padding: .6rem .8rem; }
.ev-report .chat-markdown { margin-block-start: .4rem; }

/* ------------------------------------------------------------------------------------
   The SEO expert
   
   The change queue, the findings list and the connections screen. Only what the shared
   classes above do not already carry, so a change to the portal's look reaches them too.
   ------------------------------------------------------------------------------------ */

/* SEO pages: the change queue, the findings list, and the connections screen.
 *
 * Only what the shared stylesheet does not already carry, and only what these three pages need.
 * The portal's existing classes — card, table-wrap, badge, alert, form-grid, row-actions — do the
 * rest, so a change to the portal's look reaches these pages too.
 */

/* The before-and-after pair is the point of the queue, so the two values are told apart at a
   glance rather than by reading the column heading twice. */
.seo-change-list { display: grid; gap: 1rem; }
.seo-change-card { min-width: 0; border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; }
.seo-change-heading { display: flex; align-items: center; gap: .65rem; min-width: 0; }
.seo-change-heading a { overflow-wrap: anywhere; }
.seo-change-values { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; margin-block: 1rem; }
.seo-change-value { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.8; padding: .75rem; margin-block-start: .5rem; border-radius: var(--radius); unicode-bidi: plaintext; }
.seo-change-value.before { background: color-mix(in srgb, currentColor 6%, transparent); }
.seo-change-value.after { background: color-mix(in srgb, #1f9d55 12%, transparent); border-inline-start: 3px solid #1f9d55; }
@media (max-width: 640px) { .seo-change-values { grid-template-columns: 1fr; } }

code.before,
code.after {
    display: block;
    padding: 0.15rem 0.35rem;
    border-radius: 4px;
    font-size: 0.85em;
    line-height: 1.4;
    white-space: pre-wrap;
    word-break: break-word;
    max-width: 32ch;
}

code.before {
    background: color-mix(in srgb, currentColor 6%, transparent);
    text-decoration: line-through;
    text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
    opacity: 0.75;
}

code.after {
    background: color-mix(in srgb, #1f9d55 12%, transparent);
    border-inline-start: 3px solid #1f9d55;
}

/* A finding is a block rather than a table row: the evidence and the recommendation are long
   enough that a table would either truncate them or scroll sideways on a phone. */
.finding {
    padding: 0.9rem 0;
    border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}

.finding:last-child {
    border-bottom: none;
}

.finding.closed {
    opacity: 0.6;
}

.finding-detail {
    display: grid;
    grid-template-columns: minmax(8rem, max-content) 1fr;
    gap: 0.35rem 1rem;
    margin: 0.6rem 0;
}

.finding-detail dt {
    font-weight: 600;
    opacity: 0.85;
}

.finding-detail dd {
    margin: 0;
    min-width: 0;
    overflow-wrap: anywhere;
}

.finding-detail code {
    display: inline-block;
    padding: 0.1rem 0.3rem;
    border-radius: 4px;
    background: color-mix(in srgb, currentColor 6%, transparent);
    font-size: 0.85em;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* The counters on the overview are read as a row of numbers, not as a table. */
.kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: 0.75rem;
    margin: 0.75rem 0;
}

.kpi {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0.6rem 0.75rem;
    border-radius: 6px;
    background: color-mix(in srgb, currentColor 5%, transparent);
}

.kpi span {
    font-size: 0.8em;
    opacity: 0.8;
}

.kpi strong {
    font-size: 1.35em;
    line-height: 1.1;
}

/* A selected row is where the detail below it belongs, so it is marked. */
tr.selected {
    background: color-mix(in srgb, #2b6cb0 10%, transparent);
}

@media (max-width: 640px) {
    .finding-detail {
        grid-template-columns: 1fr;
    }

    code.before,
    code.after {
        max-width: none;
    }
}

/* ---------------------------------------------------------------------------------------
   Online radio
   --------------------------------------------------------------------------------------- */
.radio-station-chips { display: flex; flex-wrap: wrap; gap: .5rem; margin-block-end: .8rem; }
.radio-chip { display: inline-flex; align-items: center; gap: .45rem; border-radius: 999px; padding: .35rem .9rem; }
.radio-chip.selected { border-color: var(--accent); color: var(--text); background: var(--surface-2); }
.dot.on { --sev: var(--ok); box-shadow: 0 0 8px var(--ok); }
.dot.off { --sev: var(--muted); }
.radio-live-head { display: flex; justify-content: space-between; align-items: start; gap: 1rem; flex-wrap: wrap; }
.radio-live-head h2 { margin: 0 0 .4rem; }
.radio-live-head .badge { margin-inline-end: .4rem; }
.radio-now { display: flex; align-items: center; justify-content: space-between; gap: 1.2rem; flex-wrap: wrap; margin-block: 1rem; padding: 1rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); }
.radio-now-main { flex: 1 1 260px; min-width: 0; }
.radio-now-title { font-size: 1.25rem; margin-block: .25rem; overflow-wrap: anywhere; }
.radio-now audio { width: min(360px, 100%); }
.radio-upnext { margin-top: .4rem; }
.radio-notice { margin-top: .6rem; margin-bottom: 0; }
.radio-links { display: grid; gap: .5rem; margin-block-end: .6rem; }
.radio-tabs { display: flex; flex-wrap: wrap; gap: .35rem; margin-block: 1rem; border-bottom: 1px solid var(--border); padding-bottom: .5rem; }
.radio-tabs button { border-radius: 999px; }
.radio-tabs button.selected { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; }
.radio-prompt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: .6rem; margin-block: .8rem; }
.radio-prompt { display: flex; flex-direction: column; gap: .3rem; padding: .7rem .8rem; border: 1px solid var(--border); border-radius: var(--radius); cursor: pointer; font-size: .85rem; }
.radio-prompt input { align-self: flex-start; }
.radio-prompt.selected { border-color: var(--accent); background: var(--surface-2); }
.radio-prompt .muted { font-size: .78rem; line-height: 1.6; }
.form-grid .span-2 { grid-column: 1 / -1; }
.toolbar .grow { flex: 1 1 260px; }
.toolbar .grow input { width: 100%; }
.radio-script { white-space: pre-wrap; font-family: inherit; font-size: .85rem; line-height: 1.8; margin: .4rem 0 0; }
.radio-voices-details { margin-top: .8rem; }
.radio-voices-details summary { cursor: pointer; color: var(--accent); }
.radio-voice-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: .6rem; margin-top: .7rem; }
.radio-voice { border: 1px solid var(--border); border-radius: var(--radius); padding: .6rem .7rem; display: flex; flex-direction: column; gap: .45rem; }
.radio-voice.selected { border-color: var(--accent); background: var(--surface-2); }
.radio-voice-head { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.radio-voice audio { width: 100%; height: 34px; }

/* The composer's voice button. Red and pulsing while the microphone is live, so a recording
   left running is obvious. */
.composer-voice { display: inline-flex; align-items: center; gap: .45rem; }
.composer-voice-icon { font-size: 1.05rem; line-height: 1; }
.composer-voice.recording { border-color: #c62828; color: #c62828; }
.composer-voice-dot {
    width: .65rem;
    height: .65rem;
    border-radius: 50%;
    background: #c62828;
    animation: composer-voice-pulse 1s ease-in-out infinite;
}

@keyframes composer-voice-pulse { 50% { opacity: .25; } }

@media (prefers-reduced-motion: reduce) { .composer-voice-dot { animation: none; } }

/* ---------------------------------------------------------------------------
   Losing and regaining the connection
   ---------------------------------------------------------------------------
   The page is rendered on the server and lives on a circuit; when the connection to the gateway
   drops, nothing in .NET can draw anything, so this overlay is plain HTML revealed by app.js.
   It covers the page rather than replacing it: the transcript underneath is still the right
   transcript, and when the gateway answers again the page reloads itself and reattaches. */
.yasna-reconnect {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    background: rgba(15, 23, 42, .55);
    backdrop-filter: blur(2px);
}

.yasna-reconnect[hidden] { display: none; }

.yasna-reconnect-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .55rem;
    max-width: 26rem;
    padding: 1.4rem 1.6rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: 0 18px 48px rgba(0, 0, 0, .35);
    text-align: center;
}

.yasna-reconnect-card .muted { font-size: .82rem; line-height: 1.7; }

/* The button offered when interrupted work was already continued once and did not survive
   either: continuing again is the operator's decision, so it is a control and not a reflex. */
.chat-resume { margin-block: .5rem; }
