/* Shared components of the abt.nz/panel cabinet (names/panel/index.html links this
   file BEFORE its inline <style>, so page-specific rules there still win ties).
   Everything here is used by more than one screen: Models (#/m/...) and Sessions
   (#/s/...) draw their tabs, buttons, section headings, key box and small tables
   from these classes, so a new tab looks like the existing ones without new CSS.
   Design tokens (--bg, --control, --ok, --err ...) live in :root of index.html;
   only the tokens this file adds are declared below.

   Index
     tokens          --warn
     buttons         .btn  --primary  --success  --warning  --danger  --sm
     small tables    .rrtable  .ktable  .mwrap
     tabs            .pills > .pill  (.is-active, .pill--sm, .pill__soon)
     status text     .soonbox__msg (error / .is-ok)
     section heads   .sh  .sform  .slink
     sessions guide  .sguide (details#sesGuide)  .sguide__body/__h/__steps/__p/__skills/__flow
     key box         .keybox > .ksecret + .keybox__row (.btn + .keybox__msg)
     project page    .pwrap  .pstack  .grid-2  .pcard (--row  --table)  .pcard__head/__h/__actions/__meta/__empty
                     .badge (--ok --warn --muted)  .keyrow (+ __msg)  .dl  .ctable
   Adding a tab: <nav class="pills"><a class="pill is-active" aria-current="page" href="#/...">...</a></nav> */

:root { --warn: #f5b950; }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 18px; border-radius: 999px; border: 1px solid var(--border-raised);
  font-weight: 600; font-size: 0.9rem; color: var(--text);
  background: var(--control); transition: 0.2s;
}
.btn:hover { background: var(--control-2); border-color: var(--brand-2); }
.btn:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }
.btn--primary { background: var(--grad); border: none; color: #fff; box-shadow: 0 8px 28px -10px rgba(124,92,255,0.55); }
.btn--primary:hover { filter: brightness(1.08); }
.btn--danger { color: var(--err); }
.btn--sm { padding: 7px 13px; font-size: 0.82rem; }
.btn[disabled] { opacity: 0.55; pointer-events: none; }

/* Variants of .btn. success = "turn on / confirm", warning = "turn off, can be undone",
   danger (above) = destructive. Success is a saturated green gradient with near-black
   text (#04210f on #16a34a is 5.2:1, on #22c55e 7.3:1: AA at every point of the
   gradient; white would be 3.3:1 and fail), a soft glow and a light sheen that sweeps
   across every few seconds. The amber outline stays clearly apart from the red danger text. */
.btn--success {
  position: relative; overflow: hidden; isolation: isolate;
  background: linear-gradient(135deg, #3be072 0%, #22c55e 48%, #16a34a 100%);
  border-color: #22c55e; color: #04210f;
  box-shadow: 0 6px 20px -6px rgba(34,197,94,0.70), inset 0 1px 0 rgba(255,255,255,0.35);
}
.btn--success:hover {
  background: linear-gradient(135deg, #4ff083 0%, #2fd86a 48%, #1db954 100%);
  border-color: #4ade80; box-shadow: 0 8px 26px -6px rgba(34,197,94,0.90), inset 0 1px 0 rgba(255,255,255,0.45);
}
/* the sheen: a skewed white band that travels left to right, then rests */
.btn--success::after {
  content: ''; position: absolute; z-index: -1; top: 0; bottom: 0; left: 0; width: 45%;
  background: linear-gradient(100deg, transparent 0%, rgba(255,255,255,0.55) 50%, transparent 100%);
  transform: translateX(-130%) skewX(-18deg); pointer-events: none;
  animation: btn-sheen 4.5s ease-in-out infinite;
}
.btn--success:hover::after { animation-duration: 1.6s; }
@keyframes btn-sheen {
  0%, 62% { transform: translateX(-130%) skewX(-18deg); }
  100%    { transform: translateX(330%) skewX(-18deg); }
}
/* disabled: muted dark green with a pale-green label (6.5:1), no glow, no sheen,
   a "not allowed" cursor — clearly "off", still legible */
.btn--success[disabled], .btn--success[disabled]:hover {
  opacity: 1; pointer-events: auto; cursor: not-allowed;
  background: #1f3a2f; border-color: #2f5c47; color: #8fc9ae; box-shadow: none;
}
.btn--success[disabled]::after { display: none; }
@media (prefers-reduced-motion: reduce) {
  .btn--success::after { animation: none; display: none; }
}
.btn--warning { background: transparent; border-color: var(--warn); color: var(--warn); }
.btn--warning:hover { background: rgba(245,185,80,0.14); border-color: var(--warn); }
.btn--warning[disabled] { cursor: not-allowed; pointer-events: auto; opacity: 0.55; }
.btn--warning[disabled]:hover { background: transparent; }

/* ---------- records table ---------- */
.rrtable { width: 100%; border-collapse: collapse; }
.rrtable th {
  text-align: left; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-dim); padding: 10px 12px; border-bottom: 1px solid var(--border);
}
.rrtable td { padding: 12px; border-bottom: 1px solid var(--border); vertical-align: top; font-size: 0.9rem; }
.rrtable tr:hover td { background: var(--surface); }
.rrtable .mono { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 0.82rem; white-space: pre-wrap; word-break: break-all; color: var(--text-muted); }
.rrtable .tag {
  display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 0.75rem; font-weight: 700;
  background: var(--control); border: 1px solid var(--border-raised); color: var(--brand-3);
}
.rrtable .actions { display: flex; gap: 6px; justify-content: flex-end; }

/* ---------- models tab: sub-page pills ---------- */
.pills { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
.pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 16px; border-radius: 999px; border: 1px solid var(--border-raised);
  background: var(--control); color: var(--text-muted);
  font-size: 0.86rem; font-weight: 600; white-space: nowrap; transition: 0.2s;
}
.pill:hover { color: var(--text); background: var(--control-2); border-color: var(--brand-2); }
.pill:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }
/* .sidebar__item.is-active composites its alpha fill on --surface-raised;
   pills sit straight on --bg, where the same rgba collapses to #281f53 —
   1.10:1 against neighbours. Opaque fill (1.73:1 vs --control) plus a
   heavier weight, so "selected" survives a monochrome or washed-out screen. */
.pill.is-active { color: var(--text); background: #453596; border-color: var(--brand); font-weight: 700; }
.pill--sm { padding: 5px 12px; font-size: 0.8rem; }
.pill__soon {
  padding: 1px 8px; border-radius: 999px; font-size: 0.66rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em;
  background: var(--surface-raised-2); border: 1px solid var(--border-raised); color: var(--text-muted);
}

/* reserve the line so an appearing message does not shift the buttons */
.soonbox__msg { font-size: 0.82rem; color: var(--err); min-height: 1.2em; }
.soonbox__msg.is-ok { color: var(--ok); }

/* the price table is wider than a phone: it scrolls inside its own box
   instead of stretching the page */
.mwrap {
  overflow-x: auto; background: var(--surface-raised);
  border: 1px solid var(--border-raised); border-radius: var(--radius); box-shadow: var(--shadow);
}
.mwrap:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }

/* ---------- model keys ---------- */
/* six columns do not fit a phone: like the catalog, the table scrolls inside
   .mwrap instead of stretching the page */
.ktable { min-width: 720px; }
.ktable th, .ktable td { white-space: nowrap; }
.ktable th:first-child, .ktable td:first-child { padding-left: 18px; }
.ktable th:last-child, .ktable td:last-child { padding-right: 18px; }
.ktable th { color: var(--text-muted); }
.ktable tbody tr:last-child td, .ktable tbody tr:last-child th.kname { border-bottom: none; }
/* the name cell is a <th scope="row"> — undo the column-header styling */
.ktable th.kname {
  padding: 12px 12px 12px 18px; font-size: 0.9rem; text-transform: none; letter-spacing: normal;
  color: var(--text); font-weight: 700; white-space: normal; word-break: break-word;
  vertical-align: top; max-width: 320px;
}
.ktable .ksub {
  display: block; margin-top: 3px; font-weight: 400; font-size: 0.75rem;
  color: var(--text-muted); white-space: normal; word-break: break-word;
}
/* --surface hover is 1.09:1 here — invisible while the eye tracks a row */
.ktable tbody tr:hover td, .ktable tbody tr:hover th.kname { background: var(--surface-raised-2); }
/* revoked keys stay listed: a key that simply vanished is indistinguishable
   from one that was never issued. Muting the row with opacity is what NOT
   to do: it composites text, border and background alike, and dragged the
   "revoked" tag down to ~2:1 — the one word the row exists to carry. The
   row is marked with an opaque colour and a rule instead. */
.krow--revoked th.kname, .krow--revoked td { color: var(--text-muted); }
.krow--revoked th.kname { box-shadow: inset 2px 0 0 var(--border-raised); }
.rrtable .tag.ktag--off { color: var(--text-muted); border-color: var(--border-raised); }

/* the one-time secret: selectable in full even where the clipboard API is
   unavailable (CSP allows no polyfill from anywhere) */
.ksecret {
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 0.82rem;
  word-break: break-all; overflow-wrap: anywhere; min-width: 0; max-width: 100%; user-select: all; -webkit-user-select: all; padding: 10px 12px;
  background: var(--control); border: 1px solid var(--border-raised); border-radius: var(--radius-sm);
}

/* ---------- sessions ---------- */
.sh { font-size: 1.05rem; font-weight: 700; margin: 22px 0 12px; }
.sform { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
.sform .cselect { min-width: 240px; max-width: 100%; }
.sform .filter { max-width: 100%; }
.sform .sform__name { width: 200px; }
.sform .sform__remote { width: 320px; }
.schk { display: inline-flex; align-items: center; gap: 6px; font-size: 0.9rem; color: var(--text-muted); }
.sh .tag {
  display: inline-block; padding: 2px 10px; margin-left: 6px; border-radius: 999px; font-size: 0.75rem; font-weight: 700;
  vertical-align: middle; background: var(--control); border: 1px solid var(--border-raised); color: var(--brand-3);
}
.slink { font-size: 0.82rem; font-weight: 600; color: var(--brand-3); }
.slink:hover { text-decoration: underline; }
.slink:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }
.sguide { margin: 0 0 14px; border: 1px solid var(--border-raised); border-radius: var(--radius-sm); background: var(--control); }
.sguide > summary { cursor: pointer; padding: 10px 14px; font-size: 0.9rem; font-weight: 600; }
.sguide > summary:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }
.sguide__body { padding: 0 14px 14px; font-size: 0.88rem; line-height: 1.5; color: var(--text-muted); }
.sguide__h { margin: 14px 0 6px; font-size: 0.82rem; font-weight: 700; color: var(--text); }
.sguide__p { margin: 6px 0 0; }
.sguide__steps, .sguide__skills { margin: 0; padding-left: 20px; }
.sguide__skills { list-style: none; padding-left: 0; }
.sguide__skills li { margin: 0 0 6px; }
.sguide code { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 0.82rem; color: var(--text); overflow-wrap: anywhere; }
.sguide__flow { display: block; width: 100%; max-width: 360px; height: auto; margin: 8px 0 12px; color: var(--brand-3); }
.sguide__more { margin: -6px 0 14px; }
.sguide__node rect { fill: var(--control-2, var(--control)); stroke: var(--border-raised); }
.sguide__node text { fill: var(--text); font: 600 13px 'JetBrains Mono', ui-monospace, monospace; }

/* projects pager under the table: Prev / "Page N of M" / Next */
.pager { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; margin: 14px 0 4px; }
.pager__info { font-size: 0.82rem; font-weight: 600; color: var(--text-muted); }

/* ---------- key / code box with a copy button ---------- */
/* .keybox stacks the field and the button row with a gap, so the button never
   touches the field; the field wraps inside the container instead of
   overflowing it. .ksecret also stands alone (one-time secrets). */
.keybox { display: flex; flex-direction: column; align-items: stretch; gap: 10px; max-width: 100%; min-width: 0; margin-bottom: 12px; }
.keybox .ksecret { width: 100%; }
.keybox__row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.keybox__msg { font-size: 0.82rem; color: var(--ok); min-height: 1.2em; }

/* ---------- compact content blocks (project page: «Доступ» / «Индексация») ---------- */
/* .pcard is a calm, non-clickable panel (the domain list's .card in index.html is
   a clickable tile and is left alone). One spacing scale for all of them: 14px
   padding, 12px gaps. .pwrap caps the page width so lines are not stretched. */
.pwrap { max-width: 960px; }
/* project page header: back link and project name on one line (wraps on a phone) */
.phead { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 16px; margin: 0 0 12px; min-width: 0; }
.phead .slink { flex: none; }
.pwrap .phead .sh { margin: 0; min-width: 0; max-width: 100%; }
.phead__text { overflow-wrap: anywhere; }
.phead__edit { padding: 4px 7px; vertical-align: middle; line-height: 0; }
.phead__edit svg { width: 15px; height: 15px; }
.pwrap .sh { margin: 0 0 12px; }
.pwrap .pills { margin-bottom: 14px; }
.pwrap .soonbox__msg { min-height: 0; margin: 0; }
.pwrap .soonbox__msg:not(:empty) { margin: 0 0 10px; }
.pstack { display: flex; flex-direction: column; gap: 12px; }
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.pcard {
  padding: 14px 16px; background: var(--surface-raised);
  border: 1px solid var(--border-raised); border-radius: var(--radius); box-shadow: var(--shadow);
  min-width: 0;
}
.pcard__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.pcard__h { font-size: 0.98rem; font-weight: 700; }
.pcard__actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.pcard__meta { font-size: 0.8rem; line-height: 1.45; color: var(--text-muted); overflow-wrap: anywhere; }
.pcard__meta + .pcard__meta { margin-top: 2px; }
.pcard__empty { padding: 4px 0 2px; font-size: 0.85rem; color: var(--text-muted); }
/* one switch: text on the left, the action centred on the right */
.pcard--row { display: flex; align-items: center; gap: 14px; }
.pcard--row .pcard__main { flex: 1; min-width: 0; }
.pcard--row .btn { flex: none; }
.pcard__title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.pcard__name { font-weight: 700; font-size: 0.95rem; }
/* a table inside a card: the card is the frame, so the scroll box loses its own */
.pcard--table { padding: 14px 0 0; overflow: hidden; }
.pcard--table .pcard__head { padding: 0 16px; margin-bottom: 8px; }
.pcard--table .pcard__empty { padding: 0 16px 14px; }
/* .main is shrink-to-fit in the phone's column layout: without this a wide table
   (nowrap cells) would widen the whole page instead of scrolling inside its box */
.pwrap .mwrap { contain: inline-size; }
.pcard__head:last-child { margin-bottom: 0; }
.pcard--table .mwrap:focus-visible { outline-offset: -2px; }   /* the card clips (overflow:hidden): draw the ring inside */
.pcard--table .mwrap { border: 0; border-radius: 0; box-shadow: none; background: none; border-top: 1px solid var(--border); }
.rrtable.ctable { min-width: 0; }
.ctable th, .ctable td { white-space: nowrap; }
.ctable th { padding: 7px 12px; }
.ctable td, .ctable th.kname { padding: 7px 12px; font-size: 0.84rem; }
.ctable th:first-child, .ctable td:first-child { padding-left: 16px; }
.ctable th:last-child, .ctable td:last-child { padding-right: 16px; }
.ctable .actions { justify-content: flex-end; }
/* text and buttons share one centre line; the buttons are a little tighter so rows stay short */
.ctable td, .ctable th.kname { vertical-align: middle; }
.ctable .btn--sm { padding: 4px 12px; }
/* cells keep their words whole: a narrow phone scrolls the table sideways instead of
   breaking an address letter by letter */
.ctable th.kname, .rrtable.ctable .mono { white-space: nowrap; word-break: normal; max-width: none; }
/* the «Доступ» table (addresses + status + date + button): on a narrow phone the address
   wraps so the status and the button stay on screen; the handle sits under the email */
.ctable.atable th.kname { white-space: normal; overflow-wrap: anywhere; word-break: normal; }
.atable .nw { white-space: nowrap; }   /* a date wraps between its words, never inside one */
.atable .kname__sub, .ttable .kname__sub { margin-top: 1px; font-size: 0.78rem; font-weight: 400; color: var(--text-muted); }
@media (max-width: 520px) {
  .ctable.atable th, .ctable.atable td { padding-left: 8px; padding-right: 8px; }
  .ctable.atable td { white-space: normal; }   /* the date wraps to two lines; the badge and the button keep their words */
  .ctable.atable .btn--sm { padding: 4px 9px; }
}
/* a state pill: ok / warn / muted (colour AND word, never colour alone) */
.badge {
  display: inline-flex; align-items: center; gap: 5px; padding: 1px 9px; border-radius: 999px;
  font-size: 0.74rem; font-weight: 700; line-height: 1.5; white-space: nowrap;
  background: var(--control); border: 1px solid var(--border-raised); color: var(--text-muted);
}
.badge--ok { color: var(--ok); background: rgba(95,227,177,0.10); border-color: rgba(95,227,177,0.45); }
.badge--warn { color: var(--warn); background: rgba(245,185,80,0.10); border-color: rgba(245,185,80,0.5); }
.badge--muted { color: var(--text-muted); }
/* key and the copy button in ONE box; the key wraps, the button never overlaps it */
.keyrow {
  display: flex; align-items: flex-start; gap: 10px; padding: 7px 8px 7px 12px;
  background: var(--control); border: 1px solid var(--border-raised); border-radius: var(--radius-sm);
}
.keyrow .ksecret { flex: 1; min-width: 0; padding: 3px 0; background: none; border: 0; border-radius: 0; }
.keyrow .btn { flex: none; }
.keyrow__msg { flex: none; align-self: center; font-size: 0.74rem; line-height: 1.3; color: var(--ok); }
.keyrow__msg:empty { display: none; }
/* label / value pairs, two columns */
.dl { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 4px 14px; margin: 16px 0 0; font-size: 0.82rem; }
.dl dt { color: var(--text-muted); }
.dl dd { min-width: 0; overflow-wrap: anywhere; }
.dl code { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 0.78rem; }
.pcard .sform { margin-bottom: 0; }
/* the repository card of the project page: address and id, then the owner's edit fold */
#sRepoCard, #sNameCard { margin: 0 0 12px; }
#sNameCard .sform { margin: 10px 0 8px; }
#sRepoCard .dl { margin-top: 0; }
.repo__none { color: var(--text-muted); }
.repo__edit { margin-top: 12px; }
.repo__edit summary { cursor: pointer; font-size: 0.82rem; font-weight: 600; color: var(--brand-3); }
.repo__edit .sform { margin: 10px 0 8px; }

/* two-column blocks stack on a phone */
@media (max-width: 720px) {
  .grid-2 { grid-template-columns: 1fr; }
  .pcard--row { gap: 10px; }
}
/* on a phone the tab row is a horizontal strip */
@media (max-width: 760px) {
  /* five sub-pages don't fit a phone row — swipe them instead of wrapping */
  .pills {
    flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    /* overflow-x:auto makes overflow-y auto too, so the box clips the 2px
       outline + 2px offset of .pill:focus-visible — leave it 4px each side */
    padding-top: 4px; padding-bottom: 4px; margin-bottom: 16px;
  }
  .pills .pill { flex: none; }
}

/* ---------- «Сессии» on a phone: the page itself never scrolls sideways ----------
   .main is shrink-to-fit in the phone's column layout (index.html), so a table with
   nowrap cells and min-width widened the whole page. The sessions screens take the
   full column instead, and their tables either wrap (projects) or turn into cards
   (project tokens); anything still wider scrolls inside its own .mwrap. */
@media (max-width: 760px) {
  .main:has(#sbody) { align-self: stretch; }
  #sbody .mwrap { contain: inline-size; }
  /* add-project form: field on a full-width line, button below it */
  #sAddForm .filter { flex: 1 1 100%; width: 100%; min-width: 0; }
  #sAddForm .btn { flex: 1 1 100%; justify-content: center; }
  /* projects table: three short columns; long remotes and dates wrap */
  #sProjList .ktable { min-width: 0; }
  #sProjList .ktable th, #sProjList .ktable td { white-space: normal; }
  #sProjList .ktable th.kname { max-width: none; overflow-wrap: anywhere; word-break: normal; }
  #sProjList .ktable th:first-child, #sProjList .ktable td:first-child { padding-left: 12px; }
  #sProjList .ktable th:last-child, #sProjList .ktable td:last-child { padding-right: 12px; }
  #sProjList .ktable .nw { white-space: nowrap; }   /* a date wraps between its words, never inside one */
  #sProjList .ktable td:nth-child(2) .tag, #sProjList .ktable td:nth-child(2) .badge { margin: 0 4px 4px 0; }
  /* project tokens: one card per token, «column: value» lines */
  .ttable { min-width: 0; }
  .ttable thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .ttable, .ttable tbody, .ttable tr, .ttable th, .ttable td { display: block; }
  .ttable tr { padding: 10px 14px; border-bottom: 1px solid var(--border); }
  .ttable tbody tr:last-child { border-bottom: 0; }
  .ttable th.kname, .ttable td { padding: 3px 0; border: 0; white-space: normal; max-width: none; overflow-wrap: anywhere; }
  .ttable tr:hover td { background: none; }
  .ttable th.kname { padding-left: 0; padding-bottom: 6px; }
  .ttable td[data-label] { display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: 0 10px; }
  .ttable td[data-label]::before { content: attr(data-label); color: var(--text-muted); font-size: 0.78rem; }
  .ttable .ttable__act { padding-top: 8px; }
  .ttable .actions { justify-content: flex-start; flex-wrap: wrap; }
  .ttable .krow--revoked th.kname { box-shadow: none; }
  .ttable .krow--revoked { box-shadow: inset 2px 0 0 var(--border-raised); }
}

/* a 360px phone: brand + «← На сайт» + language + «Выйти» were 8px wider than the screen */
@media (max-width: 400px) {
  .topnav .topnav__inner { padding: 0 10px; gap: 8px; }
  .topnav .topnav__menu { gap: 6px; }
  .topnav .brand { gap: 7px; }
  .topnav .brand__name { font-size: 1.1rem; }
  .topnav .brand__sub { font-size: 0.98rem; }
}
