/* portal/static/portal.css — ONLY WHAT IS THE TENANT PLANE'S OWN.

   ★★★ THE DESIGN SYSTEM IS cloudify.css, CARRIED VERBATIM FROM THE ADMIN (Eslam 2026-10-09: "i want
   the menu style, the way things are created and managed to match exactly the way it is in admin ...
   make sure all input fields, separatos, buttons and so on have the custom css").

   This file used to re-declare 188 rules whose SELECTORS the admin already defines — :root, body, .btn,
   input.ti, .appbar, .shell, .side, .nav-item, .tile, .panel, .section-h, every one of them ported by
   eye. That is the drift the request is about: two stylesheets that are supposed to look identical and
   diverge one padding value at a time, invisibly, because nothing compares them.

   ⇒ Those 188 rules are GONE. cloudify.css is served next to this file and loaded FIRST, so the shared
   vocabulary is the admin's byte for byte — tokens, spacing, focus rings, icon masks and all — and
   portal/adminui_test.go fails the build if the copy drifts from the admin's or if this file starts
   re-declaring a selector the admin owns.

   ⚠ WHAT BELONGS HERE: the surfaces the admin does not have. The sign-in and suspended pages, the
   toast, the quota meters, the per-instance usage bars, the pane container, and the two form helpers
   (.formrow/.kv) the tenant forms are built from. Nothing else. A rule that would also make sense in
   the admin belongs in the admin's stylesheet, where both sides get it.
*/

@keyframes spin{ to { transform: rotate(360deg); } }/* ---- the sign-in card (the admin's login card, ported) ---- */
body.signin{ min-height: 100dvh; margin: 0; display: grid; place-items: center; padding: 1.5rem; }.login{
  width: min(390px, 100%); padding: 2.4rem 2.1rem; position: relative;
  border: 1px solid var(--border); border-radius: 18px;
  background: linear-gradient(180deg, rgba(31,33,37,.92), rgba(22,23,26,.92));
  box-shadow: var(--shadow-lg); backdrop-filter: blur(8px);
}.login::before{
  content: ""; position: absolute; inset: -1px; border-radius: 18px; padding: 1px; pointer-events: none;
  background: linear-gradient(160deg, rgba(19,211,158,.5), transparent 40%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}.login .brand{ display: flex; align-items: center; gap: .7rem; margin-bottom: .3rem; }.login .brand img{ height: 30px; }.login .sub{ margin: 0 0 1.7rem; color: var(--muted); font-size: .82rem; letter-spacing: .01em; }.login label{ display: block; font-size: .78rem; color: var(--text-2); margin: 1rem 0 .35rem; }.login input{
  width: 100%; padding: .72rem .8rem; font: inherit; font-size: .95rem; color: var(--text);
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface-inset); transition: .15s;
}.login input:focus{ outline: none; border-color: var(--green); box-shadow: 0 0 0 3px var(--green-soft); }.login button{
  width: 100%; margin-top: 1.7rem; padding: .74rem; font: inherit; font-weight: 700; letter-spacing: .01em;
  color: var(--on-green); background: linear-gradient(180deg, var(--green), var(--green-2));
  border: 0; border-radius: 10px; cursor: pointer; transition: filter .15s, transform .05s;
}.login button:hover{ filter: brightness(1.06); }.login button:active{ transform: translateY(1px); }.login .err{
  margin: 0 0 1.1rem; padding: .65rem .8rem; border-radius: 10px; font-size: .84rem;
  color: #ffb4b6; background: rgba(229,72,77,.12); border: 1px solid rgba(229,72,77,.3);
}.login .foot{ margin-top: 1.7rem; text-align: center; color: var(--muted); font-size: .72rem; display: flex; align-items: center; justify-content: center; gap: .35rem; }/* ---- the tenant's quota meters -----------------------------------------------------------------
   ★ A QUOTA WITH NO CEILING RENDERS DIFFERENTLY ON PURPOSE. `absent = unlimited` is the platform's
   convention (tenantquota.go), and a bar drawn to an invented maximum would tell a tenant they are
   "80% of the way" to a limit that does not exist. `.q.unlimited` drops the bar entirely. */
.quotas{ display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 1rem; }.q{ border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: .9rem 1rem; box-shadow: var(--shadow); }.q .qh{ display: flex; align-items: center; gap: .45rem; color: var(--muted); font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; }.q .qn{ font-size: 1.5rem; font-weight: 750; letter-spacing: -.02em; margin: .3rem 0 .45rem; font-variant-numeric: tabular-nums; }.q .qn .of{ font-size: .82rem; font-weight: 500; color: var(--muted); letter-spacing: 0; }.q .bar{ height: 6px; border-radius: 4px; background: #2a2c30; overflow: hidden; }.q .bar > i{ display: block; height: 100%; background: linear-gradient(90deg, var(--green-2), var(--green)); border-radius: 4px; }.q.warn .bar > i{ background: linear-gradient(90deg, #c8871a, var(--amber)); }.q.full .bar > i{ background: linear-gradient(90deg, #b83a3e, var(--red)); }.q.unlimited .bar{ display: none; }.q .qsub{ color: var(--muted); font-size: .72rem; margin-top: .4rem; }/* ---- forms a tenant fills in ---- */
.kv{ display: grid; grid-template-columns: 148px 1fr; gap: .65rem .9rem; align-items: center; }.kv > label{ color: var(--text-2); font-size: .8rem; }.kv .hint{ grid-column: 2; color: var(--muted); font-size: .72rem; margin-top: -.3rem; }/* ⚠ THE ROW OWNS ITS VERTICAL RHYTHM (Eslam, 2026-10-11: "some even needs a bit more padding on top
   bottom or/and sides to seperate them from other elements"). Every one of the four .formrow uses
   carried its OWN inline spacing — `padding:.7rem 1rem`, `margin:.4rem 0 1.1rem`, `margin:.2rem 0 .9rem`,
   `margin-top:.6rem` — so how far a button stood from what sat above and below it depended on which
   one-off a row happened to have, and the one with only a top margin had nothing underneath at all.
   ⇒ One value here, and the inline overrides removed, so the rhythm is consistent and tunable in a
     single place instead of four. */
.formrow{ display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; margin: .75rem 0; }.formrow .sp{ flex: 1; }/* ---- a refusal from the door, shown verbatim --------------------------------------------------
   ⛔ THE SERVER'S SENTENCE IS THE UI'S SENTENCE. Every refusal this plane can receive was written to
   be read by a person ("tenant acme may hold 4 vCPUs, already holds 4, this asks for 2 more"), and
   rewording it in the browser would mean a tenant and an operator reading the same event describe it
   differently. ⚠ `white-space: pre-wrap` because those sentences arrive as streamed lines. */
.said{ margin: 0; padding: .7rem .85rem; border-radius: 10px; font-size: .82rem; white-space: pre-wrap;
  word-break: break-word; border: 1px solid var(--border); background: var(--surface-inset); color: var(--text-2); }.said.bad{ color: #ffb4b6; background: rgba(229,72,77,.12); border-color: rgba(229,72,77,.3); }.said.ok{ color: #9fe6d4; background: var(--green-soft); border-color: rgba(19,211,158,.35); }.said:empty{ display: none; }/* ---- toast (one, bottom-right, replaced not stacked) ---- */
#toast{ position: fixed; right: 1.1rem; bottom: 1.1rem; z-index: 400; max-width: min(460px, 92vw);
  padding: .7rem .95rem; font-size: .82rem; white-space: pre-wrap; color: var(--text-2);
  background: var(--surface-0); border: 1px solid var(--border); border-radius: 11px; box-shadow: var(--shadow-lg);
  opacity: 0; transform: translateY(6px); transition: opacity .15s, transform .15s; pointer-events: none; }#toast.show{ opacity: 1; transform: translateY(0); }#toast.bad{ color: #ffb4b6; border-color: rgba(229,72,77,.4); }.appbar .tenant{ display: inline-flex; align-items: center; gap: .35rem; font-size: .78rem; color: var(--text-2);
  border: 1px solid var(--border); border-radius: 7px; padding: .1rem .5rem; background: var(--surface-3); }.appbar .tenant b{ color: var(--green); font-weight: 650; }.pane[hidden]{ display: none; }/* ---- the suspended page: two lines, centred both ways, and nothing else -----------------------
   ★ Eslam 2026-10-09: "tenant admin/user only sees 'Account suspended! Contact Provider.' 2 lines,
   centered hor and vert". ⛔ Deliberately NOT a banner over the app — a suspended account is not a working
   account with a notice on it, so the plane does not build the app for these sessions at all. */
body.suspended{ min-height: 100dvh; margin: 0; display: grid; place-items: center; padding: 1.5rem; }/* ★ THE SIGN-IN CARD, REUSED. Eslam, on seeing the first version: "the suspention page should keep the
   theme btw, not a blak white page". A bare message on white reads as "something broke" — and this state is
   the opposite of broken: the platform is doing exactly what the operator told it to. So it is the same
   card, the same backdrop and the same brand mark as the page the tenant signed in on. */
.susp-card{ text-align: center; }.susp-card .brand{ justify-content: center; margin-bottom: 1.6rem; }.susp-msg p{ margin: .3rem 0; font-size: 1.3rem; font-weight: 650; letter-spacing: -.01em; }.susp-msg p:first-child{ color: var(--red); }.susp-msg p:last-child{ color: var(--text-2); font-weight: 500; }/* ★ PERCENTAGE-ONLY LOAD BARS for a static site (Eslam 2026-10-09: "cpu and memory as bars same way
   like now, but as % only, not showing actual reserved cpu and memory"). Every site runs at the same
   fixed size, so the absolute figure would be identical on every row and only the proportion carries
   information. ⚠ The colours are the platform's own tokens — nothing new is introduced here, because
   this stylesheet is compared with the admin's token by token. */
.ubar{ display:flex; align-items:center; gap:.45rem; min-width:104px }.ubar .track{ flex:1; height:6px; border-radius:4px; background:var(--surface-2); overflow:hidden }.ubar .track span{ display:block; height:100%; background:var(--green) }.ubar.warn .track span{ background:var(--amber) }.ubar.crit .track span{ background:var(--red) }.ubar .pc{ font-size:.72rem; color:var(--muted); min-width:2.5rem; text-align:right }/* ★ .note.warn — a note that states a SITE FACT a tenant must not miss, not an error.
   ⛔ IT NEEDED ITS OWN RULE: `class="note warn"` was written first and rendered as a plain note,
   because every `warn` in the stylesheets is qualified (.pill.warn, .q.warn, .ubar.warn) and a bare
   one matches nothing. A class that exists in no rule is the trap this repository keeps paying for —
   the agreement test only proves the WORD appears somewhere, never that THIS element gets styled. */
.note.warn{ border-left: 3px solid var(--amber); background: rgba(245,166,35,.07); color: var(--text); padding: .5rem .7rem; border-radius: 0 8px 8px 0; margin-top: .6rem }
.note.warn b{ color: var(--amber) }

/* ---- the Activity Log's expandable event (the portal's own table) -------------------------------
   ★★ Eslam 2026-10-11: "the log item should not show details (should be collapsable below each item,
   and shows and hides on click, and can be multi line to prevent the scrollbar from appearing".
   ⛔ `white-space: pre-wrap` AND `overflow-wrap: anywhere` together, and that pairing is the whole
   point: the platform's sentences arrive with their own line breaks (pre-wrap keeps them) and they
   contain unbroken tokens — namespaces, image digests, quota sentences — that no space will break
   (anywhere breaks them). With only pre-wrap a long token re-introduced exactly the horizontal
   scrollbar this change exists to remove. */
.au-x { width: 26px; padding-right: 0 }
.au-r.has { cursor: pointer }
.au-r.has:hover { background: rgba(255,255,255,.035) }
.au-t { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px;
  padding: 0; color: var(--muted); background: none; border: 0; cursor: pointer; border-radius: 6px;
  transform: rotate(-90deg); transition: transform .16s ease, color .15s }
.au-t:hover { color: var(--text); background: rgba(255,255,255,.06) }
.au-t.open { transform: rotate(0deg); color: var(--green) }
.au-t .ico { width: .8rem; height: .8rem }
.au-d > td { padding-top: 0 }
.au-det { margin: 0 0 .25rem; padding: .55rem .7rem; border-radius: 8px; font-size: .78rem;
  color: var(--text-2); background: var(--surface-inset); border: 1px solid var(--border);
  border-left: 3px solid var(--green-soft);
  white-space: pre-wrap; overflow-wrap: anywhere; max-width: 100% }
