/*
 * Arakasa Border Gateway Systems — direct port of the validated cyberpunk
 * mockup (https://claude.ai/code/artifact/f352369c-903b-43e5-b441-89eb8562961e).
 * Class names and motion keyframes are kept identical to that artifact on
 * purpose — this file is a port, not a reinterpretation. Single committed
 * dark world, no light mode: a corpo netrunner terminal doesn't have one.
 */
:root {
    --void: #0a0a0d;
    --panel: #15171d;
    --panel-2: #1c1f27;
    --corp-red: #a6192e;
    --signal: #fcee0a;
    --net-cyan: #00e5ff;
    --alert: #ff003c;
    --ink: #e9e7e2;
    --dim: #7d8492;
    --line: #3a3d46;

    --font-display: 'Rajdhani', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --font-mono: 'IBM Plex Mono', ui-monospace, "SFMono-Regular", Menlo, monospace;
    /* Dedicated Burmese typeface, kept separate rather than folded into the
       stacks above — English UI stays pure Rajdhani/IBM Plex Mono, and
       Burmese-language UI (once a language switch exists) uses this
       explicitly instead of silently falling back mid-stack. */
    --font-myanmar: 'Noto Sans Myanmar', sans-serif;
}

/* Burmese mode overrides both active stacks at the body level — English
   stays pure Rajdhani/IBM Plex Mono untouched, no per-element changes
   needed elsewhere. Toggled via public/i18n.js's setLang(). */
body[data-lang="my"] {
    --font-display: var(--font-myanmar);
    --font-mono: var(--font-myanmar);
}

* { box-sizing: border-box; }
html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    /* 100vh on mobile measures the viewport with the browser's address bar
       collapsed, which is taller than what's actually visible on load —
       with page scroll disabled that extra strip (and anything pinned to
       the bottom of it, like the HUD corner bracket) becomes permanently
       hidden below the fold. 100dvh tracks the real visible area instead;
       browsers that don't support it just keep the 100% fallback above. */
    height: 100dvh;
    overflow: hidden; /* the page itself never scrolls — only the inner view-wrap does */
}
button { font: inherit; color: inherit; }
body {
    background: var(--void);
    color: var(--ink);
    font-family: var(--font-mono);
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    background-image: repeating-linear-gradient(0deg, rgba(255,255,255,0.018) 0px, rgba(255,255,255,0.018) 1px, transparent 1px, transparent 3px);
}
/* Bare mount point — no frame chrome of its own. Holds either the plain
   login screen or a single .container, never both. */
#app {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center; /* centers the bare login-screen/loading states; irrelevant once .container (flex:1) is mounted */
}
/* The app root doubles as the mockup's ".phone.hud" frame — one persistent
   HUD-bracketed panel the whole app lives inside, not a per-card treatment.
   It's pinned to exactly the viewport height and never itself overflows;
   .view-wrap (its single content child) is the only thing that scrolls. */
.container {
    position: relative;
    width: 100%;
    max-width: 480px;
    margin: 0 auto;
    flex: 1;
    min-height: 0; /* let flexbox shrink it to #app's real height instead of growing past it */
    padding: 24px 20px 24px;
    background: var(--panel);
    border-left: 1px solid var(--line);
    border-right: 1px solid var(--line);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
.container::before, .container::after { content: ""; position: absolute; width: 10px; height: 10px; pointer-events: none; }
.container::before { top: 0; left: 0; border-top: 2px solid var(--signal); border-left: 2px solid var(--signal); }
.container::after { bottom: 0; right: 0; border-bottom: 2px solid var(--signal); border-right: 2px solid var(--signal); }

/* Plays once, right as the terminal frame first mounts (fresh login or a
   returning valid session) — a HUD panel powering on rather than an abrupt
   appearance. Removed after it finishes so it never replays. */
@keyframes containerOpen {
    0% { clip-path: inset(50% 0 50% 0); opacity: 0; filter: brightness(2) saturate(0); }
    55% { filter: brightness(1.3) saturate(1); }
    100% { clip-path: inset(0 0 0 0); opacity: 1; filter: brightness(1) saturate(1); }
}
.container.container-opening { animation: containerOpen 520ms cubic-bezier(.2,.8,.3,1) both; }
@media (prefers-reduced-motion: reduce) {
    .container.container-opening { animation: none; }
}

/* A flickering film-grain texture, sitting just under the scan band so the
   two read as one aged-CRT surface rather than two separate effects. Pure
   CSS (SVG turbulence noise, tiled, jittered with steps() rather than
   smooth motion so it reads as static shimmer, not a sliding pattern). */
.crt-grain {
    position: fixed;
    inset: -20%; /* oversized so the jitter never exposes an edge */
    pointer-events: none;
    z-index: 9998;
    opacity: 0.1;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    mix-blend-mode: overlay;
    animation: grainShift 800ms steps(8) infinite;
}
@keyframes grainShift {
    0%   { transform: translate(0%, 0%); }
    12%  { transform: translate(-3%, -5%); }
    24%  { transform: translate(-6%, 2%); }
    36%  { transform: translate(4%, -3%); }
    48%  { transform: translate(-4%, 5%); }
    60%  { transform: translate(3%, -6%); }
    72%  { transform: translate(-3%, 3%); }
    84%  { transform: translate(5%, 1%); }
    100% { transform: translate(0%, 0%); }
}
@media (prefers-reduced-motion: reduce) {
    .crt-grain { animation: none; }
}

/* A faint CRT scan band drifting down over the whole screen, on top of
   every element (not just the container's own background) so it reads
   consistently over cards/buttons too — a fixed overlay, not tied to any
   one screen, added once at boot and left in place for the whole session.

   Animates transform only, never background-position: this element
   covers the entire viewport and runs forever, so animating a paint
   property here means the browser repaints the whole screen every frame,
   permanently, competing with every other animation's frame budget —
   exactly the kind of ambient drain that makes unrelated UI feel janky
   on real hardware even when that UI's own animation is otherwise fine.
   The band pattern instead repeats every 280px (background-repeat-y) over
   an oversized element, and translateY by exactly one repeat-unit loops
   seamlessly — same visual, fully compositor-only. */
.crt-scan {
    position: fixed;
    top: -280px;
    left: 0;
    right: 0;
    height: calc(100% + 560px);
    pointer-events: none;
    z-index: 9999;
    background-image: linear-gradient(to bottom, transparent 0%, rgba(233, 231, 226, 0.045) 50%, transparent 100%);
    background-repeat: repeat-y;
    background-size: 100% 280px;
    animation: crtScan 9s linear infinite;
}
@keyframes crtScan {
    0% { transform: translateY(0); }
    100% { transform: translateY(280px); }
}
@media (prefers-reduced-motion: reduce) {
    .crt-scan { display: none; }
}

.view-wrap {
    flex: 1;
    min-height: 0; /* let flexbox actually shrink this below its content size instead of forcing the container to grow */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* old Edge/IE */
}
.view-wrap::-webkit-scrollbar { display: none; } /* Chrome/Safari/new Edge */

.loading {
    min-height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    color: var(--dim);
    font-family: var(--font-display);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.card .loading {
    min-height: 0;
    display: block;
    padding: 12px 18px;
    text-transform: none;
    font-family: var(--font-mono);
    font-weight: 400;
}
.error {
    background: var(--panel);
    color: var(--alert);
    border: 1px solid var(--alert);
    padding: 16px 18px;
    font-family: var(--font-display);
    font-weight: 700;
    margin-bottom: 12px;
}

/* ---------- topbar / pills ---------- */
.topbar { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; }
.topbar h2 {
    flex: 1; margin: 0;
    font-family: var(--font-display);
    font-size: 24px;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}
.pill {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.04em;
    padding: 7px 13px;
    border: 1px solid var(--net-cyan);
    background: transparent;
    color: var(--net-cyan);
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}
.pill:hover { background: var(--net-cyan); color: var(--void); }
.pill.warn, #logout-link { border-color: var(--corp-red); color: var(--corp-red); }
.pill.warn:hover, #logout-link:hover { background: var(--corp-red); color: var(--ink); }
.pill.icon { font-size: 18px; padding: 5px 12px; line-height: 1.4; }

/* ---------- status / access card ---------- */
.card { background: var(--panel-2); border: 1px solid var(--line); padding: 0 0 18px; margin-bottom: 22px; position: relative; }
.card .idstrip { display: flex; justify-content: space-between; align-items: center; background: var(--corp-red); padding: 7px 14px; margin-bottom: 16px; }
.card .idstrip .corp-mark { font-family: var(--font-display); font-weight: 700; font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink); }
.card .idstrip .corp-sub { font-family: var(--font-mono); font-size: 9.5px; color: rgba(233,231,226,0.75); }
.card h2, .card .kicker { font-family: var(--font-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.14em; color: var(--signal); margin: 0 0 12px; padding: 0 18px; text-transform: uppercase; }
/* Only when the kicker/h2 opens the card directly (no idstrip banner above
   it, which is meant to sit flush against the top border) does it need its
   own top breathing room — .card.form-panel already gets this via its own
   padding-top rule below.

   The nested variant below covers card.appendChild(el(`<div>...multiple
   top-level elements...</div>`)) call sites — el() only unwraps a template
   down to a bare element when it has exactly one top-level child; with
   several siblings (kicker + rows in one template) it returns the anonymous
   wrapper div instead, which lands as .card's actual first child with the
   kicker one level deeper inside it. Same fix, one extra level of nesting. */
.card:not(.form-panel) > h2:first-child,
.card:not(.form-panel) > .kicker:first-child,
.card:not(.form-panel) > div:first-child > h2:first-child,
.card:not(.form-panel) > div:first-child > .kicker:first-child {
    margin-top: 18px;
}

#statusBlock {
    height: 150px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    overflow: hidden;
    transition: opacity 0.12s ease;
}
.row { display: flex; justify-content: space-between; align-items: center; padding: 10px 18px; border-top: 1px dashed var(--line); }
.row:first-of-type { border-top: none; }
.row .label { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--dim); }
.row .value { font-family: var(--font-mono); font-size: 15px; font-weight: 700; display: flex; align-items: center; gap: 8px; color: var(--ink); white-space: nowrap; }
.status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--net-cyan); box-shadow: 0 0 6px var(--net-cyan); animation: blink 1.6s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .status-dot { animation: none; } }
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.15; } }

.card-actions { display: flex; gap: 10px; margin-top: 16px; padding: 0 18px; }
.btn-mini {
    flex: 1;
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    padding: 10px 8px;
    text-align: center;
    border: 1px solid var(--signal);
    color: var(--signal);
    background: transparent;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}
.btn-mini:hover { background: var(--signal); color: var(--void); }

/* ---------- QR reveal ---------- */
/* The QR panel is taken completely out of layout flow (position: absolute,
   anchored to the card's top-right) rather than being a flex sibling that
   resizes. This isn't a micro-optimization — it removes the entire class
   of problem: an out-of-flow element's opacity/transform animation can
   never trigger a layout recalculation of anything else on the page, full
   stop, regardless of how carefully a flex-based version times its width
   changes. status-rows always renders at the card's full width and never
   has to share or reflow for the QR at all. */
.status-with-qr { position: relative; }
.status-with-qr .status-rows { width: 100%; }
/* While the QR is showing, the row labels (Status/Expires/Traffic) are
   removed from the DOM entirely (not display:none, not a persistent
   element with its width/opacity toggled) and only the data remains,
   left-aligned instead of pinned to the far right — otherwise values
   would render underneath the QR overlay in the top-right corner.

   Same create/destroy philosophy as the QR panel itself: a label
   animates out via its own one-shot class and is then actually removed
   (see toggleRowLabels in app.js), at which point .row's sole remaining
   child (the value) left-aligns for free — justify-content: space-between
   with only one flex item behaves like flex-start, no separate override
   needed. Re-showing inserts a fresh <span class="label label-entering">.

   The "getting shorter" look is done with transform: scaleX() (GPU/
   compositor only) rather than animating width/max-width — visually the
   label collapses toward its own left edge exactly like the old
   max-width version did, but never triggers a layout recalculation on
   any frame, since transform never affects layout. No layout property
   (width, max-width) is ever animated on a persistent node. */
.status-with-qr .status-rows .label { display: inline-block; transform-origin: left center; }
@keyframes labelOut { from { opacity: 1; transform: scaleX(1); } to { opacity: 0; transform: scaleX(0); } }
@keyframes labelIn { from { opacity: 0; transform: scaleX(0); } to { opacity: 1; transform: scaleX(1); } }
.status-with-qr .status-rows .label.label-leaving { animation: labelOut 180ms cubic-bezier(0.4, 0, 1, 1) both; }
.status-with-qr .status-rows .label.label-entering { animation: labelIn 220ms cubic-bezier(0.2, 0.8, 0.3, 1) both; }
/* height is set inline by JS to match .status-rows' own rendered height,
   so the QR image scales to fit without ever influencing the card's own
   size (which it structurally can't anyway, being absolute).

   The panel itself is created fresh on show and removed from the DOM on
   hide (see qrBtn.onclick in app.js) — the same lifecycle navigate() uses
   for view-wrap, rather than a single persistent element toggled back and
   forth. That's what lets it reuse .enter-item verbatim for its entrance
   (identical cardInA/cardInB keyframes as every other animation in this
   app) instead of a parallel hand-rolled one. Exit gets its own one-shot
   class, mirroring .view-wrap.leaving exactly: applied once to an element
   that's about to be discarded, never toggled back. */
@keyframes qrOut { from { opacity: 1; transform: translateX(0) skewX(0); } to { opacity: 0; transform: translateX(8px) skewX(-4deg); } }
.status-with-qr .qr-wrap {
    position: absolute;
    top: 0;
    right: 0;
    width: 152px;
    padding: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
}
.status-with-qr .qr-wrap.qr-leaving { animation: qrOut 200ms ease-in both; }
.status-with-qr .qr-wrap .qr-inner { flex: 1; min-height: 0; width: 100%; display: flex; align-items: center; justify-content: center; }
.status-with-qr .qr-wrap .qr-inner img { max-width: 100%; max-height: 100%; width: auto; height: auto; }
.status-with-qr .qr-wrap .qr-caption { flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) {
    .status-with-qr .qr-wrap.qr-leaving { animation: none; }
    .status-with-qr .status-rows .label.label-leaving { animation: none; }
    .status-with-qr .status-rows .label.label-entering { animation: none; }
}

.qr-wrap { padding: 0 18px 6px; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.qr-wrap img {
    max-width: 120px;
    width: 100%;
    height: auto;
}
.qr-caption { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim); text-align: center; }
.qr-caption b { color: var(--net-cyan); }
.qr-payment-image { display: block; max-width: 200px; margin: 14px auto 0; background: var(--void); border: 1px solid var(--line); padding: 8px; }

/* ---------- action grid / buttons ---------- */
.actions-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 12px; margin-top: 4px; }
.btn {
    display: block;
    width: 100%;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 15px;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    padding: 16px 12px;
    text-align: center;
    border: 1px solid var(--line);
    background: var(--panel-2);
    color: var(--ink);
    cursor: pointer;
    margin-top: 10px;
    transition: transform 0.06s ease;
    position: relative;
    overflow: hidden;
}
.btn:active { transform: scale(0.98); }
.btn:disabled { opacity: 0.5; cursor: default; }
.btn.primary { background: var(--signal); color: var(--void); border-color: var(--signal); }
.btn.secondary { background: transparent; border-color: var(--net-cyan); color: var(--net-cyan); }
.btn.danger { background: transparent; border-color: var(--corp-red); color: var(--corp-red); }
.btn.ghost-block { background: transparent; border-color: var(--corp-red); color: var(--corp-red); }
.actions-grid .btn.primary, .actions-grid .btn.ghost-block { grid-column: span 2; }
.actions-grid .btn.grid-btn {
    margin-top: 0;
    min-height: 68px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 12px 10px;
    font-size: 13.5px;
    line-height: 1.25;
}
.card-actions .btn.card-btn { display: inline-flex; width: auto; margin-top: 0; }

/* ---------- plan list (checkout) ---------- */
.plan-item {
    border: 1px solid var(--line);
    background: var(--panel-2);
    padding: 14px 16px;
    margin-bottom: 10px;
    display: flex; justify-content: space-between; align-items: center;
    cursor: pointer;
    transition: border-color 0.12s ease, transform 0.06s ease;
    position: relative;
    overflow: hidden;
}
.plan-item:hover { border-color: var(--net-cyan); }
.plan-item:active { transform: scale(0.98); }
.plan-item.featured { border-color: var(--signal); background: rgba(252,238,10,0.08); }
.plan-item .name { font-family: var(--font-display); font-weight: 700; font-size: 17px; text-transform: uppercase; letter-spacing: 0.01em; }
.plan-item .meta { font-family: var(--font-mono); font-size: 11.5px; color: var(--dim); text-transform: uppercase; letter-spacing: 0.04em; margin-top: 4px; }
.plan-item .price { font-family: var(--font-mono); font-weight: 700; font-size: 15px; border: 1px solid var(--line); padding: 5px 10px; color: var(--ink); text-align: right; }
.plan-item.featured .price { border-color: var(--signal); color: var(--signal); }
.plan-item .price-line + .price-line { margin-top: 2px; font-size: 12px; opacity: 0.85; }
.tag-best {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    padding: 3px 8px;
    background: var(--signal);
    color: var(--void);
    margin-bottom: 8px;
}
#plansList { transition: opacity 0.1s ease; }

.link-box {
    background: var(--panel-2);
    border: 1px solid var(--line);
    padding: 12px 14px;
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--dim);
    margin-top: 10px;
    display: flex;
    align-items: center;
    gap: 10px;
}
.link-box .link-text { flex: 1; word-break: break-all; }
.copy-btn {
    flex-shrink: 0;
    background: transparent;
    color: var(--net-cyan);
    border: 1px solid var(--net-cyan);
    padding: 6px 13px;
    font-family: var(--font-mono);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-size: 11px;
    cursor: pointer;
}

/* ---------- status pills (order history) ---------- */
.status-pill { font-family: var(--font-mono); font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; padding: 3px 8px; border: 1px solid currentColor; flex-shrink: 0; }
.status-pill.good { color: var(--net-cyan); }
.status-pill.pending { color: var(--signal); }
.status-pill.bad { color: var(--alert); }

.order-row { border: 1px solid var(--line); background: var(--panel-2); padding: 12px 14px; margin-bottom: 10px; }
.order-row .otop { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.order-row .oid { font-family: var(--font-mono); font-size: 11px; color: var(--dim); }
.order-row .obottom { display: flex; justify-content: space-between; align-items: flex-end; }
.order-row .oplan { font-family: var(--font-display); font-weight: 700; font-size: 16px; text-transform: uppercase; }
.order-row .odate { font-family: var(--font-mono); font-size: 10.5px; color: var(--dim); margin-top: 2px; }
.order-row .oprice { font-family: var(--font-mono); font-weight: 700; font-size: 14px; color: var(--ink); }

/* ---------- referral ---------- */
.ref-code { font-family: var(--font-mono); font-size: 22px; font-weight: 700; letter-spacing: 0.14em; color: var(--signal); border: 1px dashed var(--signal); padding: 14px; text-align: center; margin-bottom: 6px; }
.ref-code-label { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim); text-align: center; margin-bottom: 16px; }
.ref-link-row { display: flex; gap: 8px; margin-bottom: 18px; }
.ref-link-row .link-box { flex: 1; margin-top: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; }
.ref-stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 8px; }
.ref-stat { border: 1px solid var(--line); background: var(--panel-2); padding: 14px; }
.ref-stat .n { font-family: var(--font-mono); font-size: 26px; font-weight: 700; color: var(--net-cyan); }
.ref-stat .l { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim); margin-top: 4px; }

/* ---------- device rows ---------- */
.device-row {
    border: 1px solid var(--line); background: var(--panel-2); padding: 12px 14px; margin-bottom: 10px;
    display: flex; justify-content: space-between; align-items: center; gap: 12px;
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.device-row .dev-name { font-family: var(--font-display); font-weight: 700; font-size: 15px; text-transform: uppercase; }
.device-row .dev-meta { font-family: var(--font-mono); font-size: 10.5px; color: var(--dim); margin-top: 3px; }
.device-row .remove-btn, .device-remove-btn {
    font-family: var(--font-mono); font-size: 10.5px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
    padding: 7px 12px; border: 1px solid var(--alert); color: var(--alert); background: transparent; cursor: pointer;
    flex-shrink: 0; transition: background 0.12s ease, color 0.12s ease;
}
.device-row .remove-btn:hover, .device-remove-btn:hover { background: var(--alert); color: var(--ink); }
.device-row.removing { opacity: 0; transform: translateX(24px); }
.device-remove-btn:disabled { opacity: 0.5; cursor: default; }

/* ---------- admin: stats / tabs / data rows ---------- */
.admin-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 20px; }
.admin-tile { border: 1px solid var(--line); background: var(--panel-2); padding: 14px; min-width: 0; }
.admin-tile .n { font-family: var(--font-mono); font-size: 22px; font-weight: 700; color: var(--signal); white-space: nowrap; }
.admin-tile .l { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim); margin-top: 4px; }
.admin-section-label { font-family: var(--font-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--net-cyan); margin-bottom: 10px; }

#adminContent, #adminUserResults { transition: opacity 0.12s ease; }
.admin-tabs, .tabbar-scroll {
    display: flex; gap: 6px; margin-bottom: 14px; overflow-x: auto;
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* old Edge/IE */
    /* With the scrollbar hidden there's otherwise no hint that a tab (e.g.
       "Payments") is sitting off-screen to the right — fade the trailing
       edge so a partially-visible tab reads as "more here", not clipped. */
    -webkit-mask-image: linear-gradient(to right, black calc(100% - 22px), transparent 100%);
    mask-image: linear-gradient(to right, black calc(100% - 22px), transparent 100%);
}
.admin-tabs::-webkit-scrollbar, .tabbar-scroll::-webkit-scrollbar { display: none; } /* Chrome/Safari/new Edge */
.admin-tab, .tabbar-scroll button {
    font-family: var(--font-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.06em;
    text-transform: uppercase; padding: 8px 12px; border: 1px solid var(--line); color: var(--dim);
    background: transparent; cursor: pointer; white-space: nowrap; flex-shrink: 0;
    transition: border-color 0.12s ease, color 0.12s ease;
}
.admin-tab.active, .tabbar-scroll button.active { border-color: var(--signal); color: var(--signal); }
.admin-tab:hover:not(.active), .tabbar-scroll button:hover:not(.active) { border-color: var(--dim); color: var(--ink); }

.data-row { border: 1px solid var(--line); background: var(--panel-2); padding: 11px 14px; margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.data-row .dmain { min-width: 0; }
.data-row .dname { font-family: var(--font-display); font-weight: 700; font-size: 15px; text-transform: uppercase; }
.data-row .dmeta { font-family: var(--font-mono); font-size: 10.5px; color: var(--dim); margin-top: 3px; }
.data-row .dval { font-family: var(--font-mono); font-size: 13px; font-weight: 700; text-align: right; flex-shrink: 0; }
.data-row.editable { cursor: pointer; transition: border-color 0.12s ease, transform 0.06s ease; }
.data-row.editable:hover { border-color: var(--net-cyan); }
.data-row.editable:active { transform: scale(0.98); }

.review-row { border: 1px solid var(--line); background: var(--panel-2); padding: 12px 14px; margin-bottom: 10px; }
.review-row .rtop { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
.review-row .ruser { font-family: var(--font-mono); font-size: 11px; color: var(--dim); }
.review-row .rplan { font-family: var(--font-display); font-weight: 700; font-size: 15px; text-transform: uppercase; margin-top: 2px; }
.review-row .rmeta { font-family: var(--font-mono); font-size: 10px; color: var(--dim); margin-top: 6px; }
.review-actions { display: flex; gap: 8px; margin-top: 10px; }
.review-actions button { flex: 1; font-family: var(--font-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; padding: 8px; cursor: pointer; border: 1px solid; background: transparent; transition: background 0.12s ease, color 0.12s ease; }
.review-actions .approve { border-color: var(--net-cyan); color: var(--net-cyan); }
.review-actions .approve:hover { background: var(--net-cyan); color: var(--void); }
.review-actions .reject { border-color: var(--alert); color: var(--alert); }
.review-actions .reject:hover { background: var(--alert); color: var(--ink); }
.review-row.resolved { opacity: 0.45; }
.review-row.resolved .review-actions { display: none; }

.search-row { display: flex; align-items: center; gap: 8px; border: 1px solid var(--net-cyan); background: var(--void); padding: 9px 12px; margin-bottom: 14px; }
.search-row .prompt { color: var(--net-cyan); font-family: var(--font-mono); font-size: 13px; }
.search-row input { flex: 1; background: transparent; border: none; outline: none; font-family: var(--font-mono); font-size: 13px; color: var(--ink); letter-spacing: 0.02em; }
.search-row input::placeholder { color: var(--dim); }
.search-empty { font-family: var(--font-mono); font-size: 12px; color: var(--dim); padding: 16px 2px; text-align: center; }

.new-btn {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    width: 100%; border: 1px dashed var(--net-cyan); background: transparent; color: var(--net-cyan);
    font-family: var(--font-mono); font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    padding: 10px; margin-bottom: 14px; cursor: pointer; transition: background 0.12s ease;
}
.new-btn:hover { background: rgba(0,229,255,0.08); }

.chip { font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; padding: 2px 7px; border: 1px solid currentColor; display: inline-block; }
.chip.on { color: var(--net-cyan); }
.chip.off { color: var(--dim); }
.chip.banned { color: var(--alert); }

/* ---------- admin forms (CRUD panel) ---------- */
@keyframes panelDeploy {
    from { opacity: 0; transform: translateY(18px) scaleY(0.96); transform-origin: top; }
    to   { opacity: 1; transform: translateY(0) scaleY(1); }
}
.form-panel { animation: panelDeploy 260ms cubic-bezier(.2,.8,.3,1.1) both; }
@media (prefers-reduced-motion: reduce) { .form-panel { animation: none; } }
/* Base .card leaves padding-top at 0 so the idstrip can bleed flush to the
   top edge elsewhere (Home, user info card) — but a form-panel never has
   an idstrip, so it needs its own top inset to match the 18px it already
   gets on the bottom, instead of the first field sitting on the border. */
.card.form-panel { padding-top: 18px; }

.form-group, .form-field { padding: 8px 18px; }
.form-group label, .form-field label {
    display: block; font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
    letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim); margin-bottom: 6px;
}
.form-input, .form-field input, .form-field select {
    width: 100%; min-width: 0; max-width: 100%; box-sizing: border-box;
    background: var(--void); border: 1px solid var(--line); color: var(--ink);
    font-family: var(--font-mono); font-size: 13px; padding: 9px 11px; outline: none;
    transition: border-color 0.12s ease;
}
/* iOS Safari's native type="date" control lays out its internal day/month/
   year text at its own intrinsic width regardless of width:100%/min-width:0
   — confirmed overflowing the card on an actual iPhone (Safari), not just a
   theoretical quirk. -webkit-appearance:none is the fix that actually works
   here: it makes Safari treat the box like a normal styled input instead of
   sizing it from the native control's internal layout. The tap-to-open-
   picker behavior is unaffected; only the chrome around the value changes. */
input[type="date"].form-input, .form-field input[type="date"] {
    min-width: 0;
    -webkit-appearance: none;
    appearance: none;
}
/* Belt-and-suspenders: even if some other native control does this again,
   nothing should ever be able to visually push a card wider than itself. */
.card, .form-panel { overflow-x: hidden; }
.form-input:focus, .form-field input:focus, .form-field select:focus { border-color: var(--net-cyan); }
textarea.form-input { resize: vertical; min-height: 60px; font-family: inherit; }
input[type="file"].form-input { padding: 8px; font-size: 12px; color: var(--dim); }
input[type="file"].form-input::file-selector-button {
    background: var(--signal); color: var(--void); border: none; padding: 8px 14px;
    font-family: var(--font-mono); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
    font-size: 11px; cursor: pointer; margin-right: 10px;
}
.checkbox-row { display: flex; align-items: center; gap: 8px; padding: 8px 18px; }
.checkbox-row label { color: var(--ink); font-size: 13px; font-weight: 600; }
.checkbox-row input { width: 18px; height: 18px; accent-color: var(--net-cyan); }
.form-panel .link-box { margin-left: 18px; margin-right: 18px; }

.toggle-chip {
    font-family: var(--font-mono); font-size: 11.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    padding: 9px 16px; border: 1px solid var(--net-cyan); color: var(--net-cyan); background: transparent; cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.toggle-chip:hover { background: rgba(0,229,255,0.08); }
.toggle-chip[data-active="false"] { border-color: var(--dim); color: var(--dim); }
.toggle-chip[data-active="false"]:hover { background: rgba(125,132,146,0.1); }

.form-actions { display: flex; gap: 8px; margin-top: 18px; }
.form-actions button, .form-actions .btn {
    flex: 1; font-family: var(--font-mono); font-size: 11.5px; font-weight: 700; letter-spacing: 0.04em;
    text-transform: uppercase; padding: 11px; cursor: pointer; border: 1px solid; background: transparent;
    transition: background 0.12s ease, color 0.12s ease; width: auto; margin-top: 0;
}
.form-actions .save { border-color: var(--signal); color: var(--signal); }
.form-actions .save:hover { background: var(--signal); color: var(--void); }
.form-actions .delete { border-color: var(--alert); color: var(--alert); }
.form-actions .delete:hover { background: var(--alert); color: var(--ink); }
.form-actions .cancel { border-color: var(--dim); color: var(--dim); }
.form-actions .cancel:hover { border-color: var(--ink); color: var(--ink); }

@keyframes justSaved {
    0% { border-color: var(--net-cyan); box-shadow: 0 0 0 1px var(--net-cyan); }
    100% { border-color: var(--line); box-shadow: none; }
}
.data-row.just-saved { animation: justSaved 1.1s ease-out both; }
@media (prefers-reduced-motion: reduce) { .data-row.just-saved { animation: none; } }

.proof-image { width: 100%; border: 1px solid var(--line); margin: 10px 0; display: block; }

.banner {
    background: var(--panel); color: var(--ink); border: 1px solid var(--corp-red); padding: 14px 16px;
    font-size: 12.5px; font-weight: 500; margin-bottom: 14px; display: flex; justify-content: space-between; align-items: center; gap: 10px;
}
.banner a {
    color: var(--void); background: var(--signal); text-decoration: none; font-family: var(--font-mono);
    font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; padding: 6px 14px; white-space: nowrap; font-size: 11px;
}

.login-screen { position: relative; text-align: center; padding: 64px 16px; width: 100%; max-width: 480px; margin: 0 auto; box-sizing: border-box; }
.login-screen h1 { font-family: var(--font-display); font-size: 26px; font-weight: 700; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.01em; }
.login-screen p { color: var(--dim); font-size: 13px; font-weight: 500; margin-bottom: 28px; }
/* A plain text-styled control, not a button — for secondary actions like
   "‹ Back" that shouldn't compete visually with a primary/secondary .btn. */
.link-plain { background: none; border: none; padding: 0; color: var(--net-cyan); font-family: var(--font-mono); font-size: 12px; text-decoration: underline; cursor: pointer; }
.link-plain:hover { color: var(--ink); }

/* ================= motion engine (ported verbatim) ================= */
@keyframes cardInA {
    from { opacity: 0; transform: translate(-22px, 14px) skewX(-3deg); }
    to   { opacity: 1; transform: translate(0, 0) skewX(0); }
}
@keyframes cardInB {
    from { opacity: 0; transform: translate(18px, 14px) skewX(3deg); }
    to   { opacity: 1; transform: translate(0, 0) skewX(0); }
}
.enter-item { opacity: 0; animation: cardInA 300ms cubic-bezier(.2,.8,.3,1) both; }
.enter-item.b { animation-name: cardInB; }

.view-wrap.leaving { animation: viewOut 160ms ease-in both; }
@keyframes viewOut { to { opacity: 0; transform: translate(var(--exit-x, -24px), 6px); filter: saturate(0) brightness(1.4); } }

@media (prefers-reduced-motion: reduce) {
    .enter-item { animation: none; opacity: 1; }
    .view-wrap.leaving { animation: none; }
}

.impact-surface { position: relative; }
.impact-surface::before {
    content: "";
    position: absolute; inset: 0;
    background: var(--net-cyan);
    opacity: 0;
    pointer-events: none;
    mix-blend-mode: screen;
}
.impact-surface.impact::before { animation: impactPulse 240ms ease-out both; }
@keyframes impactPulse { 0% { opacity: 0.55; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .impact-surface.impact::before { animation: none; } }

.rolling { animation: rollFlicker 120ms steps(2); }
@keyframes rollFlicker { 0% { opacity: 0.4; } 100% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .rolling { animation: none; } }

/* ---------- toast ---------- */
.toast {
    position: fixed;
    left: 50%;
    bottom: 32px;
    transform: translateX(-50%);
    background: var(--panel-2);
    border: 1px solid var(--net-cyan);
    color: var(--net-cyan);
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: 12px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 11px 20px;
    z-index: 10000;
    pointer-events: none;
    box-shadow: 0 0 0 1px rgba(0,0,0,0.4), 0 8px 24px rgba(0,0,0,0.5);
    animation: toastIn 180ms ease-out both;
}
.toast.leaving { animation: toastOut 200ms ease-in both; }
@keyframes toastIn { from { opacity: 0; transform: translateX(-50%) translateY(10px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
@keyframes toastOut { from { opacity: 1; } to { opacity: 0; transform: translateX(-50%) translateY(6px); } }
@media (prefers-reduced-motion: reduce) { .toast, .toast.leaving { animation: none; } }

/* ---------- custom confirm/prompt modal ---------- */
/* Never window.confirm()/prompt() for destructive actions — a browser's
   native dialog can be permanently suppressed by the user ("prevent this
   page from creating additional dialogs"), after which confirm() silently
   returns false forever with no dialog shown. This is a real overlay
   instead, so it can't be switched off from under the app. */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(10, 10, 13, 0.72);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    z-index: 10001;
    animation: modalOverlayIn 140ms ease-out both;
}
@keyframes modalOverlayIn { from { opacity: 0; } to { opacity: 1; } }
.modal-box {
    width: 100%;
    max-width: 340px;
    background: var(--panel-2);
    border: 1px solid var(--line);
    padding: 20px;
    box-shadow: 0 0 0 1px rgba(0,0,0,0.4), 0 16px 40px rgba(0,0,0,0.5);
}
.modal-msg {
    font-family: var(--font-mono);
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--ink);
    margin-bottom: 16px;
}
.modal-input {
    display: block;
    width: 100%;
    background: var(--void);
    border: 1px solid var(--line);
    color: var(--ink);
    font-family: var(--font-mono);
    font-size: 13px;
    padding: 10px 12px;
    margin-bottom: 16px;
}
.modal-input:focus { outline: none; border-color: var(--signal); }
.modal-actions { display: flex; gap: 10px; }
.modal-btn {
    flex: 1;
    font-family: var(--font-mono);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 11px 10px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink);
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.modal-btn.cancel:hover { border-color: var(--ink); }
.modal-btn.ok, .modal-btn.danger { border-color: var(--alert); color: var(--alert); }
.modal-btn.ok:hover, .modal-btn.danger:hover { background: var(--alert); color: var(--ink); }
@media (prefers-reduced-motion: reduce) { .modal-overlay, .modal-box.enter-item { animation: none; } }
