/*
 * Phoenix Sites Admin Console — shared design system.
 * Tokens mirror paymentsService's console so the two admin surfaces read as one product.
 * Served at /admin/static/assets/console.css (the session cookie authorizes the request).
 */

:root {
    --ink: #1a1f36;
    --ink-secondary: #596171;
    --ink-muted: #666e80;
    --ground: #f6f8fa;
    --surface: #ffffff;
    --border: #e3e8ee;
    /* The outline of every input, select and button. It carries the boundary, so it is a
       UI component under WCAG 1.4.11 and owes 3:1 — it was at 1.54:1 against the card and
       1.45:1 against the page. --border stays subtle: a table rule separates, it does not
       identify a control. */
    --border-strong: #7e8899;
    --flame: #fe8515;
    --flame-deep: #b34a00;
    --flame-deeper: #a34400;
    --flame-tint: #fff4ea;
    --success: #1a7f37;
    --success-dot: #23a55a;
    --success-tint: #eaf7ee;
    --warning: #9a6700;
    --warning-dot: #d4a72c;
    --warning-tint: #fff8e5;
    --danger: #b91c1c;
    --danger-dot: #e5484d;
    --danger-tint: #fdeeee;
    --on-flame: #ffffff;

    /* Code surfaces. JSON has five things worth telling apart; every one of these was
       checked against --code-bg in both themes. */
    --code-bg: #f6f8fa;
    --code-key: #0a52a1;
    --code-string: #0a6640;
    --code-number: #8a3ffc;
    --code-atom: #953800;
    --code-gutter: #666e7b;
    --code-active: #eef1f5;
    --code-selection: #d3e2f5;
    --backdrop: rgba(16, 24, 40, 0.36);
    --shadow-dialog: 0 12px 32px -8px rgba(16, 24, 40, 0.22), 0 2px 6px rgba(16, 24, 40, 0.06);
    --radius-sm: 6px;
    --radius-md: 8px;
    --shadow-card: 0 1px 2px rgba(16, 24, 40, 0.04);
    --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    color-scheme: light;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--ground);
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: 14px;
    line-height: 1.5;
}

/* ---------- chrome ---------- */
.topbar {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    padding: 0 24px;
    /* Not a fixed height, and it wraps at every width — not only under 640px.
       Moderation carries one control the other pages do not (Refresh), and at 768px that one
       extra button put Sign out two pixels past the edge and scrolled the whole page sideways.
       The fix is not a wider breakpoint: it is that the bar may take a second row whenever what
       it holds does not fit, so the next control anyone adds cannot push the page off-screen. */
    min-height: 56px;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 20px;
    row-gap: 4px;
}
.brand { display: flex; align-items: center; gap: 9px; font-weight: 640; letter-spacing: -0.01em; }
.brand .mark {
    width: 22px; height: 22px; border-radius: 6px;
    background: linear-gradient(140deg, var(--flame), var(--flame-deep));
}
.topbar nav { display: flex; gap: 4px; margin-left: 8px; }
.topbar nav a {
    color: var(--ink-secondary); text-decoration: none; padding: 6px 11px;
    border-radius: var(--radius-sm); font-weight: 500;
}
.topbar nav a:hover { background: var(--ground); color: var(--ink); }
.topbar nav a[aria-current="page"] { background: var(--flame-tint); color: var(--flame-deeper); }
.grow { flex: 1; }

main { padding: 24px; max-width: 1180px; }
h1 { font-size: 19px; margin: 0 0 3px; letter-spacing: -0.02em; }
.page-note { color: var(--ink-muted); margin: 0 0 22px; }

/* ---------- cards ---------- */
.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    margin-bottom: 20px;
    overflow: hidden;
}
.card > header {
    padding: 13px 18px;
    border-bottom: 1px solid var(--border);
    display: flex; align-items: center; gap: 12px;
}
.card > header h2 {
    font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--ink-muted); margin: 0; font-weight: 620;
}
.card .body { padding: 16px 18px; }

/* ---------- stats ---------- */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr)); gap: 1px; background: var(--surface); }
.stats > div { background: var(--surface); padding: 15px 18px; box-shadow: 0 0 0 1px var(--border); }
.stats .value { display: block; font-size: 23px; font-weight: 620; letter-spacing: -0.025em; }
/* Service state is the one value on this card that is not configuration. */
.stats .value.up { color: var(--success); }
.stats .value.down { color: var(--danger); }
tr.handled { opacity: 0.55; }
.stats .label { color: var(--ink-muted); font-size: 13px; }
/* The track was --ground on a --surface tile: about 1.04:1, which is no track at all. */
.meter { height: 6px; background: var(--border); border-radius: 3px; overflow: hidden; margin-top: 8px; }
.meter > i { display: block; height: 100%; background: var(--success-dot); }
.meter.high > i { background: var(--warning-dot); }
.meter.over > i { background: var(--danger-dot); }

/* ---------- tables ---------- */
.scroll { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 10px 18px; border-bottom: 1px solid var(--border); }
th { font-size: 12px; font-weight: 600; color: var(--ink-muted); background: var(--ground); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--ground); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
.mono { font-family: var(--font-mono); font-size: 13px; }

/* ---------- bits ---------- */
.pill {
    display: inline-block; padding: 2px 9px; border-radius: 20px;
    font-size: 12px; font-weight: 550; background: var(--ground); color: var(--ink-secondary);
}
.pill.ok { background: var(--success-tint); color: var(--success); }
.pill.warn { background: var(--warning-tint); color: var(--warning); }
.pill.bad { background: var(--danger-tint); color: var(--danger); }

button, .btn {
    font: inherit; font-weight: 550; cursor: pointer;
    background: var(--surface); color: var(--ink);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    padding: 6px 12px;
}
.btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.btn svg { width: 13px; height: 13px; flex: none; opacity: 0.75; }
button:hover, .btn:hover { background: var(--ground); color: var(--ink); }

/* The secondary verb on a row. Present, reachable, and not competing with Open. */
button.quiet {
    background: none; border-color: transparent; color: var(--ink-secondary);
    padding-left: 6px; padding-right: 6px;
}
button.quiet:hover { background: none; color: var(--ink); text-decoration: underline; }
.addr { display: inline-flex; align-items: baseline; gap: 6px; }
.addr .copy { font-size: 12px; }

/* A site's other addresses, listed under the one it was given. They are the same kind of
   object — an address this site answers on — so they belong in the same cell, not in a
   second table with a "which site?" column to cross-reference. */
.addr-list { margin-top: 6px; display: flex; flex-direction: column; gap: 5px; }
.addr-list > div { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.addr-list .mono { color: var(--ink-secondary); }
.addr-list .pill { font-size: 11px; padding: 1px 7px; }
.addr-list button.quiet { padding: 2px 6px; font-size: 12px; min-height: 0; }

/* The record a customer has to create. It is the whole product at this moment, so it gets its
   own block and every value copies on its own — someone pasting "www CNAME t_ab12…" as one
   string into a single Name field is the classic way this goes wrong. */
.guide { background: var(--ground); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 16px 18px; }
.guide h3 { font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-muted); margin: 0 0 12px; }
.guide code { white-space: nowrap; }
.record { width: auto; margin: 0 0 14px; }
.record th, .record td { padding: 6px 16px 6px 0; border-bottom: 1px solid var(--border); text-align: left; }
.record th { font-size: 12px; font-weight: 600; color: var(--ink-muted); }
.record td .mono { font-size: 13px; }
.record tr:last-child th, .record tr:last-child td { border-bottom: 0; }
.steps { margin: 0 0 14px; padding-left: 20px; color: var(--ink-secondary); }
.steps li { margin-bottom: 4px; }
button.primary { background: var(--flame-deep); border-color: var(--flame-deep); color: var(--on-flame); }
button.primary:hover { background: var(--flame-deeper); }
/* Must precede the aria-busy rules below: same specificity, so whichever comes last wins. */
button.primary .kbd { color: var(--on-flame); border-color: currentcolor; opacity: 0.75; }
button.danger { color: var(--danger); border-color: var(--danger); background: var(--surface); }
button.danger:hover { background: var(--danger-tint); }
button:disabled { opacity: 0.5; cursor: default; }

/* textarea belongs here too: left out, it keeps the browser field colour and is the one
   control that does not follow the theme. */
input, select, textarea {
    font: inherit; color: inherit; background: var(--surface);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 6px 10px;
}
.field { display: block; margin-bottom: 12px; }
/* Child, not descendant: the nested .hint belongs on the same line as the label it qualifies. */
.field > span { display: block; font-size: 13px; color: var(--ink-secondary); margin-bottom: 4px; }
.field input { width: 100%; }

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

/* .row on a <td> makes the cell a flex box, which takes it out of the row's shared height: its
   border-bottom then draws at its own content height and the rule under the row visibly
   staggers. Keep cells as cells — .num already right-aligns their contents. */
td.row { display: table-cell; }
td.row > * { vertical-align: middle; }
td.row > * + * { margin-left: 6px; }
.empty { padding: 26px 18px; color: var(--ink-muted); }
.flash { padding: 11px 18px; border-bottom: 1px solid var(--border); }
.flash.error { background: var(--danger-tint); color: var(--danger); }
.flash.ok { background: var(--success-tint); color: var(--success); }
.hint { color: var(--ink-muted); font-size: 13px; }

/* ---------- login ---------- */
.login-wrap { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.login-card { width: 100%; max-width: 380px; }
.login-card .brand { justify-content: center; margin: 0 0 18px; font-size: 16px; font-weight: 640; }

/* ---------------------------------------------------------------------------
 * Browser surfaces. These ship with defaults belonging to no design system, and
 * they are what makes an interface feel assembled rather than built.
 * ------------------------------------------------------------------------ */
::selection { background: var(--flame-tint); color: var(--flame-deeper); }
:root { accent-color: var(--flame-deep); caret-color: var(--flame-deep); }

/* Scrollbars: present, quiet, and matched to the surface rather than the OS. */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 6px; border: 3px solid var(--surface); }
*::-webkit-scrollbar-thumb:hover { background: var(--ink-muted); }

/* Links. The browser default belongs to no palette — and under color-scheme: dark
   it turns lavender. Ink with a quiet rule; the accent arrives on hover. */
a { color: var(--ink); text-decoration-color: var(--border-strong);
    text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--flame-deep); text-decoration-color: currentcolor; }

/* One focus treatment everywhere, including links and the endpoint list. */
:focus-visible { outline: 2px solid var(--flame-deep); outline-offset: 2px; border-radius: var(--radius-sm); }
a:focus-visible, button:focus-visible { outline-offset: 1px; }

/* Numbers that line up column to column. */
.num, .value, td.num, th.num, .status, time { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------------------
 * States. Every interactive element needs all of these, not half.
 * ------------------------------------------------------------------------ */
button:active:not(:disabled) { transform: translateY(0.5px); }
button[aria-busy="true"] { color: transparent; position: relative; pointer-events: none; }
button[aria-busy="true"] .kbd { color: transparent; border-color: transparent; }
button[aria-busy="true"]::after {
    content: ""; position: absolute; inset: 0; margin: auto; width: 13px; height: 13px;
    border: 2px solid currentcolor; border-top-color: transparent; border-radius: 50%;
    color: var(--ink-secondary); animation: spin 620ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;   /* or "infinite" keeps firing at 0.01ms */
        transition-duration: 0.01ms !important;
    }
    /* Clamping the spinner to nothing left a motionless ring on a button with no visible label:
       less information than doing nothing. Keep the label and dim it instead. */
    button[aria-busy="true"] { color: inherit; opacity: 0.6; }
    button[aria-busy="true"] .kbd { color: inherit; border-color: currentcolor; }
    button[aria-busy="true"]::after { display: none; }
}

/* ---------- confirm dialog ---------- */
dialog.confirm {
    border: 1px solid var(--border); border-radius: var(--radius-md); padding: 22px 24px;
    max-width: 440px; width: calc(100% - 32px);
    box-shadow: var(--shadow-dialog);
    color: var(--ink); background: var(--surface);
}
dialog.confirm::backdrop { background: var(--backdrop); }
dialog.confirm h2 { font-size: 16px; margin: 0 0 8px; letter-spacing: -0.01em; }
dialog.confirm p { margin: 0; color: var(--ink-secondary); }
dialog.confirm p b { color: var(--ink); }
dialog.confirm .field { margin: 16px 0 0; }

/* Raw payload stays available under a readable summary rather than dumped. */
.flash details { margin-top: 8px; }
.flash summary { cursor: pointer; color: inherit; opacity: 0.8; font-size: 13px; }
.flash pre {
    margin: 8px 0 0; padding: 10px 12px; background: var(--surface); color: var(--ink);
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    font-family: var(--font-mono); font-size: 13px; max-height: 240px; overflow: auto;
}

/* Recent-call list reuses the endpoint list styling; the status carries the outcome. */
#history .status { font-weight: 700; }
#history .status.s2 { color: var(--success); }
#history .status.s4 { color: var(--warning); }
#history .status.s5 { color: var(--danger); }

/* Controls that live in a card header sit tighter than body buttons. */
.card > header button { padding: 3px 9px; font-size: 13px; }
.card > header .kbd { flex: none; }

/* The filter reads as chrome for the list below it — but that quietness comes from the
   recessed background, not from a border too faint to locate the control (1.23:1). */
#filter { background: var(--ground); }
#filter:focus { background: var(--surface); }
#filter::-webkit-search-cancel-button { cursor: pointer; }

/* ---------------------------------------------------------------------------
 * Narrow widths. The topbar is the only row that cannot wrap — an operator
 * checking a report from a phone still needs Sign out to be on the screen.
 * ------------------------------------------------------------------------ */
@media (max-width: 640px) {
    /* Squeezing the row stopped working once the theme control joined it. Two rows fit
       everything at full size, which is better than a nav that truncates or scrolls sideways. */
    .topbar {
        height: auto; min-height: 52px; padding: 7px 14px;
        flex-wrap: wrap; column-gap: 10px; row-gap: 4px;
    }
    .brand { font-size: 0; gap: 0; }              /* the mark carries the identity; the wordmark does not fit */
    .brand .mark { font-size: 14px; }
    .topbar nav { order: 3; width: 100%; margin-left: -8px; }
    .topbar nav a { padding: 6px 8px; }
    .topbar > .hint { display: none; }            /* service detail belongs on the page, not the chrome */
    .topbar button, .topbar nav a {
        min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
    }
    /* The chrome was not the only thing being aimed at: the page's own controls sat at 28-35px. */
    button, input, select { min-height: 44px; }
    /* A link used as a control needs the same target as a button — the stat labels carry one. */
    .stats a { display: inline-flex; align-items: center; min-height: 44px; }
    .card > header button { min-height: 36px; }   /* header controls sit in a 44px row already */
    .theme-toggle { min-width: 44px; }
    main { padding: 16px 14px; }
    .card > header, .card .body, th, td { padding-left: 14px; padding-right: 14px; }
}

/* A keyboard hint on a filled button inherits nothing useful from the muted default. */

/* ---------------------------------------------------------------------------
 * Dark palette.
 *
 * An operator reads this console at 3am as often as at noon, so the theme
 * follows the operating system until they choose otherwise, and then it sticks.
 *
 * The resolved value is stamped on <html> by a script in each page's head,
 * before first paint — which is why this is ONE block rather than a
 * prefers-color-scheme media query duplicated alongside an attribute selector.
 * Two copies of a palette drift; one cannot. Every page here is dead without
 * JavaScript anyway, so there is no reader that the media query would serve.
 *
 * Only tokens change. Nothing below this line re-styles a component: if a rule
 * needs a dark exception, the exception belongs in a token instead.
 * ------------------------------------------------------------------------ */
:root[data-theme="dark"] {
    --ink: #e7edf4;
    --ink-secondary: #a6b1bf;
    --ink-muted: #8b95a3;
    --ground: #0e1116;
    --surface: #171c23;
    --border: #262d37;
    --border-strong: #626e82;

    /* The flame brightens rather than deepens — on a dark ground the light-mode
       ramp inverts, so hover moves toward the light end and the label goes dark. */
    --flame: #ff9f45;
    --flame-deep: #f5871f;
    --flame-deeper: #ffb066;
    --flame-tint: #2c1c0d;
    --on-flame: #1a1206;

    --success: #52c77a;
    --success-dot: #2ea043;
    --success-tint: #12261b;
    --warning: #e0b341;
    --warning-dot: #bb8009;
    --warning-tint: #2a2109;
    --danger: #f2777d;
    --danger-dot: #e5484d;
    --danger-tint: #2e1519;

    --code-bg: #0b0e13;
    --code-key: #79c0ff;
    --code-string: #69d38a;
    --code-number: #d2a8ff;
    --code-atom: #ffa657;
    --code-gutter: #7d8794;
    --code-active: #171c23;
    --code-selection: #24395c;

    /* Shadow reads weakly on a dark ground; the border carries the separation. */
    --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.5);
    --shadow-dialog: 0 16px 40px -10px rgba(0, 0, 0, 0.72), 0 2px 8px rgba(0, 0, 0, 0.4);
    --backdrop: rgba(4, 6, 10, 0.62);
    color-scheme: dark;
}

/* ---------- theme control ---------- */
.theme-toggle { display: inline-flex; align-items: center; gap: 6px; color: var(--ink-secondary); padding: 5px 9px; }
.theme-toggle:hover { color: var(--ink); }
.theme-toggle svg { width: 15px; height: 15px; flex: none; }
@media (max-width: 640px) { .theme-toggle span { display: none; } }

/* ---------------------------------------------------------------------------
 * CodeMirror. Everything visual lives in the editor's own theme (built from the
 * same custom properties as the rest of the console); only the box it sits in
 * is set here, because only the page knows how tall each surface should be.
 * ------------------------------------------------------------------------ */
#body .cm-editor { min-height: 132px; max-height: 42vh; }
#out .cm-editor { max-height: 46vh; }
.cm-editor { width: 100%; }
